diff --git a/.gitignore b/.gitignore
index a860fb777bdf..24709e9d3276 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ yarn-error.log
local_version.txt
.env
.phpunit.result.cache
+_ide_helper.php
/resources/assets/bower
/public/logo
@@ -34,3 +35,4 @@ nbproject
public/test.pdf
public/storage/test.pdf
/Modules
+_ide_helper_models.php
\ No newline at end of file
diff --git a/VERSION.txt b/VERSION.txt
index 035b3d682934..1051ae0f1e41 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-5.5.74
\ No newline at end of file
+5.5.75
\ No newline at end of file
diff --git a/_ide_helper.php b/_ide_helper.php
index 26c323563f29..c5562d34111d 100644
--- a/_ide_helper.php
+++ b/_ide_helper.php
@@ -4193,7 +4193,7 @@
*/
public static function lock($name, $seconds = 0, $owner = null)
{
- /** @var \Illuminate\Cache\FileStore $instance */
+ /** @var \Illuminate\Cache\RedisStore $instance */
return $instance->lock($name, $seconds, $owner);
}
/**
@@ -4206,7 +4206,7 @@
*/
public static function restoreLock($name, $owner)
{
- /** @var \Illuminate\Cache\FileStore $instance */
+ /** @var \Illuminate\Cache\RedisStore $instance */
return $instance->restoreLock($name, $owner);
}
/**
@@ -4217,30 +4217,65 @@
*/
public static function flush()
{
- /** @var \Illuminate\Cache\FileStore $instance */
+ /** @var \Illuminate\Cache\RedisStore $instance */
return $instance->flush();
}
/**
- * Get the Filesystem instance.
+ * Get the Redis connection instance.
*
- * @return \Illuminate\Filesystem\Filesystem
+ * @return \Illuminate\Redis\Connections\Connection
* @static
*/
- public static function getFilesystem()
+ public static function connection()
{
- /** @var \Illuminate\Cache\FileStore $instance */
- return $instance->getFilesystem();
+ /** @var \Illuminate\Cache\RedisStore $instance */
+ return $instance->connection();
}
/**
- * Get the working directory of the cache.
+ * Get the Redis connection instance that should be used to manage locks.
*
- * @return string
+ * @return \Illuminate\Redis\Connections\Connection
* @static
*/
- public static function getDirectory()
+ public static function lockConnection()
{
- /** @var \Illuminate\Cache\FileStore $instance */
- return $instance->getDirectory();
+ /** @var \Illuminate\Cache\RedisStore $instance */
+ return $instance->lockConnection();
+ }
+ /**
+ * Specify the name of the connection that should be used to store data.
+ *
+ * @param string $connection
+ * @return void
+ * @static
+ */
+ public static function setConnection($connection)
+ {
+ /** @var \Illuminate\Cache\RedisStore $instance */
+ $instance->setConnection($connection);
+ }
+ /**
+ * Specify the name of the connection that should be used to manage locks.
+ *
+ * @param string $connection
+ * @return \Illuminate\Cache\RedisStore
+ * @static
+ */
+ public static function setLockConnection($connection)
+ {
+ /** @var \Illuminate\Cache\RedisStore $instance */
+ return $instance->setLockConnection($connection);
+ }
+ /**
+ * Get the Redis database instance.
+ *
+ * @return \Illuminate\Contracts\Redis\Factory
+ * @static
+ */
+ public static function getRedis()
+ {
+ /** @var \Illuminate\Cache\RedisStore $instance */
+ return $instance->getRedis();
}
/**
* Get the cache key prefix.
@@ -4250,8 +4285,20 @@
*/
public static function getPrefix()
{
- /** @var \Illuminate\Cache\FileStore $instance */
+ /** @var \Illuminate\Cache\RedisStore $instance */
return $instance->getPrefix();
+ }
+ /**
+ * Set the cache key prefix.
+ *
+ * @param string $prefix
+ * @return void
+ * @static
+ */
+ public static function setPrefix($prefix)
+ {
+ /** @var \Illuminate\Cache\RedisStore $instance */
+ $instance->setPrefix($prefix);
}
}
@@ -9854,45 +9901,44 @@
return $instance->setConnectionName($name);
}
/**
- * Release a reserved job back onto the queue after (n) seconds.
+ * Migrate the delayed jobs that are ready to the regular queue.
*
- * @param string $queue
- * @param \Illuminate\Queue\Jobs\DatabaseJobRecord $job
- * @param int $delay
- * @return mixed
+ * @param string $from
+ * @param string $to
+ * @param int $limit
+ * @return array
* @static
*/
- public static function release($queue, $job, $delay)
+ public static function migrateExpiredJobs($from, $to)
{
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
- return $instance->release($queue, $job, $delay);
+ /** @var \Illuminate\Queue\RedisQueue $instance */
+ return $instance->migrateExpiredJobs($from, $to);
}
/**
* Delete a reserved job from the queue.
*
* @param string $queue
- * @param string $id
+ * @param \Illuminate\Queue\Jobs\RedisJob $job
* @return void
- * @throws \Throwable
* @static
*/
- public static function deleteReserved($queue, $id)
+ public static function deleteReserved($queue, $job)
{
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
- $instance->deleteReserved($queue, $id);
+ /** @var \Illuminate\Queue\RedisQueue $instance */
+ $instance->deleteReserved($queue, $job);
}
/**
* Delete a reserved job from the reserved queue and release it.
*
* @param string $queue
- * @param \Illuminate\Queue\Jobs\DatabaseJob $job
+ * @param \Illuminate\Queue\Jobs\RedisJob $job
* @param int $delay
* @return void
* @static
*/
public static function deleteAndRelease($queue, $job, $delay)
{
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
+ /** @var \Illuminate\Queue\RedisQueue $instance */
$instance->deleteAndRelease($queue, $job, $delay);
}
/**
@@ -9904,7 +9950,7 @@
*/
public static function clear($queue)
{
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
+ /** @var \Illuminate\Queue\RedisQueue $instance */
return $instance->clear($queue);
}
/**
@@ -9916,19 +9962,30 @@
*/
public static function getQueue($queue)
{
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
+ /** @var \Illuminate\Queue\RedisQueue $instance */
return $instance->getQueue($queue);
}
/**
- * Get the underlying database instance.
+ * Get the connection for the queue.
*
- * @return \Illuminate\Database\Connection
+ * @return \Illuminate\Redis\Connections\Connection
* @static
*/
- public static function getDatabase()
+ public static function getConnection()
{
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
- return $instance->getDatabase();
+ /** @var \Illuminate\Queue\RedisQueue $instance */
+ return $instance->getConnection();
+ }
+ /**
+ * Get the underlying Redis instance.
+ *
+ * @return \Illuminate\Contracts\Redis\Factory
+ * @static
+ */
+ public static function getRedis()
+ {
+ /** @var \Illuminate\Queue\RedisQueue $instance */
+ return $instance->getRedis();
}
/**
* Get the backoff for an object-based queue handler.
@@ -9939,7 +9996,7 @@
*/
public static function getJobBackoff($job)
{ //Method inherited from \Illuminate\Queue\Queue
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
+ /** @var \Illuminate\Queue\RedisQueue $instance */
return $instance->getJobBackoff($job);
}
/**
@@ -9951,7 +10008,7 @@
*/
public static function getJobExpiration($job)
{ //Method inherited from \Illuminate\Queue\Queue
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
+ /** @var \Illuminate\Queue\RedisQueue $instance */
return $instance->getJobExpiration($job);
}
/**
@@ -9963,7 +10020,7 @@
*/
public static function createPayloadUsing($callback)
{ //Method inherited from \Illuminate\Queue\Queue
- \Illuminate\Queue\DatabaseQueue::createPayloadUsing($callback);
+ \Illuminate\Queue\RedisQueue::createPayloadUsing($callback);
}
/**
* Get the container instance being used by the connection.
@@ -9973,7 +10030,7 @@
*/
public static function getContainer()
{ //Method inherited from \Illuminate\Queue\Queue
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
+ /** @var \Illuminate\Queue\RedisQueue $instance */
return $instance->getContainer();
}
/**
@@ -9985,7 +10042,7 @@
*/
public static function setContainer($container)
{ //Method inherited from \Illuminate\Queue\Queue
- /** @var \Illuminate\Queue\DatabaseQueue $instance */
+ /** @var \Illuminate\Queue\RedisQueue $instance */
$instance->setContainer($container);
}
@@ -17797,25 +17854,6 @@
/**
*
*
- * @method static void createSubscription(array|string $channels, \Closure $callback, string $method = 'subscribe')
- * @method static \Illuminate\Redis\Limiters\ConcurrencyLimiterBuilder funnel(string $name)
- * @method static \Illuminate\Redis\Limiters\DurationLimiterBuilder throttle(string $name)
- * @method static mixed client()
- * @method static void subscribe(array|string $channels, \Closure $callback)
- * @method static void psubscribe(array|string $channels, \Closure $callback)
- * @method static mixed command(string $method, array $parameters = [])
- * @method static void listen(\Closure $callback)
- * @method static string|null getName()
- * @method static \Illuminate\Redis\Connections\Connection setName(string $name)
- * @method static \Illuminate\Contracts\Events\Dispatcher getEventDispatcher()
- * @method static void setEventDispatcher(\Illuminate\Contracts\Events\Dispatcher $events)
- * @method static void unsetEventDispatcher()
- * @method static void macro(string $name, object|callable $macro)
- * @method static void mixin(object $mixin, bool $replace = true)
- * @method static bool hasMacro(string $name)
- * @method static void flushMacros()
- * @method static mixed macroCall(string $method, array $parameters)
- * @see \Illuminate\Redis\RedisManager
*/
class Redis {
/**
diff --git a/app/Factory/ClientContactFactory.php b/app/Factory/ClientContactFactory.php
index 20e4d69e96f4..9d6216452485 100644
--- a/app/Factory/ClientContactFactory.php
+++ b/app/Factory/ClientContactFactory.php
@@ -22,7 +22,7 @@ class ClientContactFactory
$client_contact->first_name = '';
$client_contact->user_id = $user_id;
$client_contact->company_id = $company_id;
- $client_contact->contact_key = Str::random(40);
+ $client_contact->contact_key = Str::random(32);
$client_contact->id = 0;
$client_contact->send_email = true;
diff --git a/app/Factory/VendorContactFactory.php b/app/Factory/VendorContactFactory.php
index 05031eda3bbf..499377b1ac64 100644
--- a/app/Factory/VendorContactFactory.php
+++ b/app/Factory/VendorContactFactory.php
@@ -22,7 +22,7 @@ class VendorContactFactory
$vendor_contact->first_name = '';
$vendor_contact->user_id = $user_id;
$vendor_contact->company_id = $company_id;
- $vendor_contact->contact_key = Str::random(40);
+ $vendor_contact->contact_key = Str::random(32);
$vendor_contact->id = 0;
return $vendor_contact;
diff --git a/app/Filters/PaymentFilters.php b/app/Filters/PaymentFilters.php
index cb095e4aa3cc..27636dbcb74e 100644
--- a/app/Filters/PaymentFilters.php
+++ b/app/Filters/PaymentFilters.php
@@ -100,7 +100,6 @@ class PaymentFilters extends QueryFilters
if (count($payment_filters) >0) {
$query->whereIn('status_id', $payment_filters);
}
-
});
return $this->builder;
diff --git a/app/Filters/SchedulerFilters.php b/app/Filters/SchedulerFilters.php
new file mode 100644
index 000000000000..5721c1502035
--- /dev/null
+++ b/app/Filters/SchedulerFilters.php
@@ -0,0 +1,65 @@
+builder;
+ }
+
+ return $this->builder->where(function ($query) use ($filter) {
+ $query->where('name', 'like', '%'.$filter.'%');
+ });
+ }
+
+ /**
+ * Sorts the list based on $sort.
+ *
+ * @param string sort formatted as column|asc
+ * @return Builder
+ */
+ public function sort(string $sort = ''): Builder
+ {
+ $sort_col = explode('|', $sort);
+
+ if (!is_array($sort_col) || count($sort_col) != 2) {
+ return $this->builder;
+ }
+
+ return $this->builder->orderBy($sort_col[0], $sort_col[1]);
+ }
+
+ /**
+ * Filters the query by the users company ID.
+ *
+ * @return Builder
+ */
+ public function entityFilter(): Builder
+ {
+ return $this->builder->company();
+ }
+}
diff --git a/app/Http/Controllers/ClientPortal/ContactHashLoginController.php b/app/Http/Controllers/ClientPortal/ContactHashLoginController.php
index 5f5fc4fb92a6..1f8579ba6f9d 100644
--- a/app/Http/Controllers/ClientPortal/ContactHashLoginController.php
+++ b/app/Http/Controllers/ClientPortal/ContactHashLoginController.php
@@ -50,9 +50,9 @@ class ContactHashLoginController extends Controller
public function errorPage()
{
return render('generic.error', [
- 'title' => session()->get('title'),
- 'notification' => session()->get('notification'),
- 'account' => auth()->guard('contact')?->user()?->user?->account,
+ 'title' => session()->get('title'),
+ 'notification' => session()->get('notification'),
+ 'account' => auth()->guard('contact')?->user()?->user?->account,
'company' => auth()->guard('contact')?->user()?->user?->company
]);
}
diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php
index 3307eb7d2eb7..61a08aea9c3e 100644
--- a/app/Http/Controllers/ClientPortal/PaymentController.php
+++ b/app/Http/Controllers/ClientPortal/PaymentController.php
@@ -12,21 +12,24 @@
namespace App\Http\Controllers\ClientPortal;
-use App\Factory\PaymentFactory;
-use App\Http\Controllers\Controller;
-use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
-use App\Models\CompanyGateway;
use App\Models\Invoice;
use App\Models\Payment;
+use Illuminate\View\View;
+use App\Models\GatewayType;
use App\Models\PaymentHash;
+use App\Models\PaymentType;
+use Illuminate\Http\Request;
+use App\Models\CompanyGateway;
+use App\Factory\PaymentFactory;
+use App\Utils\Traits\MakesHash;
+use App\Utils\Traits\MakesDates;
+use App\Http\Controllers\Controller;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Contracts\View\Factory;
+use App\PaymentDrivers\Stripe\BankTransfer;
use App\Services\ClientPortal\InstantPayment;
use App\Services\Subscription\SubscriptionService;
-use App\Utils\Traits\MakesDates;
-use App\Utils\Traits\MakesHash;
-use Illuminate\Contracts\View\Factory;
-use Illuminate\Http\RedirectResponse;
-use Illuminate\Http\Request;
-use Illuminate\View\View;
+use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
/**
* Class PaymentController.
@@ -56,9 +59,35 @@ class PaymentController extends Controller
public function show(Request $request, Payment $payment)
{
$payment->load('invoices');
+ $bank_details = false;
+ $payment_intent = false;
+ $data = false;
+ $gateway = false;
+ if($payment->gateway_type_id == GatewayType::DIRECT_DEBIT && $payment->type_id == PaymentType::DIRECT_DEBIT){
+
+ if (method_exists($payment->company_gateway->driver($payment->client), 'getPaymentIntent')) {
+ $stripe = $payment->company_gateway->driver($payment->client);
+ $payment_intent = $stripe->getPaymentIntent($payment->transaction_reference);
+
+ $bt = new BankTransfer($stripe);
+
+ match($payment->currency->code){
+ 'MXN' => $data = $bt->formatDataforMx($payment_intent),
+ 'EUR' => $data = $bt->formatDataforEur($payment_intent),
+ 'JPY' => $data = $bt->formatDataforJp($payment_intent),
+ 'GBP' => $data = $bt->formatDataforUk($payment_intent),
+ };
+
+ $gateway = $stripe;
+ }
+ }
+
+
return $this->render('payments.show', [
'payment' => $payment,
+ 'bank_details' => $payment_intent ? $data : false,
+ 'currency' => strtolower($payment->currency->code),
]);
}
diff --git a/app/Http/Controllers/PreviewController.php b/app/Http/Controllers/PreviewController.php
index 19627cb4fe64..137e0d085abb 100644
--- a/app/Http/Controllers/PreviewController.php
+++ b/app/Http/Controllers/PreviewController.php
@@ -32,10 +32,12 @@ use App\Repositories\CreditRepository;
use App\Repositories\InvoiceRepository;
use App\Repositories\QuoteRepository;
use App\Repositories\RecurringInvoiceRepository;
+use App\Services\Pdf\PdfMock;
use App\Services\PdfMaker\Design;
use App\Services\PdfMaker\Design as PdfDesignModel;
use App\Services\PdfMaker\Design as PdfMakerDesign;
use App\Services\PdfMaker\PdfMaker;
+use App\Services\Preview\StubBuilder;
use App\Utils\HostedPDF\NinjaPdf;
use App\Utils\HtmlEngine;
use App\Utils\Ninja;
@@ -177,162 +179,13 @@ class PreviewController extends BaseController
public function design(DesignPreviewRequest $request)
{
- if (Ninja::isHosted() && in_array($request->getHost(), ['preview.invoicing.co','staging.invoicing.co'])) {
+ if (Ninja::isHosted() && !in_array($request->getHost(), ['preview.invoicing.co','staging.invoicing.co'])) {
return response()->json(['message' => 'This server cannot handle this request.'], 400);
}
- $company = auth()->user()->company();
+ $pdf = (new PdfMock($request->all(), auth()->user()->company()))->build()->getPdf();
- MultiDB::setDb($company->db);
-
- if ($request->input('entity') == 'quote') {
- $repo = new QuoteRepository();
- $entity_obj = QuoteFactory::create($company->id, auth()->user()->id);
- $class = Quote::class;
- } elseif ($request->input('entity') == 'credit') {
- $repo = new CreditRepository();
- $entity_obj = CreditFactory::create($company->id, auth()->user()->id);
- $class = Credit::class;
- } elseif ($request->input('entity') == 'recurring_invoice') {
- $repo = new RecurringInvoiceRepository();
- $entity_obj = RecurringInvoiceFactory::create($company->id, auth()->user()->id);
- $class = RecurringInvoice::class;
- } else { //assume it is either an invoice or a null object
- $repo = new InvoiceRepository();
- $entity_obj = InvoiceFactory::create($company->id, auth()->user()->id);
- $class = Invoice::class;
- }
-
- try {
- DB::connection(config('database.default'))->beginTransaction();
-
- if ($request->has('entity_id')) {
- $entity_obj = $class::on(config('database.default'))
- ->with('client.company')
- ->where('id', $this->decodePrimaryKey($request->input('entity_id')))
- ->where('company_id', $company->id)
- ->withTrashed()
- ->first();
- }
-
- if ($request->has('client_id')) {
- $client = Client::withTrashed()->find($this->decodePrimaryKey($request->client_id));
- if ($request->settings_type == 'client') {
- $client->settings = $request->settings;
- $client->save();
- }
- }
-
- if ($request->has('group_id')) {
- $group = GroupSetting::withTrashed()->find($this->decodePrimaryKey($request->group_id));
- if ($request->settings_type == 'group') {
- $group->settings = $request->settings;
- $group->save();
- }
- }
-
- if ($request->settings_type == 'company') {
- $company->settings = $request->settings;
- $company->save();
- }
-
- if ($request->has('footer') && !$request->filled('footer') && $request->input('entity') == 'recurring_invoice') {
- $request->merge(['footer' => $company->settings->invoice_footer]);
- }
-
- if ($request->has('terms') && !$request->filled('terms') && $request->input('entity') == 'recurring_invoice') {
- $request->merge(['terms' => $company->settings->invoice_terms]);
- }
-
- $entity_obj = $repo->save($request->all(), $entity_obj);
-
- if (! $request->has('entity_id')) {
- $entity_obj->service()->fillDefaults()->save();
- }
-
- App::forgetInstance('translator');
- $t = app('translator');
- App::setLocale($entity_obj->client->locale());
- $t->replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
-
- $html = new HtmlEngine($entity_obj->invitations()->first());
-
- $design = \App\Models\Design::find($entity_obj->design_id);
-
- /* Catch all in case migration doesn't pass back a valid design */
- if (! $design) {
- $design = \App\Models\Design::find(2);
- }
-
- if ($design->is_custom) {
- $options = [
- 'custom_partials' => json_decode(json_encode($design->design), true),
- ];
- $template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options);
- } else {
- $template = new PdfMakerDesign(strtolower($design->name));
- }
-
- $variables = $html->generateLabelsAndValues();
-
- $state = [
- 'template' => $template->elements([
- 'client' => $entity_obj->client,
- 'entity' => $entity_obj,
- 'pdf_variables' => (array) $entity_obj->company->settings->pdf_variables,
- '$product' => $design->design->product,
- 'variables' => $variables,
- ]),
- 'variables' => $variables,
- 'options' => [
- 'all_pages_header' => $entity_obj->client->getSetting('all_pages_header'),
- 'all_pages_footer' => $entity_obj->client->getSetting('all_pages_footer'),
- ],
- 'process_markdown' => $entity_obj->client->company->markdown_enabled,
- ];
-
- $maker = new PdfMaker($state);
-
- $maker
- ->design($template)
- ->build();
-
- DB::connection(config('database.default'))->rollBack();
-
- if (request()->query('html') == 'true') {
- nlog($maker->getCompiledHTML());
- return $maker->getCompiledHTML();
- }
- } catch(\Exception $e) {
- nlog($e->getMessage());
- DB::connection(config('database.default'))->rollBack();
-
- return;
- }
-
- //if phantom js...... inject here..
- if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
- return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
- }
-
- if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
- $pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
-
- $numbered_pdf = $this->pageNumbering($pdf, auth()->user()->company());
-
-
- $numbered_pdf = $this->pageNumbering($pdf, auth()->user()->company());
-
- if ($numbered_pdf) {
- $pdf = $numbered_pdf;
- }
-
- return $pdf;
- }
-
- $file_path = (new PreviewPdf($maker->getCompiledHTML(true), $company))->handle();
-
- $response = Response::make($file_path, 200);
+ $response = Response::make($pdf, 200);
$response->header('Content-Type', 'application/pdf');
return $response;
@@ -340,10 +193,11 @@ class PreviewController extends BaseController
public function live(PreviewInvoiceRequest $request)
{
- if (Ninja::isHosted() && !in_array($request->getHost(), ['preview.invoicing.co', 'staging.invoicing.co'])) {
+ if (Ninja::isHosted() && !in_array($request->getHost(), ['preview.invoicing.co','staging.invoicing.co'])) {
return response()->json(['message' => 'This server cannot handle this request.'], 400);
}
+
$company = auth()->user()->company();
MultiDB::setDb($company->db);
diff --git a/app/Http/Controllers/SetupController.php b/app/Http/Controllers/SetupController.php
index 91fc65b19585..e89f75c238f5 100644
--- a/app/Http/Controllers/SetupController.php
+++ b/app/Http/Controllers/SetupController.php
@@ -208,9 +208,7 @@ class SetupController extends Controller
public function checkPdf(Request $request)
{
try {
-
return response(['url' => ''], 200);
-
} catch (Exception $e) {
nlog($e->getMessage());
diff --git a/app/Http/Controllers/TaskSchedulerController.php b/app/Http/Controllers/TaskSchedulerController.php
index e16abab562be..370d45cf1feb 100644
--- a/app/Http/Controllers/TaskSchedulerController.php
+++ b/app/Http/Controllers/TaskSchedulerController.php
@@ -12,6 +12,7 @@
namespace App\Http\Controllers;
use App\Factory\SchedulerFactory;
+use App\Filters\SchedulerFilters;
use App\Http\Requests\TaskScheduler\DestroySchedulerRequest;
use App\Http\Requests\TaskScheduler\CreateSchedulerRequest;
use App\Http\Requests\TaskScheduler\ShowSchedulerRequest;
@@ -58,9 +59,9 @@ class TaskSchedulerController extends BaseController
* ),
* )
*/
- public function index()
+ public function index(SchedulerFilters $filters)
{
- $schedulers = Scheduler::where('company_id', auth()->user()->company()->id);
+ $schedulers = Scheduler::filter($filters);
return $this->listResponse($schedulers);
}
diff --git a/app/Http/Controllers/VendorPortal/InvitationController.php b/app/Http/Controllers/VendorPortal/InvitationController.php
index 80bc02e5d5df..fcf88380d5e0 100644
--- a/app/Http/Controllers/VendorPortal/InvitationController.php
+++ b/app/Http/Controllers/VendorPortal/InvitationController.php
@@ -91,7 +91,7 @@ class InvitationController extends Controller
$file_name = $invitation->purchase_order->numberFormatter().'.pdf';
- $file = (new CreatePurchaseOrderPdf($invitation))->rawPdf();
+ $file = (new CreatePurchaseOrderPdf($invitation))->handle();
$headers = ['Content-Type' => 'application/pdf'];
diff --git a/app/Http/Requests/BankIntegration/UploadBankIntegrationRequest.php b/app/Http/Requests/BankIntegration/UploadBankIntegrationRequest.php
index b8dfa9f09b7f..b92c22c55ad5 100644
--- a/app/Http/Requests/BankIntegration/UploadBankIntegrationRequest.php
+++ b/app/Http/Requests/BankIntegration/UploadBankIntegrationRequest.php
@@ -29,8 +29,15 @@ class UploadBankIntegrationRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/BankTransaction/UploadBankTransactionRequest.php b/app/Http/Requests/BankTransaction/UploadBankTransactionRequest.php
index 910704e08fb6..e3b62225019e 100644
--- a/app/Http/Requests/BankTransaction/UploadBankTransactionRequest.php
+++ b/app/Http/Requests/BankTransaction/UploadBankTransactionRequest.php
@@ -29,8 +29,15 @@ class UploadBankTransactionRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php
index 91c03091659f..ffe65adb9612 100644
--- a/app/Http/Requests/Client/StoreClientRequest.php
+++ b/app/Http/Requests/Client/StoreClientRequest.php
@@ -38,14 +38,15 @@ class StoreClientRequest extends Request
public function rules()
{
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
if (isset($this->number)) {
diff --git a/app/Http/Requests/Client/UpdateClientRequest.php b/app/Http/Requests/Client/UpdateClientRequest.php
index 57a2e0874fb0..6a51aec64bf0 100644
--- a/app/Http/Requests/Client/UpdateClientRequest.php
+++ b/app/Http/Requests/Client/UpdateClientRequest.php
@@ -38,14 +38,15 @@ class UpdateClientRequest extends Request
{
/* Ensure we have a client name, and that all emails are unique*/
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
$rules['company_logo'] = 'mimes:jpeg,jpg,png,gif|max:10000';
diff --git a/app/Http/Requests/Client/UploadClientRequest.php b/app/Http/Requests/Client/UploadClientRequest.php
index 3be3c67204a4..1093b3f1ebcd 100644
--- a/app/Http/Requests/Client/UploadClientRequest.php
+++ b/app/Http/Requests/Client/UploadClientRequest.php
@@ -29,8 +29,15 @@ class UploadClientRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/Company/UploadCompanyRequest.php b/app/Http/Requests/Company/UploadCompanyRequest.php
index f56037c0034f..999101417dd0 100644
--- a/app/Http/Requests/Company/UploadCompanyRequest.php
+++ b/app/Http/Requests/Company/UploadCompanyRequest.php
@@ -29,8 +29,15 @@ class UploadCompanyRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/Credit/StoreCreditRequest.php b/app/Http/Requests/Credit/StoreCreditRequest.php
index b47f6efa58fc..589cfe24527c 100644
--- a/app/Http/Requests/Credit/StoreCreditRequest.php
+++ b/app/Http/Requests/Credit/StoreCreditRequest.php
@@ -43,14 +43,15 @@ class StoreCreditRequest extends Request
{
$rules = [];
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
diff --git a/app/Http/Requests/Credit/UpdateCreditRequest.php b/app/Http/Requests/Credit/UpdateCreditRequest.php
index 71d02a674dcd..d208cf223f51 100644
--- a/app/Http/Requests/Credit/UpdateCreditRequest.php
+++ b/app/Http/Requests/Credit/UpdateCreditRequest.php
@@ -41,15 +41,16 @@ class UpdateCreditRequest extends Request
public function rules()
{
$rules = [];
+
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
-
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
if ($this->number) {
diff --git a/app/Http/Requests/Credit/UploadCreditRequest.php b/app/Http/Requests/Credit/UploadCreditRequest.php
index 8af1851982b7..06721355bbbd 100644
--- a/app/Http/Requests/Credit/UploadCreditRequest.php
+++ b/app/Http/Requests/Credit/UploadCreditRequest.php
@@ -29,8 +29,15 @@ class UploadCreditRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/Expense/UploadExpenseRequest.php b/app/Http/Requests/Expense/UploadExpenseRequest.php
index 9451e7896d21..b04cde728549 100644
--- a/app/Http/Requests/Expense/UploadExpenseRequest.php
+++ b/app/Http/Requests/Expense/UploadExpenseRequest.php
@@ -29,8 +29,15 @@ class UploadExpenseRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/GroupSetting/UploadGroupSettingRequest.php b/app/Http/Requests/GroupSetting/UploadGroupSettingRequest.php
index 05abef1bb632..c3c1415a6c90 100644
--- a/app/Http/Requests/GroupSetting/UploadGroupSettingRequest.php
+++ b/app/Http/Requests/GroupSetting/UploadGroupSettingRequest.php
@@ -29,10 +29,17 @@ class UploadGroupSettingRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
- }
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $rules;
}
}
diff --git a/app/Http/Requests/Invoice/StoreInvoiceRequest.php b/app/Http/Requests/Invoice/StoreInvoiceRequest.php
index 446f1380e578..5f21287c3839 100644
--- a/app/Http/Requests/Invoice/StoreInvoiceRequest.php
+++ b/app/Http/Requests/Invoice/StoreInvoiceRequest.php
@@ -37,24 +37,15 @@ class StoreInvoiceRequest extends Request
{
$rules = [];
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
-
- if ($this->input('file') && is_array($this->input('file'))) {
- $documents = count($this->input('file'));
-
- foreach (range(0, $documents) as $index) {
- $rules['file.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('file')) {
- $rules['file'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
$rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
diff --git a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php
index 0fbcf8cb3402..0a4f5d31e149 100644
--- a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php
+++ b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php
@@ -39,14 +39,15 @@ class UpdateInvoiceRequest extends Request
{
$rules = [];
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
$rules['id'] = new LockedInvoiceRule($this->invoice);
diff --git a/app/Http/Requests/Invoice/UploadInvoiceRequest.php b/app/Http/Requests/Invoice/UploadInvoiceRequest.php
index 8613eeef6204..70157d05aac1 100644
--- a/app/Http/Requests/Invoice/UploadInvoiceRequest.php
+++ b/app/Http/Requests/Invoice/UploadInvoiceRequest.php
@@ -29,14 +29,22 @@ class UploadInvoiceRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
- }
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- if ($this->input('file')) {
- $rules['file'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
}
+
+ public function prepareForValidation()
+ {
+
+ }
}
diff --git a/app/Http/Requests/Payment/StorePaymentRequest.php b/app/Http/Requests/Payment/StorePaymentRequest.php
index 908c3fd65ad1..f31e1cf8d3ff 100644
--- a/app/Http/Requests/Payment/StorePaymentRequest.php
+++ b/app/Http/Requests/Payment/StorePaymentRequest.php
@@ -113,16 +113,17 @@ class StorePaymentRequest extends Request
];
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
-
+
return $rules;
}
}
diff --git a/app/Http/Requests/Payment/UpdatePaymentRequest.php b/app/Http/Requests/Payment/UpdatePaymentRequest.php
index 1664d76d9a60..8f24eb3f6518 100644
--- a/app/Http/Requests/Payment/UpdatePaymentRequest.php
+++ b/app/Http/Requests/Payment/UpdatePaymentRequest.php
@@ -38,23 +38,23 @@ class UpdatePaymentRequest extends Request
$rules = [
'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule($this->all())],
'invoices.*.invoice_id' => 'distinct',
- 'documents' => 'mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
];
if ($this->number) {
$rules['number'] = Rule::unique('payments')->where('company_id', auth()->user()->company()->id)->ignore($this->payment->id);
}
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
-
+
return $rules;
}
diff --git a/app/Http/Requests/Payment/UploadPaymentRequest.php b/app/Http/Requests/Payment/UploadPaymentRequest.php
index 4be1947ba2a6..ad3874895f2e 100644
--- a/app/Http/Requests/Payment/UploadPaymentRequest.php
+++ b/app/Http/Requests/Payment/UploadPaymentRequest.php
@@ -29,8 +29,15 @@ class UploadPaymentRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/Preview/DesignPreviewRequest.php b/app/Http/Requests/Preview/DesignPreviewRequest.php
index 3d9a6cb973df..21d47405e2d1 100644
--- a/app/Http/Requests/Preview/DesignPreviewRequest.php
+++ b/app/Http/Requests/Preview/DesignPreviewRequest.php
@@ -42,8 +42,7 @@ class DesignPreviewRequest extends Request
public function rules()
{
$rules = [
- 'entity' => 'bail|sometimes|string',
- 'entity_id' => 'bail|sometimes|string',
+ 'entity_type' => 'bail|required|in:invoice,quote,credit,purchase_order',
'settings_type' => 'bail|required|in:company,group,client',
'settings' => 'sometimes',
'group_id' => 'sometimes',
diff --git a/app/Http/Requests/Product/StoreProductRequest.php b/app/Http/Requests/Product/StoreProductRequest.php
index a12249964e44..a512dc6e1750 100644
--- a/app/Http/Requests/Product/StoreProductRequest.php
+++ b/app/Http/Requests/Product/StoreProductRequest.php
@@ -28,14 +28,15 @@ class StoreProductRequest extends Request
public function rules()
{
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
$rules['cost'] = 'sometimes|numeric';
diff --git a/app/Http/Requests/Product/UpdateProductRequest.php b/app/Http/Requests/Product/UpdateProductRequest.php
index c69758b4b842..f824cc954ae0 100644
--- a/app/Http/Requests/Product/UpdateProductRequest.php
+++ b/app/Http/Requests/Product/UpdateProductRequest.php
@@ -31,16 +31,17 @@ class UpdateProductRequest extends Request
public function rules()
{
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
-
+
$rules['cost'] = 'numeric';
$rules['price'] = 'numeric';
$rules['quantity'] = 'numeric';
diff --git a/app/Http/Requests/Product/UploadProductRequest.php b/app/Http/Requests/Product/UploadProductRequest.php
index 8a12376f7ab6..d08a20c0f571 100644
--- a/app/Http/Requests/Product/UploadProductRequest.php
+++ b/app/Http/Requests/Product/UploadProductRequest.php
@@ -28,9 +28,15 @@ class UploadProductRequest extends Request
public function rules()
{
$rules = [];
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/Project/CreateProjectRequest.php b/app/Http/Requests/Project/CreateProjectRequest.php
index 3d3b29506c9c..e9f9f193f750 100644
--- a/app/Http/Requests/Project/CreateProjectRequest.php
+++ b/app/Http/Requests/Project/CreateProjectRequest.php
@@ -11,6 +11,7 @@
namespace App\Http\Requests\Project;
+use App\Models\Project;
use App\Http\Requests\Request;
class CreateProjectRequest extends Request
@@ -22,6 +23,7 @@ class CreateProjectRequest extends Request
*/
public function authorize() : bool
{
- return auth()->user()->isAdmin();
+ return auth()->user()->can('create', Project::class);
+
}
}
diff --git a/app/Http/Requests/Project/DestroyProjectRequest.php b/app/Http/Requests/Project/DestroyProjectRequest.php
index 4c50902015cd..368f779f8a1b 100644
--- a/app/Http/Requests/Project/DestroyProjectRequest.php
+++ b/app/Http/Requests/Project/DestroyProjectRequest.php
@@ -22,6 +22,6 @@ class DestroyProjectRequest extends Request
*/
public function authorize() : bool
{
- return auth()->user()->isAdmin();
+ return auth()->user()->can('edit', $this->project);
}
}
diff --git a/app/Http/Requests/Project/EditProjectRequest.php b/app/Http/Requests/Project/EditProjectRequest.php
index 04bf95304d3e..5c785163a2a3 100644
--- a/app/Http/Requests/Project/EditProjectRequest.php
+++ b/app/Http/Requests/Project/EditProjectRequest.php
@@ -22,7 +22,7 @@ class EditProjectRequest extends Request
*/
public function authorize() : bool
{
- return auth()->user()->isAdmin();
+ return auth()->user()->can('edit', $this->project);
}
/**
diff --git a/app/Http/Requests/Project/ShowProjectRequest.php b/app/Http/Requests/Project/ShowProjectRequest.php
index 140c7fb1919f..6e6b4ba98a84 100644
--- a/app/Http/Requests/Project/ShowProjectRequest.php
+++ b/app/Http/Requests/Project/ShowProjectRequest.php
@@ -22,7 +22,9 @@ class ShowProjectRequest extends Request
*/
public function authorize() : bool
{
- return auth()->user()->isAdmin();
+ // return auth()->user()->isAdmin();
+ return auth()->user()->can('view', $this->project);
+
}
/**
diff --git a/app/Http/Requests/Project/StoreProjectRequest.php b/app/Http/Requests/Project/StoreProjectRequest.php
index df39200d8d3d..3e2b7ee6a132 100644
--- a/app/Http/Requests/Project/StoreProjectRequest.php
+++ b/app/Http/Requests/Project/StoreProjectRequest.php
@@ -42,6 +42,17 @@ class StoreProjectRequest extends Request
$rules['number'] = Rule::unique('projects')->where('company_id', auth()->user()->company()->id);
}
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $this->globalRules($rules);
}
diff --git a/app/Http/Requests/Project/UpdateProjectRequest.php b/app/Http/Requests/Project/UpdateProjectRequest.php
index 50595104c824..9d938ce8d046 100644
--- a/app/Http/Requests/Project/UpdateProjectRequest.php
+++ b/app/Http/Requests/Project/UpdateProjectRequest.php
@@ -37,6 +37,17 @@ class UpdateProjectRequest extends Request
$rules['number'] = Rule::unique('projects')->where('company_id', auth()->user()->company()->id)->ignore($this->project->id);
}
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $this->globalRules($rules);
}
diff --git a/app/Http/Requests/Project/UploadProjectRequest.php b/app/Http/Requests/Project/UploadProjectRequest.php
index cccf73f4bad3..bf37beab7d29 100644
--- a/app/Http/Requests/Project/UploadProjectRequest.php
+++ b/app/Http/Requests/Project/UploadProjectRequest.php
@@ -29,8 +29,15 @@ class UploadProjectRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php b/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php
index 21c82dda16c0..3071a870a6ec 100644
--- a/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php
+++ b/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php
@@ -47,6 +47,17 @@ class StorePurchaseOrderRequest extends Request
$rules['is_amount_discount'] = ['boolean'];
$rules['line_items'] = 'array';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $rules;
}
diff --git a/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php b/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php
index d84a443d74b5..3ee030773a09 100644
--- a/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php
+++ b/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php
@@ -50,6 +50,17 @@ class UpdatePurchaseOrderRequest extends Request
$rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean'];
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $rules;
}
diff --git a/app/Http/Requests/PurchaseOrder/UploadPurchaseOrderRequest.php b/app/Http/Requests/PurchaseOrder/UploadPurchaseOrderRequest.php
index a534153f3076..3fdce5fac4e0 100644
--- a/app/Http/Requests/PurchaseOrder/UploadPurchaseOrderRequest.php
+++ b/app/Http/Requests/PurchaseOrder/UploadPurchaseOrderRequest.php
@@ -29,10 +29,17 @@ class UploadPurchaseOrderRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
- }
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $rules;
}
}
diff --git a/app/Http/Requests/Quote/StoreQuoteRequest.php b/app/Http/Requests/Quote/StoreQuoteRequest.php
index 2722d64aa22f..18c132367891 100644
--- a/app/Http/Requests/Quote/StoreQuoteRequest.php
+++ b/app/Http/Requests/Quote/StoreQuoteRequest.php
@@ -39,16 +39,17 @@ class StoreQuoteRequest extends Request
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
-
+
$rules['number'] = ['nullable', Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)];
$rules['discount'] = 'sometimes|numeric';
diff --git a/app/Http/Requests/Quote/UpdateQuoteRequest.php b/app/Http/Requests/Quote/UpdateQuoteRequest.php
index d3b786fde20e..5d75ef768a7a 100644
--- a/app/Http/Requests/Quote/UpdateQuoteRequest.php
+++ b/app/Http/Requests/Quote/UpdateQuoteRequest.php
@@ -37,16 +37,18 @@ class UpdateQuoteRequest extends Request
{
$rules = [];
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
+
if ($this->number) {
$rules['number'] = Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->quote->id);
}
diff --git a/app/Http/Requests/Quote/UploadQuoteRequest.php b/app/Http/Requests/Quote/UploadQuoteRequest.php
index ee7a125df8c2..1dce9e19de03 100644
--- a/app/Http/Requests/Quote/UploadQuoteRequest.php
+++ b/app/Http/Requests/Quote/UploadQuoteRequest.php
@@ -28,9 +28,16 @@ class UploadQuoteRequest extends Request
public function rules()
{
$rules = [];
+
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/RecurringExpense/StoreRecurringExpenseRequest.php b/app/Http/Requests/RecurringExpense/StoreRecurringExpenseRequest.php
index 978989b3730f..d2752a446e57 100644
--- a/app/Http/Requests/RecurringExpense/StoreRecurringExpenseRequest.php
+++ b/app/Http/Requests/RecurringExpense/StoreRecurringExpenseRequest.php
@@ -48,6 +48,17 @@ class StoreRecurringExpenseRequest extends Request
$rules['tax_amount2'] = 'numeric';
$rules['tax_amount3'] = 'numeric';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $this->globalRules($rules);
}
diff --git a/app/Http/Requests/RecurringExpense/UpdateRecurringExpenseRequest.php b/app/Http/Requests/RecurringExpense/UpdateRecurringExpenseRequest.php
index acbb167aff32..271a46bfd25b 100644
--- a/app/Http/Requests/RecurringExpense/UpdateRecurringExpenseRequest.php
+++ b/app/Http/Requests/RecurringExpense/UpdateRecurringExpenseRequest.php
@@ -48,6 +48,17 @@ class UpdateRecurringExpenseRequest extends Request
$rules['tax_amount3'] = 'numeric';
$rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $this->globalRules($rules);
}
diff --git a/app/Http/Requests/RecurringExpense/UploadRecurringExpenseRequest.php b/app/Http/Requests/RecurringExpense/UploadRecurringExpenseRequest.php
index 516a0495a940..d086142bff41 100644
--- a/app/Http/Requests/RecurringExpense/UploadRecurringExpenseRequest.php
+++ b/app/Http/Requests/RecurringExpense/UploadRecurringExpenseRequest.php
@@ -29,8 +29,15 @@ class UploadRecurringExpenseRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php
index 6b92361e9566..9cfab48bbf5f 100644
--- a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php
+++ b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php
@@ -38,14 +38,15 @@ class StoreRecurringInvoiceRequest extends Request
{
$rules = [];
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
diff --git a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php
index bbaeed17f9f6..2ad0c1d52558 100644
--- a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php
+++ b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php
@@ -38,14 +38,15 @@ class UpdateRecurringInvoiceRequest extends Request
{
$rules = [];
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
if ($this->number) {
diff --git a/app/Http/Requests/RecurringInvoice/UploadRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/UploadRecurringInvoiceRequest.php
index b975ad85ed5a..a6125e8c7bcb 100644
--- a/app/Http/Requests/RecurringInvoice/UploadRecurringInvoiceRequest.php
+++ b/app/Http/Requests/RecurringInvoice/UploadRecurringInvoiceRequest.php
@@ -29,10 +29,17 @@ class UploadRecurringInvoiceRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
- }
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $rules;
}
}
diff --git a/app/Http/Requests/RecurringQuote/StoreRecurringQuoteRequest.php b/app/Http/Requests/RecurringQuote/StoreRecurringQuoteRequest.php
index 588ccaa80e9a..ec3225dfd0f1 100644
--- a/app/Http/Requests/RecurringQuote/StoreRecurringQuoteRequest.php
+++ b/app/Http/Requests/RecurringQuote/StoreRecurringQuoteRequest.php
@@ -36,15 +36,16 @@ class StoreRecurringQuoteRequest extends Request
public function rules()
{
$rules = [];
+
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
-
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
diff --git a/app/Http/Requests/RecurringQuote/UpdateRecurringQuoteRequest.php b/app/Http/Requests/RecurringQuote/UpdateRecurringQuoteRequest.php
index 9c58ad24a20e..957173a241fc 100644
--- a/app/Http/Requests/RecurringQuote/UpdateRecurringQuoteRequest.php
+++ b/app/Http/Requests/RecurringQuote/UpdateRecurringQuoteRequest.php
@@ -37,16 +37,17 @@ class UpdateRecurringQuoteRequest extends Request
{
$rules = [];
- if ($this->input('documents') && is_array($this->input('documents'))) {
- $documents = count($this->input('documents'));
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
- foreach (range(0, $documents) as $index) {
- $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
- }
- } elseif ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
-
+
if ($this->number) {
$rules['number'] = Rule::unique('recurring_quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_quote->id);
}
diff --git a/app/Http/Requests/RecurringQuote/UploadRecurringQuoteRequest.php b/app/Http/Requests/RecurringQuote/UploadRecurringQuoteRequest.php
index ed6b6824ff2f..6ec54bf6ae1b 100644
--- a/app/Http/Requests/RecurringQuote/UploadRecurringQuoteRequest.php
+++ b/app/Http/Requests/RecurringQuote/UploadRecurringQuoteRequest.php
@@ -29,10 +29,17 @@ class UploadRecurringQuoteRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
- }
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $rules;
}
}
diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php
index 281490e37ed3..5553feddf077 100644
--- a/app/Http/Requests/Request.php
+++ b/app/Http/Requests/Request.php
@@ -20,6 +20,7 @@ class Request extends FormRequest
use MakesHash;
use RuntimeFormRequest;
+ protected $file_validation = 'sometimes|file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
/**
* Get the validation rules that apply to the request.
*
diff --git a/app/Http/Requests/Task/StoreTaskRequest.php b/app/Http/Requests/Task/StoreTaskRequest.php
index b5d160316e08..a4fe31f950c5 100644
--- a/app/Http/Requests/Task/StoreTaskRequest.php
+++ b/app/Http/Requests/Task/StoreTaskRequest.php
@@ -58,6 +58,17 @@ class StoreTaskRequest extends Request
$fail('Please correct overlapping values');
}
}];
+
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
return $this->globalRules($rules);
diff --git a/app/Http/Requests/Task/UpdateTaskRequest.php b/app/Http/Requests/Task/UpdateTaskRequest.php
index 8917e44643d4..69194e7beee0 100644
--- a/app/Http/Requests/Task/UpdateTaskRequest.php
+++ b/app/Http/Requests/Task/UpdateTaskRequest.php
@@ -30,7 +30,6 @@ class UpdateTaskRequest extends Request
*/
public function authorize() : bool
{
- nlog("oioi");
//prevent locked tasks from updating
if ($this->task->invoice_id && $this->task->company->invoice_task_lock) {
return false;
@@ -67,6 +66,17 @@ class UpdateTaskRequest extends Request
}
}];
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $this->globalRules($rules);
}
diff --git a/app/Http/Requests/TaskScheduler/UpdateSchedulerRequest.php b/app/Http/Requests/TaskScheduler/UpdateSchedulerRequest.php
index dc537d1a37ab..4c5038813644 100644
--- a/app/Http/Requests/TaskScheduler/UpdateSchedulerRequest.php
+++ b/app/Http/Requests/TaskScheduler/UpdateSchedulerRequest.php
@@ -11,8 +11,8 @@
namespace App\Http\Requests\TaskScheduler;
use App\Http\Requests\Request;
-use Illuminate\Validation\Rule;
use App\Http\ValidationRules\Scheduler\ValidClientIds;
+use Illuminate\Validation\Rule;
class UpdateSchedulerRequest extends Request
{
diff --git a/app/Http/Requests/Vendor/StoreVendorRequest.php b/app/Http/Requests/Vendor/StoreVendorRequest.php
index dc67adcbffa4..e2ee08adf7cc 100644
--- a/app/Http/Requests/Vendor/StoreVendorRequest.php
+++ b/app/Http/Requests/Vendor/StoreVendorRequest.php
@@ -47,6 +47,16 @@ class StoreVendorRequest extends Request
$rules['currency_id'] = 'bail|required|exists:currencies,id';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
return $rules;
}
diff --git a/app/Http/Requests/Vendor/UpdateVendorRequest.php b/app/Http/Requests/Vendor/UpdateVendorRequest.php
index 8beda8c22eef..46ac272f643d 100644
--- a/app/Http/Requests/Vendor/UpdateVendorRequest.php
+++ b/app/Http/Requests/Vendor/UpdateVendorRequest.php
@@ -44,6 +44,17 @@ class UpdateVendorRequest extends Request
$rules['contacts.*.email'] = 'nullable|distinct';
$rules['currency_id'] = 'bail|sometimes|exists:currencies,id';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
+ }
+
return $rules;
}
diff --git a/app/Http/Requests/Vendor/UploadVendorRequest.php b/app/Http/Requests/Vendor/UploadVendorRequest.php
index ca89b547cbba..fb08ca962608 100644
--- a/app/Http/Requests/Vendor/UploadVendorRequest.php
+++ b/app/Http/Requests/Vendor/UploadVendorRequest.php
@@ -29,8 +29,15 @@ class UploadVendorRequest extends Request
{
$rules = [];
- if ($this->input('documents')) {
- $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
+ if($this->file('documents') && is_array($this->file('documents')))
+ $rules['documents.*'] = $this->file_validation;
+ elseif($this->file('documents'))
+ $rules['documents'] = $this->file_validation;
+
+ if ($this->file('file') && is_array($this->file('file'))) {
+ $rules['file.*'] = $this->file_validation;
+ } elseif ($this->file('file')) {
+ $rules['file'] = $this->file_validation;
}
return $rules;
diff --git a/app/Http/ValidationRules/Company/ValidCompanyQuantity.php b/app/Http/ValidationRules/Company/ValidCompanyQuantity.php
index b9166698e8c5..75796f03eb97 100644
--- a/app/Http/ValidationRules/Company/ValidCompanyQuantity.php
+++ b/app/Http/ValidationRules/Company/ValidCompanyQuantity.php
@@ -26,8 +26,9 @@ class ValidCompanyQuantity implements Rule
*/
public function passes($attribute, $value)
{
- if(config('ninja.testvars.travis'))
+ if (config('ninja.testvars.travis')) {
return true;
+ }
if (Ninja::isSelfHost()) {
return auth()->user()->company()->account->companies->count() < 10;
diff --git a/app/Jobs/Mail/NinjaMailerJob.php b/app/Jobs/Mail/NinjaMailerJob.php
index b6dc286b23df..da3e02d04da8 100644
--- a/app/Jobs/Mail/NinjaMailerJob.php
+++ b/app/Jobs/Mail/NinjaMailerJob.php
@@ -611,8 +611,9 @@ class NinjaMailerJob implements ShouldQueue
public function failed($exception = null)
{
- if($exception)
+ if ($exception) {
nlog($exception->getMessage());
+ }
config(['queue.failed.driver' => null]);
}
diff --git a/app/Jobs/Util/UnlinkFile.php b/app/Jobs/Util/UnlinkFile.php
index 82757879f794..722a489c98c3 100644
--- a/app/Jobs/Util/UnlinkFile.php
+++ b/app/Jobs/Util/UnlinkFile.php
@@ -22,14 +22,8 @@ class UnlinkFile implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
- protected $file_path;
-
- protected $disk;
-
- public function __construct(string $disk, ?string $file_path)
+ public function __construct(protected string $disk = '', protected ?string $file_path = '')
{
- $this->file_path = $file_path;
- $this->disk = $disk;
}
/**
@@ -40,7 +34,12 @@ class UnlinkFile implements ShouldQueue
public function handle()
{
/* Do not delete files if we are on the sync queue*/
- if (config('queue.default') == 'sync' || ! $this->file_path) {
+ if (config('queue.default') == 'sync') {
+ return;
+ }
+
+
+ if (!$this->file_path) {
return;
}
diff --git a/app/Listeners/Payment/PaymentNotification.php b/app/Listeners/Payment/PaymentNotification.php
index 9db966cffc5d..5f581f88b3f8 100644
--- a/app/Listeners/Payment/PaymentNotification.php
+++ b/app/Listeners/Payment/PaymentNotification.php
@@ -77,7 +77,6 @@ class PaymentNotification implements ShouldQueue
(new NinjaMailerJob($nmo))->handle();
$nmo = null;
-
}
}
diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php
index 39c697a8c899..db9e5e9d7f1a 100644
--- a/app/Models/BaseModel.php
+++ b/app/Models/BaseModel.php
@@ -11,16 +11,16 @@
namespace App\Models;
-use Illuminate\Support\Str;
-use Illuminate\Support\Carbon;
-use App\Utils\Traits\MakesHash;
+use App\DataMapper\ClientSettings;
use App\Jobs\Util\WebhookHandler;
use App\Models\Traits\Excludable;
-use App\DataMapper\ClientSettings;
-use Illuminate\Database\Eloquent\Model;
+use App\Utils\Traits\MakesHash;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Str;
/**
* Class BaseModel
diff --git a/app/Models/Client.php b/app/Models/Client.php
index 173bd945310f..288e68c374da 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -327,21 +327,6 @@ class Client extends BaseModel implements HasLocalePreference
return $this->service()->updateBalance($amount);
}
- /**
- * Adjusts client "balances" when a client
- * makes a payment that goes on file, but does
- * not effect the client.balance record.
- *
- * @param float $amount Adjustment amount
- * @return Client
- */
- // public function processUnappliedPayment($amount) :Client
- // {
- // return $this->service()->updatePaidToDate($amount)
- // ->adjustCreditBalance($amount)
- // ->save();
- // }
-
/**
* Returns the entire filtered set
* of settings which have been merged from
diff --git a/app/Models/Scheduler.php b/app/Models/Scheduler.php
index 2e34d9ab2e06..028b4fb6dbd4 100644
--- a/app/Models/Scheduler.php
+++ b/app/Models/Scheduler.php
@@ -32,7 +32,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
class Scheduler extends BaseModel
{
use SoftDeletes;
-
+ use Filterable;
+
protected $fillable = [
'name',
'frequency_id',
diff --git a/app/Models/StaticModel.php b/app/Models/StaticModel.php
index c2a29fd67ddb..b2c6ccf58506 100644
--- a/app/Models/StaticModel.php
+++ b/app/Models/StaticModel.php
@@ -11,8 +11,8 @@
namespace App\Models;
-use App\Utils\Traits\MakesHash;
use App\Models\Traits\Excludable;
+use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
diff --git a/app/Models/User.php b/app/Models/User.php
index f2211b547158..e952e7f5a9b3 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -486,15 +486,15 @@ class User extends Authenticatable implements MustVerifyEmail
/**
* Used when we need to filter permissions carefully.
- *
+ *
* For instance, users that have view_client permissions should not
- * see the client balance, however if they also have
+ * see the client balance, however if they also have
* view_invoice or view_all etc, then they should be able to see the balance.
- *
+ *
* First we pass over the excluded permissions and return false if we find a match.
- *
+ *
* If those permissions are not hit, then we can iterate through the matched_permissions and search for a hit.
- *
+ *
* Note, returning FALSE here means the user does NOT have the permission we want to exclude
*
* @param array $matched_permission
@@ -513,7 +513,7 @@ class User extends Authenticatable implements MustVerifyEmail
}
}
- foreach($matched_permission as $permission) {
+ foreach ($matched_permission as $permission) {
if ($this->hasExactPermission($permission)) {
return true;
}
diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php
index 20582ad23ab0..3c5d7ddc095c 100644
--- a/app/Models/Vendor.php
+++ b/app/Models/Vendor.php
@@ -169,6 +169,11 @@ class Vendor extends BaseModel
return '';
}
+ public function getMergedSettings() :object
+ {
+ return $this->company->settings;
+ }
+
public function purchase_order_filepath($invitation)
{
$contact_key = $invitation->contact->contact_key;
diff --git a/app/PaymentDrivers/Stripe/BankTransfer.php b/app/PaymentDrivers/Stripe/BankTransfer.php
index 29b83c6c9aed..7e110e9f8844 100644
--- a/app/PaymentDrivers/Stripe/BankTransfer.php
+++ b/app/PaymentDrivers/Stripe/BankTransfer.php
@@ -12,16 +12,17 @@
namespace App\PaymentDrivers\Stripe;
-use App\Models\Payment;
-use App\Models\SystemLog;
-use Stripe\PaymentIntent;
-use App\Models\GatewayType;
-use App\Models\PaymentType;
-use App\Jobs\Util\SystemLogger;
-use App\Utils\Traits\MakesHash;
use App\Exceptions\PaymentFailed;
-use App\PaymentDrivers\StripePaymentDriver;
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
+use App\Jobs\Util\SystemLogger;
+use App\Models\GatewayType;
+use App\Models\Payment;
+use App\Models\PaymentType;
+use App\Models\SystemLog;
+use App\PaymentDrivers\StripePaymentDriver;
+use App\Utils\Number;
+use App\Utils\Traits\MakesHash;
+use Stripe\PaymentIntent;
class BankTransfer
{
@@ -68,7 +69,7 @@ class BankTransfer
$data['return_url'] = $this->buildReturnUrl();
$data['gateway'] = $this->stripe;
$data['client_secret'] = $intent ? $intent->client_secret : false;
-
+
return render('gateways.stripe.bank_transfer.pay', $data);
}
@@ -79,14 +80,12 @@ class BankTransfer
*/
private function resolveBankType()
{
-
- return match($this->stripe->client->currency()->code){
+ return match ($this->stripe->client->currency()->code) {
'GBP' => ['type' => 'gb_bank_transfer'],
'EUR' => ['type' => 'eu_bank_transfer', 'eu_bank_transfer' => ['country' => $this->stripe->client->country->iso_3166_2]],
'JPY' => ['type' => 'jp_bank_transfer'],
'MXN' => ['type' =>'mx_bank_transfer'],
};
-
}
/**
@@ -103,59 +102,145 @@ class BankTransfer
]);
}
-
+
+ /**
+ * paymentResponse
+ *
+ * @param mixed $request
+ * @return void
+ */
public function paymentResponse(PaymentResponseRequest $request)
{
-
$this->stripe->init();
$this->stripe->setPaymentHash($request->getPaymentHash());
$this->stripe->client = $this->stripe->payment_hash->fee_invoice->client;
- if($request->payment_intent){
-
+ if ($request->payment_intent) {
$pi = \Stripe\PaymentIntent::retrieve(
$request->payment_intent,
$this->stripe->stripe_connect_auth
);
if (in_array($pi->status, ['succeeded', 'processing'])) {
- return $this->processSuccesfulRedirect($pi);
+ $payment = $this->processSuccesfulRedirect($pi);
+ redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
}
/* Create a pending payment */
- if($pi->status == 'requires_action') {
+ if ($pi->status == 'requires_action' && $pi->next_action->type == 'display_bank_transfer_instructions') {
+ match ($pi->next_action->display_bank_transfer_instructions->currency) {
+ 'mxn' => $data['bank_details'] = $this->formatDataforMx($pi),
+ 'gbp' => $data['bank_details'] = $this->formatDataforUk($pi),
+ 'eur' => $data['bank_details'] = $this->formatDataforEur($pi),
+ 'jpy' => $data['bank_details'] = $this->formatDataforJp($pi),
+ };
+
+ $payment = $this->processSuccesfulRedirect($pi);
- $data = [
- 'payment_method' => $pi->payment_method,
- 'payment_type' => PaymentType::DIRECT_DEBIT,
- 'amount' => $this->stripe->convertFromStripeAmount($this->stripe->payment_hash->data->stripe_amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
- 'transaction_reference' => $pi->id,
- 'gateway_type_id' => GatewayType::DIRECT_DEBIT,
+ return render('gateways.stripe.bank_transfer.bank_details_container', $data);
+ }
+
+ return $this->processUnsuccesfulRedirect();
+ }
+ }
+
+ /**
+ * formatDataForUk
+ *
+ * @param PaymentIntent $pi
+ * @return array
+ */
+ public function formatDataForUk(PaymentIntent $pi): array
+ {
+ return [
+ 'amount' => Number::formatMoney($this->stripe->convertFromStripeAmount($pi->next_action->display_bank_transfer_instructions->amount_remaining, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), $this->stripe->client),
+ 'account_holder_name' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->sort_code->account_holder_name,
+ 'account_number' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->sort_code->account_number,
+ 'sort_code' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->sort_code->sort_code,
+ 'reference' => $pi->next_action->display_bank_transfer_instructions->reference,
+ 'description' => $pi->description,
+ 'gateway' => $this->stripe->company_gateway,
+ 'currency' => $pi->next_action->display_bank_transfer_instructions->currency,
];
+ }
+
+ /**
+ * formatDataforMx
+ *
+ * @param PaymentIntent $pi
+ * @return array
+ */
+ public function formatDataforMx(PaymentIntent $pi): array
+ {
+ return [
+ 'amount' => Number::formatMoney($this->stripe->convertFromStripeAmount($pi->next_action->display_bank_transfer_instructions->amount_remaining, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), $this->stripe->client),
+ 'account_holder_name' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->spei->bank_name,
+ 'account_number' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->spei->bank_code,
+ 'sort_code' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->spei->clabe,
+ 'reference' => $pi->next_action->display_bank_transfer_instructions->reference,
+ 'description' => $pi->description,
+ 'gateway' => $this->stripe->company_gateway,
+ 'currency' => $pi->next_action->display_bank_transfer_instructions->currency,
- $payment = $this->stripe->createPayment($data, Payment::STATUS_PENDING);
-
- SystemLogger::dispatch(
- ['response' => $this->stripe->payment_hash->data, 'data' => $data],
- SystemLog::CATEGORY_GATEWAY_RESPONSE,
- SystemLog::EVENT_GATEWAY_SUCCESS,
- SystemLog::TYPE_STRIPE,
- $this->stripe->client,
- $this->stripe->client->company,
- );
-
- return redirect($pi->next_action->display_bank_transfer_instructions->hosted_instructions_url);
-
- }
- return $this->processUnsuccesfulRedirect();
-
- }
-
+ ];
}
- public function processSuccesfulRedirect($payment_intent)
+
+ /**
+ * formatDataforEur
+ *
+ * @param mixed $pi
+ * @return array
+ */
+ public function formatDataforEur(PaymentIntent $pi): array
+ {
+ return [
+ 'amount' => Number::formatMoney($this->stripe->convertFromStripeAmount($pi->next_action->display_bank_transfer_instructions->amount_remaining, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), $this->stripe->client),
+ 'account_holder_name' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->iban->account_holder_name,
+ 'account_number' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->iban->iban,
+ 'sort_code' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->iban->bic,
+ 'reference' => $pi->next_action->display_bank_transfer_instructions->reference,
+ 'description' => $pi->description,
+ 'gateway' => $this->stripe->company_gateway,
+ 'currency' => $pi->next_action->display_bank_transfer_instructions->currency,
+
+ ];
+ }
+
+ /**
+ *
+ * @param PaymentIntent $pi
+ * @return array
+ */
+ public function formatDataforJp(PaymentIntent $pi): array
+ {
+ return [
+ 'amount' => Number::formatMoney($this->stripe->convertFromStripeAmount($pi->next_action->display_bank_transfer_instructions->amount_remaining, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), $this->stripe->client),
+ 'account_holder_name' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->zengin->account_holder_name,
+ 'account_number' => $pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->zengin->account_number,
+ 'account_type' =>$pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->zengin->account_type,
+ 'bank_code' =>$pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->zengin->bank_code,
+ 'bank_name' =>$pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->zengin->bank_name,
+ 'branch_code' =>$pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->zengin->branch_code,
+ 'branch_name' =>$pi->next_action->display_bank_transfer_instructions->financial_addresses[0]->zengin->branch_name,
+ 'reference' => $pi->next_action->display_bank_transfer_instructions->reference,
+ 'description' => $pi->description,
+ 'gateway' => $this->stripe->company_gateway,
+ 'currency' => $pi->next_action->display_bank_transfer_instructions->currency,
+
+ ];
+ }
+
+
+ /**
+ * processSuccesfulRedirect
+ *
+ * @param PaymentIntent $payment_intent
+ * @return Payment
+ */
+ public function processSuccesfulRedirect(PaymentIntent $payment_intent): Payment
{
$this->stripe->init();
@@ -168,7 +253,7 @@ class BankTransfer
];
- $payment = $this->stripe->createPayment($data, $payment_intent->status == 'processing' ? Payment::STATUS_PENDING : Payment::STATUS_COMPLETED);
+ $payment = $this->stripe->createPayment($data, $payment_intent->status == 'succeeded' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING);
SystemLogger::dispatch(
['response' => $this->stripe->payment_hash->data, 'data' => $data],
@@ -179,9 +264,14 @@ class BankTransfer
$this->stripe->client->company,
);
- return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
+ return $payment;
}
-
+
+ /**
+ * processUnsuccesfulRedirect
+ *
+ * @return void
+ */
public function processUnsuccesfulRedirect()
{
$server_response = $this->stripe->payment_hash->data;
@@ -204,6 +294,4 @@ class BankTransfer
throw new PaymentFailed('Failed to process the payment.', 500);
}
-
-
-}
\ No newline at end of file
+}
diff --git a/app/PaymentDrivers/Stripe/UpdatePaymentMethods.php b/app/PaymentDrivers/Stripe/UpdatePaymentMethods.php
index 70f01f81cbba..49e994c2bd61 100644
--- a/app/PaymentDrivers/Stripe/UpdatePaymentMethods.php
+++ b/app/PaymentDrivers/Stripe/UpdatePaymentMethods.php
@@ -76,23 +76,20 @@ class UpdatePaymentMethods
$this->importBankAccounts($customer, $client);
$this->importPMBankAccounts($customer, $client);
-
}
- /* ACH may also be nested inside Payment Methods.*/
+ /* ACH may also be nested inside Payment Methods.*/
public function importPMBankAccounts($customer, $client)
{
$bank_methods = \Stripe\PaymentMethod::all(
[
'customer' => $customer->id,
'type' => 'us_bank_account',
- ],
+ ],
$this->stripe->stripe_connect_auth
);
- foreach($bank_methods->data as $method)
- {
-
+ foreach ($bank_methods->data as $method) {
$token_exists = ClientGatewayToken::where([
'gateway_customer_reference' => $customer->id,
'token' => $method->id,
@@ -126,9 +123,7 @@ class UpdatePaymentMethods
}
$this->stripe->storeGatewayToken($data, $additional_data);
-
}
-
}
public function importBankAccounts($customer, $client)
diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php
index f714c7f095e1..9f331d5bfee2 100644
--- a/app/PaymentDrivers/StripePaymentDriver.php
+++ b/app/PaymentDrivers/StripePaymentDriver.php
@@ -260,7 +260,12 @@ class StripePaymentDriver extends BaseDriver
if (
$this->client
&& isset($this->client->country)
- && in_array($this->client->country->iso_3166_2, ['FR', 'IE', 'NL', 'GB', 'DE', 'ES', 'JP', 'MX'])
+ && (
+ (in_array($this->client->country->iso_3166_2, ['FR', 'IE', 'NL', 'DE', 'ES']) && $this->client->currency()->code == 'EUR') ||
+ ($this->client->country->iso_3166_2 == 'JP' && $this->client->currency()->code == 'JPY') ||
+ ($this->client->country->iso_3166_2 == 'MX' && $this->client->currency()->code == 'MXN') ||
+ ($this->client->country->iso_3166_2 == 'GB' && $this->client->currency()->code == 'GBP')
+ )
) {
$types[] = GatewayType::DIRECT_DEBIT;
}
@@ -433,6 +438,17 @@ class StripePaymentDriver extends BaseDriver
return PaymentIntent::create($data, array_merge($meta, ['idempotency_key' => uniqid("st", true)]));
}
+ public function getPaymentIntent($payment_intent_id): ?PaymentIntent
+ {
+ $this->init();
+
+ return PaymentIntent::retrieve(
+ $payment_intent_id,
+ $this->stripe_connect_auth
+ );
+
+ }
+
/**
* Returns a setup intent that allows the user
* to enter card details without initiating a transaction.
diff --git a/app/Services/Pdf/PdfBuilder.php b/app/Services/Pdf/PdfBuilder.php
new file mode 100644
index 000000000000..c57fef889f33
--- /dev/null
+++ b/app/Services/Pdf/PdfBuilder.php
@@ -0,0 +1,1601 @@
+service = $service;
+
+ $this->commonmark = new CommonMarkConverter([
+ 'allow_unsafe_links' => false,
+ ]);
+ }
+
+ /**
+ * Builds the template sections
+ *
+ * @return self
+ *
+ */
+ public function build(): self
+ {
+ $this->getTemplate()
+ ->buildSections()
+ ->getEmptyElements()
+ ->updateElementProperties()
+ ->updateVariables();
+
+ return $this;
+ }
+
+ /**
+ * Final method to get compiled HTML.
+ *
+ * @param bool $final @deprecated // is it? i still see it being called elsewhere
+ * @return string
+ */
+ public function getCompiledHTML($final = false)
+ {
+ $html = $this->document->saveHTML();
+
+ return str_replace('%24', '$', $html);
+ }
+
+ /**
+ * Generate the template
+ *
+ * @return self
+ *
+ */
+ private function getTemplate() :self
+ {
+ $document = new DOMDocument();
+
+ $document->validateOnParse = true;
+
+ @$document->loadHTML(mb_convert_encoding($this->service->designer->template, 'HTML-ENTITIES', 'UTF-8'));
+
+ $this->document = $document;
+
+ // $this->xpath = new DOMXPath($document);
+
+ return $this;
+ }
+
+ /**
+ * Generates product entity sections
+ *
+ * @return self
+ *
+ */
+ private function getProductSections(): self
+ {
+ $this->genericSectionBuilder()
+ ->getClientDetails()
+ ->getProductAndTaskTables()
+ ->getProductEntityDetails()
+ ->getProductTotals();
+
+ return $this;
+ }
+
+ private function mergeSections(array $section) :self
+ {
+ $this->sections = array_merge($this->sections, $section);
+
+ return $this;
+ }
+
+ /**
+ * Generates delivery note sections
+ *
+ * @return self
+ *
+ */
+ private function getDeliveryNoteSections(): self
+ {
+ $this->genericSectionBuilder()
+ ->getProductTotals();
+
+ $this->mergeSections([
+ 'client-details' => [
+ 'id' => 'client-details',
+ 'elements' => $this->clientDeliveryDetails(),
+ ],
+ 'delivery-note-table' => [
+ 'id' => 'delivery-note-table',
+ 'elements' => $this->deliveryNoteTable(),
+ ],
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->deliveryNoteDetails(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates statement sections
+ *
+ * @return self
+ *
+ */
+ private function getStatementSections(): self
+ {
+ $this->genericSectionBuilder();
+
+ $this->mergeSections([
+ 'statement-invoice-table' => [
+ 'id' => 'statement-invoice-table',
+ 'elements' => $this->statementInvoiceTable(),
+ ],
+ 'statement-invoice-table-totals' => [
+ 'id' => 'statement-invoice-table-totals',
+ 'elements' => $this->statementInvoiceTableTotals(),
+ ],
+ 'statement-payment-table' => [
+ 'id' => 'statement-payment-table',
+ 'elements' => $this->statementPaymentTable(),
+ ],
+ 'statement-payment-table-totals' => [
+ 'id' => 'statement-payment-table-totals',
+ 'elements' => $this->statementPaymentTableTotals(),
+ ],
+ 'statement-aging-table' => [
+ 'id' => 'statement-aging-table',
+ 'elements' => $this->statementAgingTable(),
+ ],
+ 'table-totals' => [
+ 'id' => 'table-totals',
+ 'elements' => $this->statementTableTotals(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Parent method for building invoice table totals
+ * for statements.
+ *
+ * @return array
+ */
+ public function statementInvoiceTableTotals(): array
+ {
+ $outstanding = $this->service->options['invoices']->sum('balance');
+
+ return [
+ ['element' => 'p', 'content' => '$outstanding_label: ' . $this->service->config->formatMoney($outstanding)],
+ ];
+ }
+
+
+ /**
+ * Parent method for building payments table within statement.
+ *
+ * @return array
+ */
+ public function statementPaymentTable(): array
+ {
+ if (is_null($this->service->options['payments'])) {
+ return [];
+ }
+
+ if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
+ return [];
+ }
+
+ $tbody = [];
+
+ //24-03-2022 show payments per invoice
+ foreach ($this->service->options['invoices'] as $invoice) {
+ foreach ($invoice->payments as $payment) {
+ if ($payment->is_deleted) {
+ continue;
+ }
+
+ $element = ['element' => 'tr', 'elements' => []];
+
+ $element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $payment->type ? $payment->type->name : ctrans('texts.manual_entry')];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($payment->pivot->amount) ?: ' '];
+
+ $tbody[] = $element;
+
+ $this->payment_amount_total += $payment->pivot->amount;
+ }
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $this->buildTableHeader('statement_payment')],
+ ['element' => 'tbody', 'elements' => $tbody],
+ ];
+ }
+
+ /**
+ * Generates the statement payments table
+ *
+ * @return array
+ *
+ */
+ public function statementPaymentTableTotals(): array
+ {
+ if (is_null($this->service->options['payments']) || !$this->service->options['payments']->first()) {
+ return [];
+ }
+
+ if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
+ return [];
+ }
+
+ $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->type ? $payment->type->name : ctrans('texts.manual_entry'))],
+ ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_date'), $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' ')],
+ ];
+ }
+
+ /**
+ * Generates the statement aging table
+ *
+ * @return array
+ *
+ */
+ public function statementAgingTable(): array
+ {
+ if (\array_key_exists('show_aging_table', $this->service->options) && $this->service->options['show_aging_table'] === false) {
+ return [];
+ }
+
+ $elements = [
+ ['element' => 'thead', 'elements' => []],
+ ['element' => 'tbody', 'elements' => [
+ ['element' => 'tr', 'elements' => []],
+ ]],
+ ];
+
+ foreach ($this->service->options['aging'] as $column => $value) {
+ $elements[0]['elements'][] = ['element' => 'th', 'content' => $column];
+ $elements[1]['elements'][] = ['element' => 'td', 'content' => $value];
+ }
+
+ return $elements;
+ }
+
+
+ /**
+ * Generates the purchase order sections
+ *
+ * @return self
+ *
+ */
+ private function getPurchaseOrderSections(): self
+ {
+ $this->genericSectionBuilder()
+ ->getProductAndTaskTables()
+ ->getProductTotals();
+
+ $this->mergeSections([
+ 'vendor-details' => [
+ 'id' => 'vendor-details',
+ 'elements' => $this->vendorDetails(),
+ ],
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->purchaseOrderDetails(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the generic section which apply
+ * across all design templates
+ *
+ * @return self
+ *
+ */
+ private function genericSectionBuilder(): self
+ {
+ $this->mergeSections([
+ 'company-details' => [
+ 'id' => 'company-details',
+ 'elements' => $this->companyDetails(),
+ ],
+ 'company-address' => [
+ 'id' => 'company-address',
+ 'elements' => $this->companyAddress(),
+ ],
+ 'footer-elements' => [
+ 'id' => 'footer',
+ 'elements' => [
+ $this->sharedFooterElements(),
+ ],
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the invoices table for statements
+ *
+ * @return array
+ *
+ */
+ public function statementInvoiceTable(): array
+ {
+ $tbody = [];
+
+ foreach ($this->service->options['invoices'] as $invoice) {
+ $element = ['element' => 'tr', 'elements' => []];
+
+ $element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->date, $this->service->config->client->date_format(), $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->due_date, $this->service->config->client->date_format(), $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($invoice->amount) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($invoice->balance) ?: ' '];
+
+ $tbody[] = $element;
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $this->buildTableHeader('statement_invoice')],
+ ['element' => 'tbody', 'elements' => $tbody],
+ ];
+ }
+
+
+ /**
+ * Generate the structure of table body. (
)
+ *
+ * @param string $type "$product" or "$task"
+ * @return array
+ *
+ */
+ public function buildTableBody(string $type): array
+ {
+ $elements = [];
+
+ $items = $this->transformLineItems($this->service->config->entity->line_items, $type);
+
+ $this->processNewLines($items);
+
+ if (count($items) == 0) {
+ return [];
+ }
+
+ if ($type == PdfService::DELIVERY_NOTE) {
+ $product_customs = [false, false, false, false];
+
+ foreach ($items as $row) {
+ for ($i = 0; $i < count($product_customs); $i++) {
+ if (!empty($row['delivery_note.delivery_note' . ($i + 1)])) {
+ $product_customs[$i] = true;
+ }
+ }
+ }
+
+ foreach ($items as $row) {
+ $element = ['element' => 'tr', 'elements' => []];
+
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.product_key'], 'properties' => ['data-ref' => 'delivery_note_table.product_key-td']];
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.notes'], 'properties' => ['data-ref' => 'delivery_note_table.notes-td']];
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.quantity'], 'properties' => ['data-ref' => 'delivery_note_table.quantity-td']];
+
+ for ($i = 0; $i < count($product_customs); $i++) {
+ if ($product_customs[$i]) {
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.delivery_note' . ($i + 1)], 'properties' => ['data-ref' => 'delivery_note_table.product' . ($i + 1) . '-td']];
+ }
+ }
+
+ $elements[] = $element;
+ }
+
+ return $elements;
+ }
+
+ foreach ($items as $row) {
+ $element = ['element' => 'tr', 'elements' => []];
+
+ if (
+ array_key_exists($type, $this->service->options) &&
+ !empty($this->service->options[$type]) &&
+ !is_null($this->service->options[$type])
+ ) {
+ $document = new DOMDocument();
+ $document->loadHTML($this->service->options[$type], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+
+ $td = $document->getElementsByTagName('tr')->item(0);
+
+ if ($td) {
+ foreach ($td->childNodes as $child) {
+ if ($child->nodeType !== 1) {
+ continue;
+ }
+
+ if ($child->tagName !== 'td') {
+ continue;
+ }
+
+ $element['elements'][] = ['element' => 'td', 'content' => strtr($child->nodeValue, $row)];
+ }
+ }
+ } else {
+ $_type = Str::startsWith($type, '$') ? ltrim($type, '$') : $type;
+
+ foreach ($this->service->config->pdf_variables["{$_type}_columns"] as $key => $cell) {
+ // We want to keep aliases like these:
+ // $task.cost => $task.rate
+ // $task.quantity => $task.hours
+
+ if ($cell == '$task.rate') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row['$task.cost'], 'properties' => ['data-ref' => 'task_table-task.cost-td']];
+ } elseif ($cell == '$product.discount' && !$this->service->company->enable_product_discount) {
+ $element['elements'][] = ['element' => 'td', 'content' => $row['$product.discount'], 'properties' => ['data-ref' => 'product_table-product.discount-td', 'style' => 'display: none;']];
+ } elseif ($cell == '$product.quantity' && !$this->service->company->enable_product_quantity) {
+ $element['elements'][] = ['element' => 'td', 'content' => $row['$product.quantity'], 'properties' => ['data-ref' => 'product_table-product.quantity-td', 'style' => 'display: none;']];
+ } elseif ($cell == '$task.hours') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row['$task.quantity'], 'properties' => ['data-ref' => 'task_table-task.hours-td']];
+ } elseif ($cell == '$product.tax_rate1') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax1-td']];
+ } elseif ($cell == '$product.tax_rate2') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax2-td']];
+ } elseif ($cell == '$product.tax_rate3') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax3-td']];
+ } elseif ($cell == '$product.unit_cost' || $cell == '$task.rate') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['style' => 'white-space: nowrap;', 'data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
+ } else {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
+ }
+ }
+ }
+
+ $elements[] = $element;
+ }
+
+ $document = null;
+
+ return $elements;
+ }
+
+ /**
+ * Formats the line items for display.
+ *
+ * @param mixed $items
+ * @param string $table_type
+ * @param mixed|null $custom_fields
+ *
+ * @return array
+ */
+ public function transformLineItems($items, $table_type = '$product') :array
+ {
+ $data = [];
+
+ $locale_info = localeconv();
+
+ // $this->service->config->entity_currency = $this->service->config->currency;
+
+ foreach ($items as $key => $item) {
+ if ($table_type == '$product' && $item->type_id != 1) {
+ if ($item->type_id != 4 && $item->type_id != 6 && $item->type_id != 5) {
+ continue;
+ }
+ }
+
+ if ($table_type == '$task' && $item->type_id != 2) {
+ // if ($item->type_id != 4 && $item->type_id != 5) {
+ continue;
+ // }
+ }
+
+ $helpers = new Helpers();
+ $_table_type = ltrim($table_type, '$'); // From $product -> product.
+
+ $data[$key][$table_type.'.product_key'] = is_null(optional($item)->product_key) ? $item->item : $item->product_key;
+ $data[$key][$table_type.'.item'] = is_null(optional($item)->item) ? $item->product_key : $item->item;
+ $data[$key][$table_type.'.service'] = is_null(optional($item)->service) ? $item->product_key : $item->service;
+
+ $currentDateTime = null;
+ if (isset($this->service->config->entity->next_send_date)) {
+ $currentDateTime = Carbon::parse($this->service->config->entity->next_send_date);
+ }
+
+ $data[$key][$table_type.'.notes'] = Helpers::processReservedKeywords($item->notes, $this->service->config->currency_entity, $currentDateTime);
+ $data[$key][$table_type.'.description'] = Helpers::processReservedKeywords($item->notes, $this->service->config->currency_entity, $currentDateTime);
+
+ $data[$key][$table_type.".{$_table_type}1"] = strlen($item->custom_value1) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}1", $item->custom_value1, $this->service->config->currency_entity) : '';
+ $data[$key][$table_type.".{$_table_type}2"] = strlen($item->custom_value2) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}2", $item->custom_value2, $this->service->config->currency_entity) : '';
+ $data[$key][$table_type.".{$_table_type}3"] = strlen($item->custom_value3) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}3", $item->custom_value3, $this->service->config->currency_entity) : '';
+ $data[$key][$table_type.".{$_table_type}4"] = strlen($item->custom_value4) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}4", $item->custom_value4, $this->service->config->currency_entity) : '';
+
+ if ($item->quantity > 0 || $item->cost > 0) {
+ $data[$key][$table_type.'.quantity'] = $this->service->config->formatMoney($item->quantity);
+
+ $data[$key][$table_type.'.unit_cost'] = $this->service->config->formatMoney($item->cost);
+
+ $data[$key][$table_type.'.cost'] = $this->service->config->formatMoney($item->cost);
+
+ $data[$key][$table_type.'.line_total'] = $this->service->config->formatMoney($item->line_total);
+ } else {
+ $data[$key][$table_type.'.quantity'] = '';
+
+ $data[$key][$table_type.'.unit_cost'] = '';
+
+ $data[$key][$table_type.'.cost'] = '';
+
+ $data[$key][$table_type.'.line_total'] = '';
+ }
+
+ if (property_exists($item, 'gross_line_total')) {
+ $data[$key][$table_type.'.gross_line_total'] = ($item->gross_line_total == 0) ? '' : $this->service->config->formatMoney($item->gross_line_total);
+ } else {
+ $data[$key][$table_type.'.gross_line_total'] = '';
+ }
+
+ if (property_exists($item, 'tax_amount')) {
+ $data[$key][$table_type.'.tax_amount'] = ($item->tax_amount == 0) ? '' : $this->service->config->formatMoney($item->tax_amount);
+ } else {
+ $data[$key][$table_type.'.tax_amount'] = '';
+ }
+
+ if (isset($item->discount) && $item->discount > 0) {
+ if ($item->is_amount_discount) {
+ $data[$key][$table_type.'.discount'] = $this->service->config->formatMoney($item->discount);
+ } else {
+ $data[$key][$table_type.'.discount'] = floatval($item->discount).'%';
+ }
+ } else {
+ $data[$key][$table_type.'.discount'] = '';
+ }
+
+ // Previously we used to check for tax_rate value,
+ // but that's no longer necessary.
+
+ if (isset($item->tax_rate1)) {
+ $data[$key][$table_type.'.tax_rate1'] = floatval($item->tax_rate1).'%';
+ $data[$key][$table_type.'.tax1'] = &$data[$key][$table_type.'.tax_rate1'];
+ }
+
+ if (isset($item->tax_rate2)) {
+ $data[$key][$table_type.'.tax_rate2'] = floatval($item->tax_rate2).'%';
+ $data[$key][$table_type.'.tax2'] = &$data[$key][$table_type.'.tax_rate2'];
+ }
+
+ if (isset($item->tax_rate3)) {
+ $data[$key][$table_type.'.tax_rate3'] = floatval($item->tax_rate3).'%';
+ $data[$key][$table_type.'.tax3'] = &$data[$key][$table_type.'.tax_rate3'];
+ }
+
+ $data[$key]['task_id'] = property_exists($item, 'task_id') ? $item->task_id : '';
+ }
+
+ //nlog(microtime(true) - $start);
+
+ return $data;
+ }
+
+ /**
+ * Generate the structure of table headers. ()
+ *
+ * @param string $type "product" or "task"
+ * @return array
+ *
+ */
+ public function buildTableHeader(string $type): array
+ {
+ $this->processTaxColumns($type);
+
+ $elements = [];
+
+ // Some of column can be aliased. This is simple workaround for these.
+ $aliases = [
+ '$product.product_key' => '$product.item',
+ '$task.product_key' => '$task.service',
+ '$task.rate' => '$task.cost',
+ ];
+
+ foreach ($this->service->config->pdf_variables["{$type}_columns"] as $column) {
+ if (array_key_exists($column, $aliases)) {
+ $elements[] = ['element' => 'th', 'content' => $aliases[$column] . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($aliases[$column], 1) . '-th', 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ } elseif ($column == '$product.discount' && !$this->service->company->enable_product_discount) {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
+ } elseif ($column == '$product.quantity' && !$this->service->company->enable_product_quantity) {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
+ } elseif ($column == '$product.tax_rate1') {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax1-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ } elseif ($column == '$product.tax_rate2') {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax2-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ } elseif ($column == '$product.tax_rate3') {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax3-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ } else {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ }
+ }
+
+ return $elements;
+ }
+
+ /**
+ * This method will help us decide either we show
+ * one "tax rate" column in the table or 3 custom tax rates.
+ *
+ * Logic below will help us calculate that & inject the result in the
+ * global state of the $context (design state).
+ *
+ * @param string $type "product" or "task"
+ * @return void
+ */
+ public function processTaxColumns(string $type): void
+ {
+ if ($type == 'product') {
+ $type_id = 1;
+ }
+
+ if ($type == 'task') {
+ $type_id = 2;
+ }
+
+ // At the moment we pass "task" or "product" as type.
+ // However, "pdf_variables" contains "$task.tax" or "$product.tax" <-- Notice the dollar sign.
+ // This sprintf() will help us convert "task" or "product" into "$task" or "$product" without
+ // evaluating the variable.
+
+ if (in_array(sprintf('%s%s.tax', '$', $type), (array) $this->service->config->pdf_variables["{$type}_columns"])) {
+ $line_items = collect($this->service->config->entity->line_items)->filter(function ($item) use ($type_id) {
+ return $item->type_id = $type_id;
+ });
+
+ $tax1 = $line_items->where('tax_name1', '<>', '')->where('type_id', $type_id)->count();
+ $tax2 = $line_items->where('tax_name2', '<>', '')->where('type_id', $type_id)->count();
+ $tax3 = $line_items->where('tax_name3', '<>', '')->where('type_id', $type_id)->count();
+
+ $taxes = [];
+
+ if ($tax1 > 0) {
+ array_push($taxes, sprintf('%s%s.tax_rate1', '$', $type));
+ }
+
+ if ($tax2 > 0) {
+ array_push($taxes, sprintf('%s%s.tax_rate2', '$', $type));
+ }
+
+ if ($tax3 > 0) {
+ array_push($taxes, sprintf('%s%s.tax_rate3', '$', $type));
+ }
+
+ $key = array_search(sprintf('%s%s.tax', '$', $type), $this->service->config->pdf_variables["{$type}_columns"], true);
+
+ if ($key !== false) {
+ array_splice($this->service->config->pdf_variables["{$type}_columns"], $key, 1, $taxes);
+ }
+ }
+ }
+
+ /**
+ * Generates the javascript block for
+ * hiding elements which need to be hidden
+ *
+ * @return array
+ *
+ */
+ public function sharedFooterElements(): array
+ {
+ // We want to show headers for statements, no exceptions.
+ $statements = "
+ document.querySelectorAll('#statement-invoice-table > thead > tr > th, #statement-payment-table > thead > tr > th, #statement-aging-table > thead > tr > th').forEach(t => {
+ t.hidden = false;
+ });
+ ";
+
+ $javascript = 'document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(t=>{if(""!==t.innerText){let e=t.getAttribute("data-ref").slice(0,-3);document.querySelector(`th[data-ref="${e}-th"]`).removeAttribute("hidden")}}),document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(t=>{let e=t.getAttribute("data-ref").slice(0,-3);(e=document.querySelector(`th[data-ref="${e}-th"]`)).hasAttribute("hidden")&&""==t.innerText&&t.setAttribute("hidden","true")})},!1);';
+
+ // Previously we've been decoding the HTML on the backend and XML parsing isn't good options because it requires,
+ // strict & valid HTML to even output/decode. Decoding is now done on the frontend with this piece of Javascript.
+
+ $html_decode = 'document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(`[data-state="encoded-html"]`).forEach(e=>e.innerHTML=e.innerText)},!1);';
+
+ return ['element' => 'div', 'elements' => [
+ ['element' => 'script', 'content' => $statements],
+ ['element' => 'script', 'content' => $javascript],
+ ['element' => 'script', 'content' => $html_decode],
+ ]];
+ }
+
+ /**
+ * Generates the totals table for
+ * the product type entities
+ *
+ * @return self
+ *
+ */
+ private function getProductTotals(): self
+ {
+ $this->mergeSections([
+ 'table-totals' => [
+ 'id' => 'table-totals',
+ 'elements' => $this->getTableTotals(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the entity details for
+ * Credits
+ * Quotes
+ * Invoices
+ *
+ * @return self
+ *
+ */
+ private function getProductEntityDetails(): self
+ {
+ if ($this->service->config->entity_string == 'invoice') {
+ $this->mergeSections([
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->invoiceDetails(),
+ ],
+ ]);
+ } elseif ($this->service->config->entity_string == 'quote') {
+ $this->mergeSections([
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->quoteDetails(),
+ ],
+ ]);
+ } elseif ($this->service->config->entity_string == 'credit') {
+ $this->mergeSections([
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->creditDetails(),
+ ],
+ ]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Parent entry point when building sections of the design content
+ *
+ * @return self
+ *
+ */
+ private function buildSections() :self
+ {
+ return match ($this->service->document_type) {
+ PdfService::PRODUCT => $this->getProductSections(),
+ PdfService::DELIVERY_NOTE => $this->getDeliveryNoteSections(),
+ PdfService::STATEMENT => $this->getStatementSections(),
+ PdfService::PURCHASE_ORDER => $this->getPurchaseOrderSections(),
+ };
+ }
+
+ /**
+ * Generates the table totals for statements
+ *
+ * @return array
+ *
+ */
+ private function statementTableTotals(): array
+ {
+ return [
+ ['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
+ ['element' => 'div', 'properties' => ['style' => 'margin-top: 1.5rem; display: block; align-items: flex-start; page-break-inside: avoid; visible !important;'], 'elements' => [
+ ['element' => 'img', 'properties' => ['src' => '$invoiceninja.whitelabel', 'style' => 'height: 2.5rem;', 'hidden' => 'false', 'id' => 'invoiceninja-whitelabel-logo']],
+ ]],
+ ]],
+ ];
+ }
+
+ /**
+ * Performs a variable check to ensure
+ * the variable exists
+ *
+ * @param string $variables
+ * @return bool
+ *
+ */
+ public function entityVariableCheck(string $variable): bool
+ {
+ // When it comes to invoice balance, we'll always show it.
+ if ($variable == '$invoice.total') {
+ return false;
+ }
+
+ // Some variables don't map 1:1 to table columns. This gives us support for such cases.
+ $aliases = [
+ '$quote.balance_due' => 'partial',
+ ];
+
+ try {
+ $_variable = explode('.', $variable)[1];
+ } catch (\Exception $e) {
+ throw new \Exception('Company settings seems to be broken. Missing $this->service->config->entity.variable type.');
+ }
+
+ if (\in_array($variable, \array_keys($aliases))) {
+ $_variable = $aliases[$variable];
+ }
+
+ if (is_null($this->service->config->entity->{$_variable})) {
+ return true;
+ }
+
+ if (empty($this->service->config->entity->{$_variable})) {
+ return true;
+ }
+
+ return false;
+ }
+
+ //First pass done, need a second pass to abstract this content completely.
+ /**
+ * Builds the table totals for all entities, we'll want to split this
+ *
+ * @return array
+ *
+ */
+ public function getTableTotals() :array
+ {
+ //need to see where we don't pass all these particular variables. try and refactor thisout
+ $_variables = array_key_exists('variables', $this->service->options)
+ ? $this->service->options['variables']
+ : ['values' => ['$entity.public_notes' => $this->service->config->entity->public_notes, '$entity.terms' => $this->service->config->entity->terms, '$entity_footer' => $this->service->config->entity->footer], 'labels' => []];
+
+ $variables = $this->service->config->pdf_variables['total_columns'];
+
+ $elements = [
+ ['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
+ ['element' => 'p', 'content' => strtr(str_replace(["labels","values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables), 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;']],
+ ['element' => 'p', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
+ ['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['hidden' => $this->entityVariableCheck('$entity.terms'), 'data-ref' => 'total_table-terms-label', 'style' => 'font-weight: bold; text-align: left; margin-top: 1rem;']],
+ ['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']],
+ ['element' => 'div', 'properties' => ['style' => 'margin-top: 1.5rem; display: flex; align-items: flex-start; page-break-inside: auto;'], 'elements' => [
+ ['element' => 'img', 'properties' => ['src' => '$invoiceninja.whitelabel', 'style' => 'height: 2.5rem;', 'hidden' => 'false', 'id' => 'invoiceninja-whitelabel-logo']],
+ ]],
+ ]],
+ ['element' => 'div', 'properties' => ['class' => 'totals-table-right-side', 'dir' => '$dir'], 'elements' => []],
+ ];
+
+
+ if ($this->service->document_type == PdfService::DELIVERY_NOTE) {
+ return $elements;
+ }
+
+ if ($this->service->config->entity instanceof Quote) {
+ // We don't want to show Balanace due on the quotes.
+ if (in_array('$outstanding', $variables)) {
+ $variables = \array_diff($variables, ['$outstanding']);
+ }
+
+ if ($this->service->config->entity->partial > 0) {
+ $variables[] = '$partial_due';
+ }
+ }
+
+ if ($this->service->config->entity instanceof Credit) {
+ // We don't want to show Balanace due on the quotes.
+ if (in_array('$paid_to_date', $variables)) {
+ $variables = \array_diff($variables, ['$paid_to_date']);
+ }
+ }
+
+ foreach (['discount'] as $property) {
+ $variable = sprintf('%s%s', '$', $property);
+
+ if (
+ !is_null($this->service->config->entity->{$property}) &&
+ !empty($this->service->config->entity->{$property}) &&
+ $this->service->config->entity->{$property} != 0
+ ) {
+ continue;
+ }
+
+ $variables = array_filter($variables, function ($m) use ($variable) {
+ return $m != $variable;
+ });
+ }
+
+ foreach ($variables as $variable) {
+ if ($variable == '$total_taxes') {
+ $taxes = $this->service->config->entity->total_tax_map;
+
+ if (!$taxes) {
+ continue;
+ }
+
+ foreach ($taxes as $i => $tax) {
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i . '-label']],
+ ['element' => 'span', 'content', 'content' => $this->service->config->formatMoney($tax['total']), 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i]],
+ ]];
+ }
+ } elseif ($variable == '$line_taxes') {
+ $taxes = $this->service->config->entity->tax_map;
+
+ if (!$taxes) {
+ continue;
+ }
+
+ foreach ($taxes as $i => $tax) {
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i . '-label']],
+ ['element' => 'span', 'content', 'content' => $this->service->config->formatMoney($tax['total']), 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i]],
+ ]];
+ }
+ } elseif (Str::startsWith($variable, '$custom_surcharge')) {
+ $_variable = ltrim($variable, '$'); // $custom_surcharge1 -> custom_surcharge1
+
+ $visible = intval($this->service->config->entity->{$_variable}) != 0;
+
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
+ ['element' => 'span', 'content' => $variable, 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1)]],
+ ]];
+ } elseif (Str::startsWith($variable, '$custom')) {
+ $field = explode('_', $variable);
+ $visible = is_object($this->service->company->custom_fields) && property_exists($this->service->company->custom_fields, $field[1]) && !empty($this->service->company->custom_fields->{$field[1]});
+
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
+ ['element' => 'span', 'content' => $variable, 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1)]],
+ ]];
+ } else {
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
+ ['element' => 'span', 'content' => $variable, 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1)]],
+ ]];
+ }
+ }
+
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content' => '',],
+ ['element' => 'span', 'content' => ''],
+ ]];
+
+ return $elements;
+ }
+
+ /**
+ * Generates the product and task tables
+ *
+ * @return self
+ *
+ */
+ public function getProductAndTaskTables(): self
+ {
+ $this->mergeSections([
+ 'product-table' => [
+ 'id' => 'product-table',
+ 'elements' => $this->productTable(),
+ ],
+ 'task-table' => [
+ 'id' => 'task-table',
+ 'elements' => $this->taskTable(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the client details
+ *
+ * @return self
+ *
+ */
+ public function getClientDetails(): self
+ {
+ $this->mergeSections([
+ 'client-details' => [
+ 'id' => 'client-details',
+ 'elements' => $this->clientDetails(),
+ ],
+ 'shipping-details' => [
+ 'id' => 'shipping-details',
+ 'elements' => $this->shippingDetails(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the product table
+ *
+ * @return array
+ */
+ public function productTable(): array
+ {
+ $product_items = collect($this->service->config->entity->line_items)->filter(function ($item) {
+ return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5;
+ });
+
+ if (count($product_items) == 0) {
+ return [];
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $this->buildTableHeader('product')],
+ ['element' => 'tbody', 'elements' => $this->buildTableBody('$product')],
+ ];
+ }
+
+ /**
+ * Generates the task table
+ *
+ * @return array
+ */
+ public function taskTable(): array
+ {
+ $task_items = collect($this->service->config->entity->line_items)->filter(function ($item) {
+ return $item->type_id == 2;
+ });
+
+ if (count($task_items) == 0) {
+ return [];
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $this->buildTableHeader('task')],
+ ['element' => 'tbody', 'elements' => $this->buildTableBody('$task')],
+ ];
+ }
+
+
+ /**
+ * Generates the statement details
+ *
+ * @return array
+ *
+ */
+ public function statementDetails(): array
+ {
+ $s_date = $this->translateDate(now(), $this->service->config->date_format, $this->service->config->locale);
+
+ return [
+ ['element' => 'tr', 'properties' => ['data-ref' => 'statement-label'], 'elements' => [
+ ['element' => 'th', 'properties' => [], 'content' => ""],
+ ['element' => 'th', 'properties' => [], 'content' => "".ctrans('texts.statement')."
"],
+ ]],
+ ['element' => 'tr', 'properties' => [], 'elements' => [
+ ['element' => 'th', 'properties' => [], 'content' => ctrans('texts.statement_date')],
+ ['element' => 'th', 'properties' => [], 'content' => $s_date ?? ''],
+ ]],
+ ['element' => 'tr', 'properties' => [], 'elements' => [
+ ['element' => 'th', 'properties' => [], 'content' => '$balance_due_label'],
+ ['element' => 'th', 'properties' => [], 'content' => $this->service->config->formatMoney($this->service->options['invoices']->sum('balance'))],
+ ]],
+ ];
+ }
+
+ /**
+ * Generates the invoice details
+ *
+ * @return array
+ *
+ */
+ public function invoiceDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['invoice_details'];
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+ /**
+ * Generates the quote details
+ *
+ * @return array
+ *
+ */
+ public function quoteDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['quote_details'];
+
+ if ($this->service->config->entity->partial > 0) {
+ $variables[] = '$quote.balance_due';
+ }
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+
+ /**
+ * Generates the credit note details
+ *
+ * @return array
+ *
+ */
+ public function creditDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['credit_details'];
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+ /**
+ * Generates the purchase order details
+ *
+ * @return array
+ */
+ public function purchaseOrderDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['purchase_order_details'];
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+ /**
+ * Generates the deliveyr note details
+ *
+ * @return array
+ *
+ */
+ public function deliveryNoteDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['invoice_details'];
+
+ $variables = array_filter($variables, function ($m) {
+ return !in_array($m, ['$invoice.balance_due', '$invoice.total']);
+ });
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+ /**
+ * Generates the custom values for the
+ * entity.
+ *
+ * @param array
+ * @return array
+ */
+ public function genericDetailsBuilder(array $variables): array
+ {
+ $elements = [];
+
+
+ foreach ($variables as $variable) {
+ $_variable = explode('.', $variable)[1];
+ $_customs = ['custom1', 'custom2', 'custom3', 'custom4'];
+
+ $var = str_replace("custom", "custom_value", $_variable);
+
+ if (in_array($_variable, $_customs) && !empty($this->service->config->entity->{$var})) {
+ $elements[] = ['element' => 'tr', 'elements' => [
+ ['element' => 'th', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1) . '_label']],
+ ['element' => 'th', 'content' => $variable, 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1)]],
+ ]];
+ } else {
+ $elements[] = ['element' => 'tr', 'properties' => ['hidden' => $this->entityVariableCheck($variable)], 'elements' => [
+ ['element' => 'th', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1) . '_label']],
+ ['element' => 'th', 'content' => $variable, 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1)]],
+ ]];
+ }
+ }
+
+ return $elements;
+ }
+
+
+ /**
+ * Generates the client delivery
+ * details array
+ *
+ * @return array
+ *
+ */
+ public function clientDeliveryDetails(): array
+ {
+ $elements = [];
+
+ if (!$this->service->config->client) {
+ return $elements;
+ }
+
+ $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' => '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],
+ ];
+
+ 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']];
+ }
+
+ return $elements;
+ }
+
+ /**
+ * Generates the client details section
+ *
+ * @return array
+ */
+ public function clientDetails(): array
+ {
+ $elements = [];
+
+ if (!$this->service->config->client) {
+ return $elements;
+ }
+
+ $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)]];
+ }
+
+ return $elements;
+ }
+
+ public function shippingDetails(): array
+ {
+ $elements = [];
+
+ if (!$this->service->config->client) {
+ return $elements;
+ }
+
+ $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' => '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],
+ ];
+
+ return $elements;
+ }
+
+
+ /**
+ * Generates the delivery note table
+ *
+ * @return array
+ */
+ public function deliveryNoteTable(): array
+ {
+ /* Static array of delivery note columns*/
+ $thead = [
+ ['element' => 'th', 'content' => '$item_label', 'properties' => ['data-ref' => 'delivery_note-item_label']],
+ ['element' => 'th', 'content' => '$description_label', 'properties' => ['data-ref' => 'delivery_note-description_label']],
+ ['element' => 'th', 'content' => '$product.quantity_label', 'properties' => ['data-ref' => 'delivery_note-product.quantity_label']],
+ ];
+
+ $items = $this->transformLineItems($this->service->config->entity->line_items, $this->service->document_type);
+
+ $this->processNewLines($items);
+
+ $product_customs = [false, false, false, false];
+
+ foreach ($items as $row) {
+ for ($i = 0; $i < count($product_customs); $i++) {
+ if (!empty($row['delivery_note.delivery_note' . ($i + 1)])) {
+ $product_customs[$i] = true;
+ }
+ }
+ }
+
+ for ($i = 0; $i < count($product_customs); $i++) {
+ if ($product_customs[$i]) {
+ array_push($thead, ['element' => 'th', 'content' => '$product.product' . ($i + 1) . '_label', 'properties' => ['data-ref' => 'delivery_note-product.product' . ($i + 1) . '_label']]);
+ }
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $thead],
+ ['element' => 'tbody', 'elements' => $this->buildTableBody(PdfService::DELIVERY_NOTE)],
+ ];
+ }
+
+ /**
+ * Passes an array of items by reference
+ * and performs a nl2br
+ *
+ * @param array
+ * @return void
+ *
+ */
+ public function processNewLines(array &$items): void
+ {
+ foreach ($items as $key => $item) {
+ foreach ($item as $variable => $value) {
+ $item[$variable] = str_replace("\n", '
', $value);
+ }
+
+ $items[$key] = $item;
+ }
+ }
+
+ /**
+ * Generates an arary of the company details
+ *
+ * @return array
+ *
+ */
+ public function companyDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['company_details'];
+
+ $elements = [];
+
+ foreach ($variables as $variable) {
+ $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
+ }
+
+ return $elements;
+ }
+
+ /**
+ *
+ * Generates an array of the company address
+ *
+ * @return array
+ *
+ */
+ public function companyAddress(): array
+ {
+ $variables = $this->service->config->pdf_variables['company_address'];
+
+ $elements = [];
+
+ foreach ($variables as $variable) {
+ $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_address-' . substr($variable, 1)]];
+ }
+
+ return $elements;
+ }
+
+ /**
+ *
+ * Generates an array of vendor details
+ *
+ * @return array
+ *
+ */
+ public function vendorDetails(): array
+ {
+ $elements = [];
+
+ $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)]];
+ }
+
+ return $elements;
+ }
+
+
+ ////////////////////////////////////////
+ // Dom Traversal
+ ///////////////////////////////////////
+
+
+ public function getSectionNode(string $selector)
+ {
+ return $this->document->getElementById($selector);
+ }
+
+ public function updateElementProperties() :self
+ {
+ foreach ($this->sections as $element) {
+ if (isset($element['tag'])) {
+ $node = $this->document->getElementsByTagName($element['tag'])->item(0);
+ } elseif (! is_null($this->document->getElementById($element['id']))) {
+ $node = $this->document->getElementById($element['id']);
+ } else {
+ continue;
+ }
+
+ if (isset($element['properties'])) {
+ foreach ($element['properties'] as $property => $value) {
+ $this->updateElementProperty($node, $property, $value);
+ }
+ }
+
+ if (isset($element['elements'])) {
+ $this->createElementContent($node, $element['elements']);
+ }
+ }
+
+ return $this;
+ }
+
+ public function updateElementProperty($element, string $attribute, ?string $value)
+ {
+ // We have exception for "hidden" property.
+ // hidden="true" or hidden="false" will both hide the element,
+ // that's why we have to create an exception here for this rule.
+
+ if ($attribute == 'hidden' && ($value == false || $value == 'false')) {
+ return $element;
+ }
+
+ $element->setAttribute($attribute, $value);
+
+ if ($element->getAttribute($attribute) === $value) {
+ return $element;
+ }
+
+ return $element;
+ }
+
+ public function createElementContent($element, $children) :self
+ {
+ foreach ($children as $child) {
+ $contains_html = false;
+
+ 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'] ?? '');
+ }
+ }
+
+ if (isset($child['content'])) {
+ if (isset($child['is_empty']) && $child['is_empty'] === true) {
+ continue;
+ }
+
+ $contains_html = preg_match('#(?<=<)\w+(?=[^<]*?>)#', $child['content'], $m) != 0;
+ }
+
+ 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.
+ // Later, we decode this using Javascript so it looks like it's normal HTML being injected.
+ // To get all elements that need frontend decoding, we use 'data-state' property.
+
+ $_child = $this->document->createElement($child['element'], '');
+ $_child->setAttribute('data-state', 'encoded-html');
+ $_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']) : '');
+ }
+
+ $element->appendChild($_child);
+
+ if (isset($child['properties'])) {
+ foreach ($child['properties'] as $property => $value) {
+ $this->updateElementProperty($_child, $property, $value);
+ }
+ }
+
+ if (isset($child['elements'])) {
+ $this->createElementContent($_child, $child['elements']);
+ }
+ }
+
+ return $this;
+ }
+
+ public function updateVariables()
+ {
+ $html = strtr($this->getCompiledHTML(), $this->service->html_variables['labels']);
+
+ $html = strtr($html, $this->service->html_variables['values']);
+
+ @$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
+
+ $this->document->saveHTML();
+
+ return $this;
+ }
+
+ public function updateVariable(string $element, string $variable, string $value)
+ {
+ $element = $this->document->getElementById($element);
+
+ $original = $element->nodeValue;
+
+ $element->nodeValue = '';
+
+ $replaced = strtr($original, [$variable => $value]);
+
+ $element->appendChild(
+ $this->document->createTextNode($replaced)
+ );
+
+ return $element;
+ }
+
+ public function getEmptyElements() :self
+ {
+ foreach ($this->sections as $element) {
+ if (isset($element['elements'])) {
+ $this->getEmptyChildrens($element['elements'], $this->service->html_variables);
+ }
+ }
+
+ return $this;
+ }
+
+ public function getEmptyChildrens(array $children)
+ {
+ foreach ($children as $key => $child) {
+ if (isset($child['content']) && isset($child['show_empty']) && $child['show_empty'] === false) {
+ $value = strtr($child['content'], $this->service->html_variables['values']);
+ if ($value === '' || $value === ' ' || $value === ' ') {
+ $child['is_empty'] = true;
+ }
+ }
+
+ if (isset($child['elements'])) {
+ $this->getEmptyChildrens($child['elements']);
+ }
+ }
+
+ return $this;
+ }
+}
diff --git a/app/Services/Pdf/PdfConfiguration.php b/app/Services/Pdf/PdfConfiguration.php
new file mode 100644
index 000000000000..11576763410f
--- /dev/null
+++ b/app/Services/Pdf/PdfConfiguration.php
@@ -0,0 +1,348 @@
+setEntityType()
+ ->setDateFormat()
+ ->setPdfVariables()
+ ->setDesign()
+ ->setCurrencyForPdf()
+ ->setLocale();
+
+ return $this;
+ }
+
+ /**
+ * setLocale
+ *
+ * @return self
+ */
+ private function setLocale(): self
+ {
+ App::forgetInstance('translator');
+
+ $t = app('translator');
+
+ App::setLocale($this->settings_object->locale());
+
+ $t->replace(Ninja::transformTranslations($this->settings));
+
+ $this->locale = $this->settings_object->locale();
+
+ return $this;
+ }
+
+ /**
+ * setCurrency
+ *
+ * @return self
+ */
+ private function setCurrencyForPdf(): self
+ {
+ $this->currency = $this->client ? $this->client->currency() : $this->vendor->currency();
+
+ $this->currency_entity = $this->client ? $this->client : $this->vendor;
+
+ return $this;
+ }
+
+ /**
+ * setPdfVariables
+ *
+ * @return self
+ */
+ public function setPdfVariables() :self
+ {
+ $default = (array) CompanySettings::getEntityVariableDefaults();
+
+ // $variables = (array)$this->service->company->settings->pdf_variables;
+ $variables = (array)$this->settings->pdf_variables;
+
+ foreach ($default as $property => $value) {
+ if (array_key_exists($property, $variables)) {
+ continue;
+ }
+
+ $variables[$property] = $value;
+ }
+
+ $this->pdf_variables = $variables;
+
+ return $this;
+ }
+
+ /**
+ * setEntityType
+ *
+ * @return self
+ */
+ private function setEntityType(): self
+ {
+ $entity_design_id = '';
+
+ if ($this->service->invitation instanceof InvoiceInvitation) {
+ $this->entity = $this->service->invitation->invoice;
+ $this->entity_string = 'invoice';
+ $this->client = $this->entity->client;
+ $this->contact = $this->service->invitation->contact;
+ $this->path = $this->client->invoice_filepath($this->service->invitation);
+ $this->entity_design_id = 'invoice_design_id';
+ $this->settings = $this->client->getMergedSettings();
+ $this->settings_object = $this->client;
+ $this->country = $this->client->country;
+ } elseif ($this->service->invitation instanceof QuoteInvitation) {
+ $this->entity = $this->service->invitation->quote;
+ $this->entity_string = 'quote';
+ $this->client = $this->entity->client;
+ $this->contact = $this->service->invitation->contact;
+ $this->path = $this->client->quote_filepath($this->service->invitation);
+ $this->entity_design_id = 'quote_design_id';
+ $this->settings = $this->client->getMergedSettings();
+ $this->settings_object = $this->client;
+ $this->country = $this->client->country;
+ } elseif ($this->service->invitation instanceof CreditInvitation) {
+ $this->entity = $this->service->invitation->credit;
+ $this->entity_string = 'credit';
+ $this->client = $this->entity->client;
+ $this->contact = $this->service->invitation->contact;
+ $this->path = $this->client->credit_filepath($this->service->invitation);
+ $this->entity_design_id = 'credit_design_id';
+ $this->settings = $this->client->getMergedSettings();
+ $this->settings_object = $this->client;
+ $this->country = $this->client->country;
+ } elseif ($this->service->invitation instanceof RecurringInvoiceInvitation) {
+ $this->entity = $this->service->invitation->recurring_invoice;
+ $this->entity_string = 'recurring_invoice';
+ $this->client = $this->entity->client;
+ $this->contact = $this->service->invitation->contact;
+ $this->path = $this->client->recurring_invoice_filepath($this->service->invitation);
+ $this->entity_design_id = 'invoice_design_id';
+ $this->settings = $this->client->getMergedSettings();
+ $this->settings_object = $this->client;
+ $this->country = $this->client->country;
+ } elseif ($this->service->invitation instanceof PurchaseOrderInvitation) {
+ $this->entity = $this->service->invitation->purchase_order;
+ $this->entity_string = 'purchase_order';
+ $this->vendor = $this->entity->vendor;
+ $this->vendor_contact = $this->service->invitation->contact;
+ $this->path = $this->vendor->purchase_order_filepath($this->service->invitation);
+ $this->entity_design_id = 'invoice_design_id';
+ $this->entity_design_id = 'purchase_order_design_id';
+ $this->settings = $this->vendor->company->settings;
+ $this->settings_object = $this->vendor;
+ $this->client = null;
+ $this->country = $this->vendor->country ?: $this->vendor->company->country();
+ } else {
+ throw new \Exception('Unable to resolve entity', 500);
+ }
+
+ $this->setTaxMap($this->entity->calc()->getTaxMap());
+ $this->setTotalTaxMap($this->entity->calc()->getTotalTaxMap());
+
+ $this->path = $this->path.$this->entity->numberFormatter().'.pdf';
+
+ return $this;
+ }
+
+ public function setTaxMap($map): self
+ {
+ $this->tax_map = $map;
+
+ return $this;
+ }
+
+ public function setTotalTaxMap($map): self
+ {
+ $this->total_tax_map = $map;
+
+ return $this;
+ }
+
+ public function setCurrency(Currency $currency): self
+ {
+ $this->currency = $currency;
+
+ return $this;
+ }
+
+ public function setCountry(Country $country): self
+ {
+ $this->country = $country;
+
+ return $this;
+ }
+
+ /**
+ * setDesign
+ *
+ * @return self
+ */
+ private function setDesign(): self
+ {
+ $design_id = $this->entity->design_id ? : $this->decodePrimaryKey($this->settings_object->getSetting($this->entity_design_id));
+
+ $this->design = Design::find($design_id ?: 2);
+
+ return $this;
+ }
+
+ /**
+ * formatMoney
+ *
+ * @param float $value
+ * @return string
+ */
+ public function formatMoney($value): string
+ {
+ $value = floatval($value);
+
+ $thousand = $this->currency->thousand_separator;
+ $decimal = $this->currency->decimal_separator;
+ $precision = $this->currency->precision;
+ $code = $this->currency->code;
+ $swapSymbol = $this->currency->swap_currency_symbol;
+
+ if (isset($this->country->thousand_separator) && strlen($this->country->thousand_separator) >= 1) {
+ $thousand = $this->country->thousand_separator;
+ }
+
+ if (isset($this->country->decimal_separator) && strlen($this->country->decimal_separator) >= 1) {
+ $decimal = $this->country->decimal_separator;
+ }
+
+ if (isset($this->country->swap_currency_symbol) && strlen($this->country->swap_currency_symbol) >= 1) {
+ $swapSymbol = $this->country->swap_currency_symbol;
+ }
+
+ $value = number_format($value, $precision, $decimal, $thousand);
+ $symbol = $this->currency->symbol;
+
+ if ($this->settings->show_currency_code === true && $this->currency->code == 'CHF') {
+ return "{$code} {$value}";
+ } elseif ($this->settings->show_currency_code === true) {
+ return "{$value} {$code}";
+ } elseif ($swapSymbol) {
+ return "{$value} ".trim($symbol);
+ } elseif ($this->settings->show_currency_code === false) {
+ return "{$symbol}{$value}";
+ } else {
+ $value = floatval($value);
+ $thousand = $this->currency->thousand_separator;
+ $decimal = $this->currency->decimal_separator;
+ $precision = $this->currency->precision;
+
+ return number_format($value, $precision, $decimal, $thousand);
+ }
+ }
+
+ /**
+ * date_format
+ *
+ * @return self
+ */
+ public function setDateFormat(): self
+ {
+ $date_formats = Cache::get('date_formats');
+
+ if (! $date_formats) {
+ $this->buildCache(true);
+ }
+
+ $this->date_format = $date_formats->filter(function ($item) {
+ return $item->id == $this->settings->date_format_id;
+ })->first()->format;
+
+ return $this;
+ }
+}
diff --git a/app/Services/Pdf/PdfDesigner.php b/app/Services/Pdf/PdfDesigner.php
new file mode 100644
index 000000000000..5db92b32a390
--- /dev/null
+++ b/app/Services/Pdf/PdfDesigner.php
@@ -0,0 +1,73 @@
+service->config->design->is_custom) {
+ $this->template = $this->composeFromPartials(json_decode(json_encode($this->service->config->design->design), true));
+ } else {
+ $this->template = file_get_contents(config('ninja.designs.base_path') . strtolower($this->service->config->design->name) . '.html');
+ }
+
+ return $this;
+ }
+
+ /**
+ * If the user has implemented a custom design, then we need to rebuild the design at this point
+ */
+
+ /**
+ * Returns the custom HTML design as
+ * a string
+ *
+ * @param array
+ * @return string
+ *
+ */
+ private function composeFromPartials(array $partials) :string
+ {
+ $html = '';
+
+ $html .= $partials['includes'];
+ $html .= $partials['header'];
+ $html .= $partials['body'];
+ $html .= $partials['footer'];
+
+ return $html;
+ }
+}
diff --git a/app/Services/Pdf/PdfMock.php b/app/Services/Pdf/PdfMock.php
new file mode 100644
index 000000000000..34a6fe52d5a3
--- /dev/null
+++ b/app/Services/Pdf/PdfMock.php
@@ -0,0 +1,758 @@
+mock->invitation);
+
+ $pdf_config = (new PdfConfiguration($pdf_service));
+ $pdf_config->entity = $this->mock;
+ $pdf_config->entity_string = $this->request['entity_type'];
+ $pdf_config->setTaxMap($this->mock->tax_map);
+ $pdf_config->setTotalTaxMap($this->mock->total_tax_map);
+ $pdf_config->client = $this->mock->client;
+ $pdf_config->settings_object = $this->mock->client;
+ $pdf_config->settings = $this->getMergedSettings();
+ $this->settings = $pdf_config->settings;
+ $pdf_config->entity_design_id = $pdf_config->settings->{"{$pdf_config->entity_string}_design_id"};
+ $pdf_config->setPdfVariables();
+ $pdf_config->setCurrency(Currency::find($this->settings->currency_id));
+ $pdf_config->setCountry(Country::find($this->settings->country_id));
+ $pdf_config->design = Design::find($this->decodePrimaryKey($pdf_config->entity_design_id));
+ $pdf_config->currency_entity = $this->mock->client;
+
+ $pdf_service->config = $pdf_config;
+
+ $pdf_designer = (new PdfDesigner($pdf_service))->build();
+ $pdf_service->designer = $pdf_designer;
+
+ $pdf_service->html_variables = $this->getStubVariables();
+
+ $pdf_builder = (new PdfBuilder($pdf_service))->build();
+ $pdf_service->builder = $pdf_builder;
+
+ $html = $pdf_service->getHtml();
+
+ return $pdf_service->resolvePdfEngine($html);
+ }
+
+ public function build(): self
+ {
+ $this->mock = $this->initEntity();
+
+ return $this;
+ }
+
+ public function initEntity(): mixed
+ {
+ match ($this->request['entity_type']) {
+ 'invoice' => $entity = Invoice::factory()->make(),
+ 'quote' => $entity = Quote::factory()->make(),
+ 'credit' => $entity = Credit::factory()->make(),
+ 'purchase_order' => $entity = PurchaseOrder::factory()->make(),
+ default => $entity = Invoice::factory()->make()
+ };
+
+ if ($this->request['entity_type'] == PurchaseOrder::class) {
+ $entity->vendor = Vendor::factory()->make();
+ } else {
+ $entity->client = Client::factory()->make();
+ }
+
+ $entity->tax_map = $this->getTaxMap();
+ $entity->total_tax_map = $this->getTotalTaxMap();
+ $entity->invitation = InvoiceInvitation::factory()->make();
+ $entity->invitation->company = $this->company;
+
+ return $entity;
+ }
+
+ public function getMergedSettings() :object
+ {
+ match ($this->request['settings_type']) {
+ 'group' => $settings = ClientSettings::buildClientSettings($this->company->settings, $this->request['settings']),
+ 'client' => $settings = ClientSettings::buildClientSettings($this->company->settings, $this->request['settings']),
+ 'company' => $settings = (object)$this->request['settings'],
+ default => $settings = $this->company->settings,
+ };
+
+ return $settings;
+ }
+
+
+ private function getTaxMap()
+ {
+ return collect([['name' => 'GST', 'total' => 10]]);
+ }
+
+ private function getTotalTaxMap()
+ {
+ return [['name' => 'GST', 'total' => 10]];
+ }
+
+ public function getStubVariables()
+ {
+ return ['values' =>
+ [
+
+ '$client.shipping_postal_code' => '46420',
+ '$client.billing_postal_code' => '11243',
+ '$company.city_state_postal' => 'Beveley Hills, CA, 90210',
+ '$company.postal_city_state' => 'CA',
+ '$company.postal_city' => '90210, CA',
+ '$product.gross_line_total' => '100',
+ '$client.postal_city_state' => '11243 Aufderharchester, North Carolina',
+ '$client.postal_city' => '11243 Aufderharchester, North Carolina',
+ '$client.shipping_address1' => '453',
+ '$client.shipping_address2' => '66327 Waters Trail',
+ '$client.city_state_postal' => 'Aufderharchester, North Carolina 11243',
+ '$client.shipping_address' => '453
66327 Waters Trail
Aufderharchester, North Carolina 11243
Afghanistan
',
+ '$client.billing_address2' => '63993 Aiyana View',
+ '$client.billing_address1' => '8447',
+ '$client.shipping_country' => 'USA',
+ '$invoiceninja.whitelabel' => 'https://raw.githubusercontent.com/invoiceninja/invoiceninja/v5-develop/public/images/new_logo.png',
+ '$client.billing_address' => '8447
63993 Aiyana View
Aufderharchester, North Carolina 11243
Afghanistan
',
+ '$client.billing_country' => 'USA',
+ '$task.gross_line_total' => '100',
+ '$contact.portal_button' => 'View client portal',
+ '$client.shipping_state' => 'Delaware',
+ '$invoice.public_notes' => 'These are some public notes for your document',
+ '$client.shipping_city' => 'Kesslerport',
+ '$client.billing_state' => 'North Carolina',
+ '$product.description' => 'A Product Description',
+ '$product.product_key' => 'A Product Key',
+ '$entity.public_notes' => 'Entity Public notes',
+ '$invoice.balance_due' => '$0.00',
+ '$client.public_notes' => ' ',
+ '$company.postal_code' => $this->settings->postal_code,
+ '$client.billing_city' => 'Aufderharchester',
+ '$secondary_font_name' => $this->settings->primary_font,
+ '$product.line_total' => '',
+ '$product.tax_amount' => '',
+ '$company.vat_number' => $this->settings->vat_number,
+ '$invoice.invoice_no' => '0029',
+ '$quote.quote_number' => '0029',
+ '$client.postal_code' => '11243',
+ '$contact.first_name' => 'Benedict',
+ '$secondary_font_url' => 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
+ '$contact.signature' => '',
+ '$company_logo_size' => $this->settings->company_logo_size ?: '65%',
+ '$product.tax_name1' => '',
+ '$product.tax_name2' => '',
+ '$product.tax_name3' => '',
+ '$product.unit_cost' => '',
+ '$quote.valid_until' => '2023-10-24',
+ '$custom_surcharge1' => '$0.00',
+ '$custom_surcharge2' => '$0.00',
+ '$custom_surcharge3' => '$0.00',
+ '$custom_surcharge4' => '$0.00',
+ '$quote.balance_due' => '$0.00',
+ '$company.id_number' => $this->settings->id_number,
+ '$invoice.po_number' => 'PO12345',
+ '$invoice_total_raw' => 0.0,
+ '$postal_city_state' => '11243 Aufderharchester, North Carolina',
+ '$client.vat_number' => '975977515',
+ '$city_state_postal' => 'Aufderharchester, North Carolina 11243',
+ '$contact.full_name' => 'Benedict Eichmann',
+ '$contact.last_name' => 'Eichmann',
+ '$company.country_2' => 'US',
+ '$product.product1' => '',
+ '$product.product2' => '',
+ '$product.product3' => '',
+ '$product.product4' => '',
+ '$statement_amount' => '',
+ '$task.description' => '',
+ '$product.discount' => '',
+ '$entity_issued_to' => 'Bob JOnes',
+ '$assigned_to_user' => '',
+ '$product.quantity' => '',
+ '$total_tax_labels' => '',
+ '$total_tax_values' => '',
+ '$invoice.discount' => '$0.00',
+ '$invoice.subtotal' => '$0.00',
+ '$company.address2' => $this->settings->address2,
+ '$partial_due_date' => ' ',
+ '$invoice.due_date' => '2023-10-24',
+ '$client.id_number' => 'CLI-2023-1234',
+ '$credit.po_number' => 'PO12345',
+ '$company.address1' => $this->settings->address1,
+ '$credit.credit_no' => '0029',
+ '$invoice.datetime' => '25/Feb/2023 1:10 am',
+ '$contact.custom1' => null,
+ '$contact.custom2' => null,
+ '$contact.custom3' => null,
+ '$contact.custom4' => null,
+ '$task.line_total' => '',
+ '$line_tax_labels' => '',
+ '$line_tax_values' => '',
+ '$secondary_color' => $this->settings->secondary_color,
+ '$invoice.balance' => '$0.00',
+ '$invoice.custom1' => 'custom value',
+ '$invoice.custom2' => 'custom value',
+ '$invoice.custom3' => 'custom value',
+ '$invoice.custom4' => 'custom value',
+ '$company.custom1' => 'custom value',
+ '$company.custom2' => 'custom value',
+ '$company.custom3' => 'custom value',
+ '$company.custom4' => 'custom value',
+ '$quote.po_number' => 'PO12345',
+ '$company.website' => $this->settings->website,
+ '$balance_due_raw' => '0.00',
+ '$entity.datetime' => '25/Feb/2023 1:10 am',
+ '$credit.datetime' => '25/Feb/2023 1:10 am',
+ '$client.address2' => '63993 Aiyana View',
+ '$client.address1' => '8447',
+ '$user.first_name' => 'Derrick Monahan DDS',
+ '$created_by_user' => 'Derrick Monahan DDS Erna Wunsch',
+ '$client.currency' => 'USD',
+ '$company.country' => 'United States',
+ '$company.address' => 'United States
',
+ '$tech_hero_image' => 'http://ninja.test:8000/images/pdf-designs/tech-hero-image.jpg',
+ '$task.tax_name1' => '',
+ '$task.tax_name2' => '',
+ '$task.tax_name3' => '',
+ '$client.balance' => '$0.00',
+ '$client_balance' => '$0.00',
+ '$credit.balance' => '$0.00',
+ '$credit_balance' => '$0.00',
+ '$gross_subtotal' => '$0.00',
+ '$invoice.amount' => '$0.00',
+ '$client.custom1' => 'custom value',
+ '$client.custom2' => 'custom value',
+ '$client.custom3' => 'custom value',
+ '$client.custom4' => 'custom value',
+ '$emailSignature' => 'A email signature.',
+ '$invoice.number' => '0029',
+ '$quote.quote_no' => '0029',
+ '$quote.datetime' => '25/Feb/2023 1:10 am',
+ '$client_address' => '8447
63993 Aiyana View
Aufderharchester, North Carolina 11243
Afghanistan
',
+ '$client.address' => '8447
63993 Aiyana View
Aufderharchester, North Carolina 11243
Afghanistan
',
+ '$payment_button' => 'Pay Now',
+ '$payment_qrcode' => '',
+ '$client.country' => 'Afghanistan',
+ '$user.last_name' => 'Erna Wunsch',
+ '$client.website' => 'http://www.parisian.org/',
+ '$dir_text_align' => 'left',
+ '$entity_images' => '',
+ '$task.discount' => '',
+ '$contact.email' => 'bob@gmail.com',
+ '$primary_color' => $this->settings->primary_color,
+ '$credit_amount' => '$0.00',
+ '$invoice.total' => '$0.00',
+ '$invoice.taxes' => '$0.00',
+ '$quote.custom1' => 'custom value',
+ '$quote.custom2' => 'custom value',
+ '$quote.custom3' => 'custom value',
+ '$quote.custom4' => 'custom value',
+ '$company.email' => $this->settings->email,
+ '$client.number' => '12345',
+ '$company.phone' => $this->settings->phone,
+ '$company.state' => $this->settings->state,
+ '$credit.number' => '0029',
+ '$entity_number' => '0029',
+ '$credit_number' => '0029',
+ '$global_margin' => '6.35mm',
+ '$contact.phone' => '681-480-9828',
+ '$portal_button' => 'View client portal',
+ '$paymentButton' => 'Pay Now',
+ '$entity_footer' => 'Default invoice footer',
+ '$client.lang_2' => 'en',
+ '$product.date' => '',
+ '$client.email' => 'client@gmail.com',
+ '$product.item' => '',
+ '$public_notes' => 'These are very public notes',
+ '$task.service' => '',
+ '$credit.total' => '$0.00',
+ '$net_subtotal' => '$0.00',
+ '$paid_to_date' => '$0.00',
+ '$quote.amount' => '$0.00',
+ '$company.city' => $this->settings->city,
+ '$payment.date' => '2022-10-10',
+ '$client.phone' => '555-123-3212',
+ '$number_short' => '0029',
+ '$quote.number' => '0029',
+ '$invoice.date' => '25/Feb/2023',
+ '$company.name' => $this->settings->name,
+ '$portalButton' => 'View client portal',
+ '$contact.name' => 'Benedict Eichmann',
+ '$entity.terms' => 'Default company invoice terms',
+ '$client.state' => 'North Carolina',
+ '$company.logo' => $this->settings->company_logo,
+ '$company_logo' => $this->settings->company_logo,
+ '$payment_link' => 'http://ninja.test:8000/client/pay/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
+ '$status_logo' => '',
+ '$description' => '',
+ '$product.tax' => '',
+ '$valid_until' => '',
+ '$your_entity' => '',
+ '$shipping' => '',
+ '$balance_due' => '$0.00',
+ '$outstanding' => '$0.00',
+ '$partial_due' => '$0.00',
+ '$quote.total' => '$0.00',
+ '$payment_due' => ' ',
+ '$credit.date' => '25/Feb/2023',
+ '$invoiceDate' => '25/Feb/2023',
+ '$view_button' => 'View Invoice',
+ '$client.city' => 'Aufderharchester',
+ '$spc_qr_code' => 'SPC
+0200
+1
+
+K
+434343
+
+
+
+
+CH
+
+
+
+
+
+
+
+0.000000
+USD
+
+
+
+
+
+
+
+NON
+
+0029
+EPD
+',
+ '$client_name' => 'A Client Called Bob',
+ '$client.name' => 'A Client Called Bob',
+ '$paymentLink' => 'http://ninja.test:8000/client/pay/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
+ '$payment_url' => 'http://ninja.test:8000/client/pay/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
+ '$page_layout' => $this->settings->page_layout,
+ '$task.task1' => '',
+ '$task.task2' => '',
+ '$task.task3' => '',
+ '$task.task4' => '',
+ '$task.hours' => '',
+ '$amount_due' => '$0.00',
+ '$amount_raw' => '0.00',
+ '$invoice_no' => '0029',
+ '$quote.date' => '25/Feb/2023',
+ '$vat_number' => '975977515',
+ '$viewButton' => 'View Invoice',
+ '$portal_url' => 'http://ninja.test:8000/client/',
+ '$task.date' => '',
+ '$task.rate' => '',
+ '$task.cost' => '',
+ '$statement' => '',
+ '$user_iban' => ' ',
+ '$signature' => ' ',
+ '$id_number' => 'ID Number',
+ '$credit_no' => '0029',
+ '$font_size' => $this->settings->font_size,
+ '$view_link' => 'View Invoice',
+ '$page_size' => $this->settings->page_size,
+ '$country_2' => 'AF',
+ '$firstName' => 'Benedict',
+ '$user.name' => 'Derrick Monahan DDS Erna Wunsch',
+ '$font_name' => 'Roboto',
+ '$auto_bill' => 'This invoice will automatically be billed to your credit card on file on the due date.',
+ '$payments' => '',
+ '$task.tax' => '',
+ '$discount' => '$0.00',
+ '$subtotal' => '$0.00',
+ '$company1' => 'custom value',
+ '$company2' => 'custom value',
+ '$company3' => 'custom value',
+ '$company4' => 'custom value',
+ '$due_date' => '2022-01-01',
+ '$poNumber' => 'PO-123456',
+ '$quote_no' => '0029',
+ '$address2' => '63993 Aiyana View',
+ '$address1' => '8447',
+ '$viewLink' => 'View Invoice',
+ '$autoBill' => 'This invoice will automatically be billed to your credit card on file on the due date.',
+ '$view_url' => 'http://ninja.test:8000/client/invoice/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
+ '$font_url' => 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
+ '$details' => '',
+ '$balance' => '$0.00',
+ '$partial' => '$0.00',
+ '$client1' => 'custom value',
+ '$client2' => 'custom value',
+ '$client3' => 'custom value',
+ '$client4' => 'custom value',
+ '$dueDate' => '2022-01-01',
+ '$invoice' => '0029',
+ '$account' => '434343',
+ '$country' => 'United States',
+ '$contact' => 'Benedict Eichmann',
+ '$app_url' => 'http://ninja.test:8000',
+ '$website' => 'http://www.parisian.org/',
+ '$entity' => '',
+ '$thanks' => 'Thanks!',
+ '$amount' => '$0.00',
+ '$method' => ' ',
+ '$number' => '0029',
+ '$footer' => 'Default invoice footer',
+ '$client' => 'The Client Name',
+ '$email' => 'email@invoiceninja.net',
+ '$notes' => '',
+ '_rate1' => '',
+ '_rate2' => '',
+ '_rate3' => '',
+ '$taxes' => '$0.00',
+ '$total' => '$0.00',
+ '$phone' => ' ',
+ '$terms' => 'Default company invoice terms',
+ '$from' => 'Bob Jones',
+ '$item' => '',
+ '$date' => '25/Feb/2023',
+ '$tax' => '',
+ '$dir' => 'ltr',
+ '$to' => 'Jimmy Giggles',
+ '$show_paid_stamp' => $this->settings->show_paid_stamp ? 'flex' : 'none',
+ '$status_logo' => ' ' . ctrans('texts.paid') .'
',
+ '$show_shipping_address' => $this->settings->show_shipping_address ? 'flex' : 'none',
+ '$show_shipping_address_block' => $this->settings->show_shipping_address ? 'block' : 'none',
+ '$show_shipping_address_visibility' => $this->settings->show_shipping_address ? 'visible' : 'hidden',
+ ],
+ 'labels' =>
+ [
+ '$client.shipping_postal_code_label' => 'Shipping Postal Code',
+ '$client.billing_postal_code_label' => 'Postal Code',
+ '$company.city_state_postal_label' => 'City/State/Postal',
+ '$company.postal_city_state_label' => 'Postal/City/State',
+ '$company.postal_city_label' => 'Postal/City',
+ '$product.gross_line_total_label' => 'Gross line total',
+ '$client.postal_city_state_label' => 'Postal/City/State',
+ '$client.postal_city_label' => 'Postal/City',
+ '$client.shipping_address1_label' => 'Shipping Street',
+ '$client.shipping_address2_label' => 'Shipping Apt/Suite',
+ '$client.city_state_postal_label' => 'City/State/Postal',
+ '$client.shipping_address_label' => 'Shipping Address',
+ '$client.billing_address2_label' => 'Apt/Suite',
+ '$client.billing_address1_label' => 'Street',
+ '$client.shipping_country_label' => 'Shipping Country',
+ '$invoiceninja.whitelabel_label' => '',
+ '$client.billing_address_label' => 'Address',
+ '$client.billing_country_label' => 'Country',
+ '$task.gross_line_total_label' => 'Gross line total',
+ '$contact.portal_button_label' => 'view_client_portal',
+ '$client.shipping_state_label' => 'Shipping State/Province',
+ '$invoice.public_notes_label' => 'Public Notes',
+ '$client.shipping_city_label' => 'Shipping City',
+ '$client.billing_state_label' => 'State/Province',
+ '$product.description_label' => 'Description',
+ '$product.product_key_label' => 'Product',
+ '$entity.public_notes_label' => 'Public Notes',
+ '$invoice.balance_due_label' => 'Balance Due',
+ '$client.public_notes_label' => 'Notes',
+ '$company.postal_code_label' => 'Postal Code',
+ '$client.billing_city_label' => 'City',
+ '$secondary_font_name_label' => '',
+ '$product.line_total_label' => 'Line Total',
+ '$product.tax_amount_label' => 'Tax',
+ '$company.vat_number_label' => 'VAT Number',
+ '$invoice.invoice_no_label' => 'Invoice Number',
+ '$quote.quote_number_label' => 'Quote Number',
+ '$client.postal_code_label' => 'Postal Code',
+ '$contact.first_name_label' => 'First Name',
+ '$secondary_font_url_label' => '',
+ '$contact.signature_label' => '',
+ '$company_logo_size_label' => '',
+ '$product.tax_name1_label' => 'Tax',
+ '$product.tax_name2_label' => 'Tax',
+ '$product.tax_name3_label' => 'Tax',
+ '$product.unit_cost_label' => 'Unit Cost',
+ '$quote.valid_until_label' => 'Valid Until',
+ '$custom_surcharge1_label' => '',
+ '$custom_surcharge2_label' => '',
+ '$custom_surcharge3_label' => '',
+ '$custom_surcharge4_label' => '',
+ '$quote.balance_due_label' => 'Balance Due',
+ '$company.id_number_label' => 'ID Number',
+ '$invoice.po_number_label' => 'PO Number',
+ '$invoice_total_raw_label' => 'Invoice Total',
+ '$postal_city_state_label' => 'Postal/City/State',
+ '$client.vat_number_label' => 'VAT Number',
+ '$city_state_postal_label' => 'City/State/Postal',
+ '$contact.full_name_label' => 'Name',
+ '$contact.last_name_label' => 'Last Name',
+ '$company.country_2_label' => 'Country',
+ '$product.product1_label' => '',
+ '$product.product2_label' => '',
+ '$product.product3_label' => '',
+ '$product.product4_label' => '',
+ '$statement_amount_label' => 'Amount',
+ '$task.description_label' => 'Description',
+ '$product.discount_label' => 'Discount',
+ '$entity_issued_to_label' => 'Invoice issued to',
+ '$assigned_to_user_label' => 'Name',
+ '$product.quantity_label' => 'Quantity',
+ '$total_tax_labels_label' => 'Taxes',
+ '$total_tax_values_label' => 'Taxes',
+ '$invoice.discount_label' => 'Discount',
+ '$invoice.subtotal_label' => 'Subtotal',
+ '$company.address2_label' => 'Apt/Suite',
+ '$partial_due_date_label' => 'Due Date',
+ '$invoice.due_date_label' => 'Due Date',
+ '$client.id_number_label' => 'ID Number',
+ '$credit.po_number_label' => 'PO Number',
+ '$company.address1_label' => 'Street',
+ '$credit.credit_no_label' => 'Invoice Number',
+ '$invoice.datetime_label' => 'Date',
+ '$contact.custom1_label' => '',
+ '$contact.custom2_label' => '',
+ '$contact.custom3_label' => '',
+ '$contact.custom4_label' => '',
+ '$task.line_total_label' => 'Line Total',
+ '$line_tax_labels_label' => 'Taxes',
+ '$line_tax_values_label' => 'Taxes',
+ '$secondary_color_label' => '',
+ '$invoice.balance_label' => 'Balance',
+ '$invoice.custom1_label' => '',
+ '$invoice.custom2_label' => '',
+ '$invoice.custom3_label' => '',
+ '$invoice.custom4_label' => '',
+ '$company.custom1_label' => '',
+ '$company.custom2_label' => '',
+ '$company.custom3_label' => '',
+ '$company.custom4_label' => '',
+ '$quote.po_number_label' => 'PO Number',
+ '$company.website_label' => 'Website',
+ '$balance_due_raw_label' => 'Balance Due',
+ '$entity.datetime_label' => 'Date',
+ '$credit.datetime_label' => 'Date',
+ '$client.address2_label' => 'Apt/Suite',
+ '$client.address1_label' => 'Street',
+ '$user.first_name_label' => 'First Name',
+ '$created_by_user_label' => 'Name',
+ '$client.currency_label' => '',
+ '$company.country_label' => 'Country',
+ '$company.address_label' => 'Address',
+ '$tech_hero_image_label' => '',
+ '$task.tax_name1_label' => 'Tax',
+ '$task.tax_name2_label' => 'Tax',
+ '$task.tax_name3_label' => 'Tax',
+ '$client.balance_label' => 'Account balance',
+ '$client_balance_label' => 'Account balance',
+ '$credit.balance_label' => 'Balance',
+ '$credit_balance_label' => 'Credit Balance',
+ '$gross_subtotal_label' => 'Subtotal',
+ '$invoice.amount_label' => 'Total',
+ '$client.custom1_label' => '',
+ '$client.custom2_label' => '',
+ '$client.custom3_label' => '',
+ '$client.custom4_label' => '',
+ '$emailSignature_label' => '',
+ '$invoice.number_label' => 'Invoice Number',
+ '$quote.quote_no_label' => 'Quote Number',
+ '$quote.datetime_label' => 'Date',
+ '$client_address_label' => 'Address',
+ '$client.address_label' => 'Address',
+ '$payment_button_label' => 'Pay Now',
+ '$payment_qrcode_label' => 'Pay Now',
+ '$client.country_label' => 'Country',
+ '$user.last_name_label' => 'Last Name',
+ '$client.website_label' => 'Website',
+ '$dir_text_align_label' => '',
+ '$entity_images_label' => '',
+ '$task.discount_label' => 'Discount',
+ '$contact.email_label' => 'Email',
+ '$primary_color_label' => '',
+ '$credit_amount_label' => 'Credit Amount',
+ '$invoice.total_label' => 'Invoice Total',
+ '$invoice.taxes_label' => 'Taxes',
+ '$quote.custom1_label' => '',
+ '$quote.custom2_label' => '',
+ '$quote.custom3_label' => '',
+ '$quote.custom4_label' => '',
+ '$company.email_label' => 'Email',
+ '$client.number_label' => 'Number',
+ '$company.phone_label' => 'Phone',
+ '$company.state_label' => 'State/Province',
+ '$credit.number_label' => 'Credit Number',
+ '$entity_number_label' => 'Invoice Number',
+ '$credit_number_label' => 'Invoice Number',
+ '$global_margin_label' => '',
+ '$contact.phone_label' => 'Phone',
+ '$portal_button_label' => 'view_client_portal',
+ '$paymentButton_label' => 'Pay Now',
+ '$entity_footer_label' => '',
+ '$client.lang_2_label' => '',
+ '$product.date_label' => 'Date',
+ '$client.email_label' => 'Email',
+ '$product.item_label' => 'Item',
+ '$public_notes_label' => 'Public Notes',
+ '$task.service_label' => 'Service',
+ '$credit.total_label' => 'Credit Total',
+ '$net_subtotal_label' => 'Net',
+ '$paid_to_date_label' => 'Paid to Date',
+ '$quote.amount_label' => 'Quote Total',
+ '$company.city_label' => 'City',
+ '$payment.date_label' => 'Payment Date',
+ '$client.phone_label' => 'Phone',
+ '$number_short_label' => 'Invoice #',
+ '$quote.number_label' => 'Quote Number',
+ '$invoice.date_label' => 'Invoice Date',
+ '$company.name_label' => 'Company Name',
+ '$portalButton_label' => 'view_client_portal',
+ '$contact.name_label' => 'Contact Name',
+ '$entity.terms_label' => 'Invoice Terms',
+ '$client.state_label' => 'State/Province',
+ '$company.logo_label' => 'Logo',
+ '$company_logo_label' => 'Logo',
+ '$payment_link_label' => 'Pay Now',
+ '$status_logo_label' => '',
+ '$description_label' => 'Description',
+ '$product.tax_label' => 'Tax',
+ '$valid_until_label' => 'Valid Until',
+ '$your_entity_label' => 'Your Invoice',
+ '$shipping_label' => 'Shipping',
+ '$balance_due_label' => 'Balance Due',
+ '$outstanding_label' => 'Balance Due',
+ '$partial_due_label' => 'Partial Due',
+ '$quote.total_label' => 'Total',
+ '$payment_due_label' => 'Payment due',
+ '$credit.date_label' => 'Credit Date',
+ '$invoiceDate_label' => 'Invoice Date',
+ '$view_button_label' => 'View Invoice',
+ '$client.city_label' => 'City',
+ '$spc_qr_code_label' => '',
+ '$client_name_label' => 'Client Name',
+ '$client.name_label' => 'Client Name',
+ '$paymentLink_label' => 'Pay Now',
+ '$payment_url_label' => 'Pay Now',
+ '$page_layout_label' => '',
+ '$task.task1_label' => '',
+ '$task.task2_label' => '',
+ '$task.task3_label' => '',
+ '$task.task4_label' => '',
+ '$task.hours_label' => 'Hours',
+ '$amount_due_label' => 'Amount due',
+ '$amount_raw_label' => 'Amount',
+ '$invoice_no_label' => 'Invoice Number',
+ '$quote.date_label' => 'Quote Date',
+ '$vat_number_label' => 'VAT Number',
+ '$viewButton_label' => 'View Invoice',
+ '$portal_url_label' => '',
+ '$task.date_label' => 'Date',
+ '$task.rate_label' => 'Rate',
+ '$task.cost_label' => 'Rate',
+ '$statement_label' => 'Statement',
+ '$user_iban_label' => '',
+ '$signature_label' => '',
+ '$id_number_label' => 'ID Number',
+ '$credit_no_label' => 'Invoice Number',
+ '$font_size_label' => '',
+ '$view_link_label' => 'View Invoice',
+ '$page_size_label' => '',
+ '$country_2_label' => 'Country',
+ '$firstName_label' => 'First Name',
+ '$user.name_label' => 'Name',
+ '$font_name_label' => '',
+ '$auto_bill_label' => '',
+ '$payments_label' => 'Payments',
+ '$task.tax_label' => 'Tax',
+ '$discount_label' => 'Discount',
+ '$subtotal_label' => 'Subtotal',
+ '$company1_label' => '',
+ '$company2_label' => '',
+ '$company3_label' => '',
+ '$company4_label' => '',
+ '$due_date_label' => 'Due Date',
+ '$poNumber_label' => 'PO Number',
+ '$quote_no_label' => 'Quote Number',
+ '$address2_label' => 'Apt/Suite',
+ '$address1_label' => 'Street',
+ '$viewLink_label' => 'View Invoice',
+ '$autoBill_label' => '',
+ '$view_url_label' => 'View Invoice',
+ '$font_url_label' => '',
+ '$details_label' => 'Details',
+ '$balance_label' => 'Balance',
+ '$partial_label' => 'Partial Due',
+ '$client1_label' => '',
+ '$client2_label' => '',
+ '$client3_label' => '',
+ '$client4_label' => '',
+ '$dueDate_label' => 'Due Date',
+ '$invoice_label' => 'Invoice Number',
+ '$account_label' => 'Company Name',
+ '$country_label' => 'Country',
+ '$contact_label' => 'Name',
+ '$app_url_label' => '',
+ '$website_label' => 'Website',
+ '$entity_label' => 'Invoice',
+ '$thanks_label' => 'Thanks',
+ '$amount_label' => 'Total',
+ '$method_label' => 'Method',
+ '$number_label' => 'Invoice Number',
+ '$footer_label' => '',
+ '$client_label' => 'Client Name',
+ '$email_label' => 'Email',
+ '$notes_label' => 'Public Notes',
+ '_rate1_label' => 'Tax',
+ '_rate2_label' => 'Tax',
+ '_rate3_label' => 'Tax',
+ '$taxes_label' => 'Taxes',
+ '$total_label' => 'Total',
+ '$phone_label' => 'Phone',
+ '$terms_label' => 'Invoice Terms',
+ '$from_label' => 'From',
+ '$item_label' => 'Item',
+ '$date_label' => 'Invoice Date',
+ '$tax_label' => 'Tax',
+ '$dir_label' => '',
+ '$to_label' => 'To',
+ '$show_paid_stamp_label' => '',
+ '$status_logo_label' => '',
+ '$show_shipping_address_label' => '',
+ '$show_shipping_address_block_label' => '',
+ '$show_shipping_address_visibility_label' => '',
+ ],
+];
+ }
+}
diff --git a/app/Services/Pdf/PdfService.php b/app/Services/Pdf/PdfService.php
new file mode 100644
index 000000000000..5f202749067c
--- /dev/null
+++ b/app/Services/Pdf/PdfService.php
@@ -0,0 +1,145 @@
+invitation = $invitation;
+
+ $this->company = $invitation->company;
+
+ $this->document_type = $document_type;
+
+ $this->options = $options;
+ }
+
+ /**
+ * Resolves the PDF generation type and
+ * attempts to generate a PDF from the HTML
+ * string.
+ *
+ * @return mixed | Exception
+ *
+ */
+ public function getPdf()
+ {
+ try {
+ $pdf = $this->resolvePdfEngine($this->getHtml());
+
+ $numbered_pdf = $this->pageNumbering($pdf, $this->company);
+
+ if ($numbered_pdf) {
+ $pdf = $numbered_pdf;
+ }
+ } catch (\Exception $e) {
+ nlog(print_r($e->getMessage(), 1));
+ throw new \Exception($e->getMessage(), $e->getCode());
+ }
+
+ return $pdf;
+ }
+
+ /**
+ * Renders the dom document to HTML
+ *
+ * @return string
+ *
+ */
+ public function getHtml(): string
+ {
+ $html = $this->builder->getCompiledHTML();
+
+ if (config('ninja.log_pdf_html')) {
+ info($html);
+ }
+
+ return $html;
+ }
+
+ /**
+ * Initialize all the services to build the PDF
+ *
+ * @return self
+ */
+ public function init(): self
+ {
+ $this->config = (new PdfConfiguration($this))->init();
+
+
+ $this->html_variables = $this->config->client ?
+ (new HtmlEngine($this->invitation))->generateLabelsAndValues() :
+ (new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
+
+ $this->designer = (new PdfDesigner($this))->build();
+
+ $this->builder = (new PdfBuilder($this))->build();
+
+ return $this;
+ }
+
+ /**
+ * resolvePdfEngine
+ *
+ * @return mixed
+ */
+ public function resolvePdfEngine(string $html): mixed
+ {
+ 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;
+ }
+}
diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php
index 0232b6876698..4487a381e6af 100644
--- a/app/Services/PdfMaker/Design.php
+++ b/app/Services/PdfMaker/Design.php
@@ -121,6 +121,10 @@ class Design extends BaseDesign
'id' => 'client-details',
'elements' => $this->clientDetails(),
],
+ 'shipping-details' => [
+ 'id' => 'shipping-details',
+ 'elements' => $this->shippingDetails(),
+ ],
'vendor-details' => [
'id' => 'vendor-details',
'elements' => $this->vendorDetails(),
@@ -171,10 +175,6 @@ class Design extends BaseDesign
$this->sharedFooterElements(),
],
],
- // 'swiss-qr' => [
- // 'id' => 'swiss-qr',
- // 'elements' => $this->swissQrCodeElement(),
- // ]
];
}
@@ -237,6 +237,34 @@ class Design extends BaseDesign
return $elements;
}
+ public function shippingDetails(): array
+ {
+ $elements = [];
+
+ if (!$this->client) {
+ return $elements;
+ }
+
+ $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->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.name']],
+ ['element' => 'p', 'content' => $this->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.shipping_address1']],
+ ['element' => 'p', 'content' => $this->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'shipping_address-client.shipping_address2']],
+ ['element' => 'p', 'show_empty' => false, 'elements' => [
+ ['element' => 'span', 'content' => "{$this->client->shipping_city} ", 'properties' => ['ref' => 'shipping_address-client.shipping_city']],
+ ['element' => 'span', 'content' => "{$this->client->shipping_state} ", 'properties' => ['ref' => 'shipping_address-client.shipping_state']],
+ ['element' => 'span', 'content' => "{$this->client->shipping_postal_code} ", 'properties' => ['ref' => 'shipping_address-client.shipping_postal_code']],
+ ]],
+ ['element' => 'p', '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']];
+ // }
+
+ return $elements;
+ }
+
public function clientDetails(): array
{
$elements = [];
@@ -275,6 +303,72 @@ class Design extends BaseDesign
return $elements;
}
+ public function entityDetailsx(): array
+ {
+ if ($this->type === 'statement') {
+ $s_date = $this->translateDate($this->options['start_date'], $this->client->date_format(), $this->client->locale()) . " - " . $this->translateDate($this->options['end_date'], $this->client->date_format(), $this->client->locale());
+
+ return [
+ ['element' => 'p', 'content' => "".ctrans('texts.statement')."
", 'properties' => ['data-ref' => 'statement-label']],
+ ['element' => 'p', 'content' => ctrans('texts.statement_date'), 'properties' => ['data-ref' => 'statement-label'],'elements' =>
+ ['element' => 'span', 'content' => "{$s_date} "]
+ ],
+ ['element' => 'p', 'content' => '$balance_due_label', 'properties' => ['data-ref' => 'statement-label'],'elements' =>
+ ['element' => 'span', 'content' => Number::formatMoney($this->invoices->sum('balance'), $this->client)]
+ ],
+ ];
+ }
+
+ $variables = $this->context['pdf_variables']['invoice_details'];
+
+ if ($this->entity instanceof Quote) {
+ $variables = $this->context['pdf_variables']['quote_details'];
+
+ if ($this->entity->partial > 0) {
+ $variables[] = '$quote.balance_due';
+ }
+ }
+
+ if ($this->entity instanceof Credit) {
+ $variables = $this->context['pdf_variables']['credit_details'];
+ }
+
+ if ($this->vendor) {
+ $variables = $this->context['pdf_variables']['purchase_order_details'];
+ }
+
+ $elements = [];
+
+ // We don't want to show account balance or invoice total on PDF.. or any amount with currency.
+ if ($this->type == self::DELIVERY_NOTE) {
+ $variables = array_filter($variables, function ($m) {
+ return !in_array($m, ['$invoice.balance_due', '$invoice.total']);
+ });
+ }
+
+ foreach ($variables as $variable) {
+ $_variable = explode('.', $variable)[1];
+ $_customs = ['custom1', 'custom2', 'custom3', 'custom4'];
+
+ /* 2/7/2022 don't show custom values if they are empty */
+ $var = str_replace("custom", "custom_value", $_variable);
+
+ if (in_array($_variable, $_customs) && !empty($this->entity->{$var})) {
+ $elements[] = ['element' => 'div', 'properties' => ['style' => "display: table-row; visibility: {$this->entityVariableCheck($_variable)};"],'elements' => [
+ ['element' => 'div', 'content' => $variable . '_label', 'properties' => ['class' => 'entity-details-cell', 'data-ref' => 'entity_details-' . substr($variable, 1) . '_label']],
+ ['element' => 'div', 'content' => $variable, 'properties' => ['class' => 'entity-details-cell', 'data-ref' => 'entity_details-' . substr($variable, 1)]],
+ ]];
+ } else {
+ $elements[] = ['element' => 'div', 'properties' => ['style' => "display: table-row; visibility: {$this->entityVariableCheck($variable)};"], 'elements' => [
+ ['element' => 'div', 'content' => $variable . '_label', 'properties' => ['class' => 'entity-details-cell','data-ref' => 'entity_details-' . substr($variable, 1) . '_label']],
+ ['element' => 'div', 'content' => $variable, 'properties' => ['class' => 'entity-details-cell','data-ref' => 'entity_details-' . substr($variable, 1)]],
+ ]];
+ }
+ }
+
+ return $elements;
+ }
+
public function entityDetails(): array
{
if ($this->type === 'statement') {
@@ -348,6 +442,7 @@ class Design extends BaseDesign
return $elements;
}
+
public function deliveryNoteTable(): array
{
if ($this->type !== self::DELIVERY_NOTE) {
diff --git a/app/Services/PdfMaker/Designs/Utilities/DesignHelpers.php b/app/Services/PdfMaker/Designs/Utilities/DesignHelpers.php
index b6393f563527..bef121554684 100644
--- a/app/Services/PdfMaker/Designs/Utilities/DesignHelpers.php
+++ b/app/Services/PdfMaker/Designs/Utilities/DesignHelpers.php
@@ -246,6 +246,7 @@ trait DesignHelpers
]];
}
+
public function entityVariableCheck(string $variable): bool
{
// Extract $invoice.date => date
@@ -282,6 +283,43 @@ trait DesignHelpers
return false;
}
+ public function entityVariableCheckx(string $variable): string
+ {
+ // Extract $invoice.date => date
+ // so we can append date as $entity->date and not $entity->$invoice.date;
+
+ // When it comes to invoice balance, we'll always show it.
+ if ($variable == '$invoice.total') {
+ return 'visible';
+ }
+
+ // Some variables don't map 1:1 to table columns. This gives us support for such cases.
+ $aliases = [
+ '$quote.balance_due' => 'partial',
+ ];
+
+ try {
+ $_variable = explode('.', $variable)[1];
+ } catch (Exception $e) {
+ nlog("Company settings seems to be broken. Could not resolve {$variable} type.");
+ return 'collapse';
+ }
+
+ if (\in_array($variable, \array_keys($aliases))) {
+ $_variable = $aliases[$variable];
+ }
+
+ if (is_null($this->entity->{$_variable})) {
+ return 'collapse';
+ }
+
+ if (empty($this->entity->{$_variable})) {
+ return 'collapse';
+ }
+
+ return 'visible';
+ }
+
public function composeFromPartials(array $partials)
{
$html = '';
diff --git a/app/Services/PdfMaker/PdfMakerUtilities.php b/app/Services/PdfMaker/PdfMakerUtilities.php
index 6670360c0691..95be78b0b1e1 100644
--- a/app/Services/PdfMaker/PdfMakerUtilities.php
+++ b/app/Services/PdfMaker/PdfMakerUtilities.php
@@ -178,7 +178,7 @@ trait PdfMakerUtilities
foreach ($children as $key => &$child) {
if (isset($child['content']) && isset($child['show_empty']) && $child['show_empty'] === false) {
$value = strtr($child['content'], $variables['values']);
- if ($value === '' || $value === ' ') {
+ if ($value === '' || $value === ' ' || $value === ' '){
$child['is_empty'] = true;
}
}
diff --git a/app/Services/Preview/StubBuilder.php b/app/Services/Preview/StubBuilder.php
new file mode 100644
index 000000000000..c17d3de593ba
--- /dev/null
+++ b/app/Services/Preview/StubBuilder.php
@@ -0,0 +1,309 @@
+entity_type = $entity_type;
+
+ return $this;
+ }
+
+ public function build(): self
+ {
+ try {
+ DB::connection(config('database.default'))->transaction(function () {
+ $this->createRecipient()
+ ->initializeSettings()
+ ->createEntity()
+ ->linkRelations()
+ ->buildHtml();
+ });
+ } catch (\Throwable $throwable) {
+ nlog("DB ERROR " . $throwable->getMessage());
+
+ if (DB::connection(config('database.default'))->transactionLevel() > 0) {
+ DB::connection(config('database.default'))->rollBack();
+ }
+ } catch(\Exception $e) {
+ nlog($e->getMessage());
+
+ if (DB::connection(config('database.default'))->transactionLevel() > 0) {
+ DB::connection(config('database.default'))->rollBack();
+ }
+ }
+
+ return $this;
+ }
+
+ public function getPdf(): mixed
+ {
+ if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
+ return (new Phantom)->convertHtmlToPdf($this->html);
+ }
+
+ if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
+ $pdf = (new NinjaPdf())->build($this->html);
+
+ $numbered_pdf = $this->pageNumbering($pdf, $this->company);
+
+ if ($numbered_pdf) {
+ $pdf = $numbered_pdf;
+ }
+
+ return $pdf;
+ }
+
+ return (new PreviewPdf($this->html, $this->company))->handle();
+ }
+
+ private function initializeSettings(): self
+ {
+ $this->dynamic_settings_type = 'company';
+
+ match ($this->dynamic_settings_type) {
+ 'company' => $this->setCompanySettings(),
+ 'client' => $this->setClientSettings(),
+ 'group' => $this->setGroupSettings(),
+ };
+
+
+ return $this;
+ }
+
+ private function setCompanySettings(): self
+ {
+ $this->company->settings = $this->settings;
+ $this->company->save();
+
+ return $this;
+ }
+
+ private function setClientSettings(): self
+ {
+ $this->recipient->settings = $this->settings;
+ $this->recipient->save();
+
+ return $this;
+ }
+
+ private function setGroupSettings(): self
+ {
+ $g = GroupSettingFactory::create($this->company->id, $this->user->id);
+ $g->name = Str::random(10);
+ $g->settings = $this->settings;
+ $g->save();
+
+ $this->recipient->group_settings_id = $g->id;
+ $this->recipient->save();
+
+ return $this;
+ }
+
+ public function setSettings($settings): self
+ {
+ $this->settings = $settings;
+
+ return $this;
+ }
+
+ public function setSettingsType($type): self
+ {
+ $this->dynamic_settings_type = $type;
+
+ return $this;
+ }
+
+ private function buildHtml(): self
+ {
+ $html = new HtmlEngine($this->invitation);
+
+ $design_string = "{$this->entity_type}_design_id";
+
+ $design = DesignModel::withTrashed()->find($this->decodePrimaryKey($html->settings->{$design_string}));
+
+ $template = new PdfMakerDesign(strtolower($design->name));
+
+ $state = [
+ 'template' => $template->elements([
+ 'client' => $this->recipient,
+ 'entity' => $this->entity,
+ 'pdf_variables' => (array) $html->settings->pdf_variables,
+ '$product' => $design->design->product,
+ ]),
+ 'variables' => $html->generateLabelsAndValues(),
+ 'process_markdown' => $this->company->markdown_enabled,
+ ];
+
+ $maker = new PdfMaker($state);
+
+ $this->html = $maker->design($template)
+ ->build()
+ ->getCompiledHTML();
+
+ return $this;
+ }
+
+ private function linkRelations(): self
+ {
+ $this->entity->setRelation('invitations', $this->invitation);
+ $this->entity->setRelation($this->recipient_string, $this->recipient);
+ $this->entity->setRelation('company', $this->company);
+ $this->entity->load("{$this->recipient_string}.company");
+
+ return $this;
+ }
+
+ private function createRecipient(): self
+ {
+ match ($this->entity_type) {
+ 'invoice' => $this->createClient(),
+ 'quote' => $this->createClient(),
+ 'credit' => $this->createClient(),
+ 'purchase_order' => $this->createVendor(),
+ };
+
+ return $this;
+ }
+
+ private function createClient(): self
+ {
+ $this->recipient = Client::factory()->create([
+ 'user_id' => $this->user->id,
+ 'company_id' => $this->company->id,
+ ]);
+
+ $this->contact = ClientContact::factory()->create([
+ 'user_id' => $this->user->id,
+ 'company_id' => $this->company->id,
+ 'client_id' => $this->recipient->id,
+ ]);
+
+ $this->recipient_string = 'client';
+
+ return $this;
+ }
+
+ private function createVendor(): self
+ {
+ $this->recipient = Vendor::factory()->create([
+ 'user_id' => $this->user->id,
+ 'company_id' => $this->user->company()->id,
+ ]);
+
+ $this->contact = VendorContact::factory()->create([
+ 'user_id' => $this->user->id,
+ 'company_id' => $this->company->id,
+ 'vendor_id' => $this->recipient->id,
+ ]);
+
+ $this->recipient_string = 'vendor';
+
+ return $this;
+ }
+
+
+ private function createEntity(): self
+ {
+ match ($this->entity_type) {
+ 'invoice' => $this->createInvoice(),
+ 'quote' => $this->createQuote(),
+ 'credit' => $this->createCredit(),
+ 'purchase_order' => $this->createPurchaseOrder(),
+ };
+
+ return $this;
+ }
+
+ private function createInvoice()
+ {
+ $this->entity = Invoice::factory()->create([
+ 'user_id' => $this->user->id,
+ 'company_id' => $this->company->id,
+ 'client_id' => $this->recipient->id,
+ 'terms' => $this->company->settings->invoice_terms,
+ 'footer' => $this->company->settings->invoice_footer,
+ 'status_id' => Invoice::STATUS_PAID,
+ ]);
+
+ $this->invitation = InvoiceInvitation::factory()->create([
+ 'user_id' => $this->user->id,
+ 'company_id' => $this->company->id,
+ 'invoice_id' => $this->entity->id,
+ 'client_contact_id' => $this->contact->id,
+ ]);
+ }
+
+ private function createQuote()
+ {
+ $this->entity->save();
+ }
+
+ private function createCredit()
+ {
+ $this->entity->save();
+ }
+
+ private function createPurchaseOrder()
+ {
+ $this->entity->save();
+ }
+}
diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php
index 5bef865ce085..c3c9ad8a10ba 100644
--- a/app/Utils/Helpers.php
+++ b/app/Utils/Helpers.php
@@ -105,7 +105,7 @@ class Helpers
* Process reserved keywords on PDF.
*
* @param string $value
- * @param Client|Company $entity
+ * @param Client|Company|Vendor $entity
* @param null|Carbon $currentDateTime
* @return null|string
*/
diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php
index 11ec8649f24b..cc4129a536d0 100644
--- a/app/Utils/HtmlEngine.php
+++ b/app/Utils/HtmlEngine.php
@@ -116,6 +116,10 @@ class HtmlEngine
$data['$global_margin'] = ['value' => '6.35mm', 'label' => ''];
$data['$company_logo_size'] = ['value' => $this->resolveCompanyLogoSize(), 'label' => ''];
+ $data['$show_shipping_address'] = ['value' => $this->settings?->show_shipping_address ? 'flex' : 'none', 'label' => ''];
+ $data['$show_shipping_address_block'] = ['value' => $this->settings?->show_shipping_address ? 'block' : 'none', 'label' => ''];
+ $data['$show_shipping_address_visibility'] = ['value' => $this->settings?->show_shipping_address ? 'visible' : 'hidden', 'label' => ''];
+
$data['$tax'] = ['value' => '', 'label' => ctrans('texts.tax')];
$data['$app_url'] = ['value' => $this->generateAppUrl(), 'label' => ''];
$data['$from'] = ['value' => '', 'label' => ctrans('texts.from')];
@@ -124,21 +128,21 @@ class HtmlEngine
$data['$total_tax_values'] = ['value' => $this->totalTaxValues(), 'label' => ctrans('texts.taxes')];
$data['$line_tax_labels'] = ['value' => $this->lineTaxLabels(), 'label' => ctrans('texts.taxes')];
$data['$line_tax_values'] = ['value' => $this->lineTaxValues(), 'label' => ctrans('texts.taxes')];
- $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.date')];
+ $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.date')];
$data['$status_logo'] = ['value' => '', 'label' => ''];
$data['$invoice.date'] = &$data['$date'];
$data['$invoiceDate'] = &$data['$date'];
- $data['$due_date'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
+ $data['$due_date'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
- $data['$partial_due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
+ $data['$partial_due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
$data['$dueDate'] = &$data['$due_date'];
- $data['$payment_due'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.payment_due')];
+ $data['$payment_due'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.payment_due')];
$data['$invoice.due_date'] = &$data['$due_date'];
- $data['$invoice.number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
- $data['$invoice.po_number'] = ['value' => $this->entity->po_number ?: ' ', 'label' => ctrans('texts.po_number')];
+ $data['$invoice.number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
+ $data['$invoice.po_number'] = ['value' => $this->entity->po_number ?: ' ', 'label' => ctrans('texts.po_number')];
$data['$poNumber'] = &$data['$invoice.po_number'];
$data['$po_number'] = &$data['$invoice.po_number'];
$data['$entity.datetime'] = ['value' => $this->formatDatetime($this->entity->created_at, $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.date')];
@@ -148,13 +152,13 @@ class HtmlEngine
$data['$payment_button'] = ['value' => $this->buildViewButton($this->invitation->getPaymentLink(), 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')];
- $data['$exchange_rate'] = ['value' => $this->entity->exchange_rate ?: ' ', 'label' => ctrans('texts.exchange_rate')];
+ $data['$exchange_rate'] = ['value' => $this->entity->exchange_rate ?: ' ', 'label' => ctrans('texts.exchange_rate')];
if ($this->entity_string == 'invoice' || $this->entity_string == 'recurring_invoice') {
$data['$entity'] = ['value' => '', 'label' => ctrans('texts.invoice')];
- $data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
- $data['$invoice'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
- $data['$number_short'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number_short')];
+ $data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
+ $data['$invoice'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
+ $data['$number_short'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number_short')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.invoice_terms')];
$data['$terms'] = &$data['$entity.terms'];
$data['$view_link'] = ['value' => $this->buildViewButton($this->invitation->getLink(), ctrans('texts.view_invoice')), 'label' => ctrans('texts.view_invoice')];
@@ -163,12 +167,12 @@ class HtmlEngine
$data['$view_button'] = &$data['$view_link'];
$data['$paymentButton'] = &$data['$payment_button'];
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_invoice')];
- $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.invoice_date')];
+ $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.invoice_date')];
- $data['$invoice.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice1', $this->entity->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice1')];
- $data['$invoice.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
- $data['$invoice.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
- $data['$invoice.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
+ $data['$invoice.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice1', $this->entity->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice1')];
+ $data['$invoice.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
+ $data['$invoice.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
+ $data['$invoice.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
$data['$custom1'] = &$data['$invoice.custom1'];
$data['$custom2'] = &$data['$invoice.custom2'];
@@ -190,10 +194,10 @@ class HtmlEngine
$data['$invoice.project'] = &$data['$project.name'];
}
- if ($this->entity->status_id == 4) {
- $data['$status_logo'] = ['value' => ' ' . ctrans('texts.paid') .'
', 'label' => ''];
- }
+ $data['$status_logo'] = ['value' => ' ' . ctrans('texts.paid') .'
', 'label' => ''];
+ $data['$show_paid_stamp'] = ['value' => $this->entity->status_id == 4 && $this->settings?->show_paid_stamp ? 'flex' : 'none', 'label' => ''];
+
if ($this->entity->vendor) {
$data['$invoice.vendor'] = ['value' => $this->entity->vendor->present()->name(), 'label' => ctrans('texts.vendor_name')];
}
@@ -219,12 +223,12 @@ class HtmlEngine
$data['$view_button'] = &$data['$view_link'];
$data['$approveButton'] = ['value' => $this->buildViewButton($this->invitation->getLink(), ctrans('texts.view_quote')), 'label' => ctrans('texts.approve')];
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_quote')];
- $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.quote_date')];
+ $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.quote_date')];
- $data['$quote.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice1', $this->entity->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice1')];
- $data['$quote.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
- $data['$quote.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
- $data['$quote.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
+ $data['$quote.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice1', $this->entity->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice1')];
+ $data['$quote.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
+ $data['$quote.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
+ $data['$quote.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
$data['$custom1'] = &$data['$quote.custom1'];
$data['$custom2'] = &$data['$quote.custom2'];
@@ -263,12 +267,12 @@ class HtmlEngine
$data['$viewLink'] = &$data['$view_link'];
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_credit')];
// $data['$view_link'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_credit')];
- $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.credit_date')];
+ $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.credit_date')];
- $data['$credit.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'credit1', $this->entity->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice1')];
- $data['$credit.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'credit2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
- $data['$credit.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'credit3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
- $data['$credit.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'credit4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
+ $data['$credit.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'credit1', $this->entity->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice1')];
+ $data['$credit.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'credit2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
+ $data['$credit.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'credit3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
+ $data['$credit.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'credit4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
$data['$custom1'] = &$data['$credit.custom1'];
$data['$custom2'] = &$data['$credit.custom2'];
@@ -289,32 +293,32 @@ class HtmlEngine
$data['$portal_url'] = ['value' => $this->invitation->getPortalLink(), 'label' =>''];
$data['$entity_number'] = &$data['$number'];
- $data['$invoice.discount'] = ['value' => Number::formatMoney($this->entity_calc->getTotalDiscount(), $this->client) ?: ' ', 'label' => ($this->entity->is_amount_discount) ? ctrans('texts.discount') : ctrans('texts.discount').' '.$this->entity->discount.'%'];
+ $data['$invoice.discount'] = ['value' => Number::formatMoney($this->entity_calc->getTotalDiscount(), $this->client) ?: ' ', 'label' => ($this->entity->is_amount_discount) ? ctrans('texts.discount') : ctrans('texts.discount').' '.$this->entity->discount.'%'];
$data['$discount'] = &$data['$invoice.discount'];
- $data['$subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.subtotal')];
- $data['$gross_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getGrossSubTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.subtotal')];
+ $data['$subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.subtotal')];
+ $data['$gross_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getGrossSubTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.subtotal')];
if ($this->entity->uses_inclusive_taxes) {
- $data['$net_subtotal'] = ['value' => Number::formatMoney(($this->entity_calc->getSubTotal() - $this->entity->total_taxes - $this->entity_calc->getTotalDiscount()), $this->client) ?: ' ', 'label' => ctrans('texts.net_subtotal')];
+ $data['$net_subtotal'] = ['value' => Number::formatMoney(($this->entity_calc->getSubTotal() - $this->entity->total_taxes - $this->entity_calc->getTotalDiscount()), $this->client) ?: ' ', 'label' => ctrans('texts.net_subtotal')];
} else {
- $data['$net_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal() - $this->entity_calc->getTotalDiscount(), $this->client) ?: ' ', 'label' => ctrans('texts.net_subtotal')];
+ $data['$net_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal() - $this->entity_calc->getTotalDiscount(), $this->client) ?: ' ', 'label' => ctrans('texts.net_subtotal')];
}
$data['$invoice.subtotal'] = &$data['$subtotal'];
/* Do not change the order of these */
if ($this->entity->partial > 0) {
- $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->client) ?: ' ', 'label' => ctrans('texts.partial_due')];
+ $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->client) ?: ' ', 'label' => ctrans('texts.partial_due')];
$data['$balance_due_raw'] = ['value' => $this->entity->partial, 'label' => ctrans('texts.partial_due')];
$data['$amount_raw'] = ['value' => $this->entity->partial, 'label' => ctrans('texts.partial_due')];
- $data['$due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
+ $data['$due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
} else {
if ($this->entity->status_id == 1) {
- $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->amount, $this->client) ?: ' ', 'label' => ctrans('texts.balance_due')];
+ $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->amount, $this->client) ?: ' ', 'label' => ctrans('texts.balance_due')];
$data['$balance_due_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.balance_due')];
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
} else {
- $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: ' ', 'label' => ctrans('texts.balance_due')];
+ $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: ' ', 'label' => ctrans('texts.balance_due')];
$data['$balance_due_raw'] = ['value' => $this->entity->balance, 'label' => ctrans('texts.balance_due')];
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
}
@@ -325,13 +329,13 @@ class HtmlEngine
if ($this->entity_string == 'credit') {
- $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
+ $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
$data['$balance_due_raw'] = ['value' => $this->entity->balance, 'label' => ctrans('texts.credit_balance')];
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
}
if ($this->entity_string == 'credit' && $this->entity->status_id == 1) {
- $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->amount, $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
+ $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->amount, $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
$data['$balance_due_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.credit_balance')];
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
}
@@ -339,27 +343,27 @@ class HtmlEngine
/* Do not change the order of these */
$data['$outstanding'] = &$data['$balance_due'];
- $data['$partial_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->client) ?: ' ', 'label' => ctrans('texts.partial_due')];
+ $data['$partial_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->client) ?: ' ', 'label' => ctrans('texts.partial_due')];
$data['$partial'] = &$data['$partial_due'];
- $data['$total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.total')];
+ $data['$total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.total')];
$data['$amount'] = &$data['$total'];
$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) ?: ' ', 'label' => ctrans('texts.invoice_total')];
+ $data['$invoice.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', '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) ?: ' ', 'label' => ctrans('texts.quote_total')];
- $data['$credit.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_total')];
- $data['$credit.number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.credit_number')];
+ $data['$quote.amount'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.quote_total')];
+ $data['$credit.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_total')];
+ $data['$credit.number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.credit_number')];
$data['$credit.total'] = &$data['$credit.total'];
$data['$credit.po_number'] = &$data['$invoice.po_number'];
$data['$credit.date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.credit_date')];
- $data['$balance'] = ['value' => Number::formatMoney($this->entity_calc->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.balance')];
+ $data['$balance'] = ['value' => Number::formatMoney($this->entity_calc->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.balance')];
$data['$credit.balance'] = &$data['$balance'];
$data['$invoice.balance'] = &$data['$balance'];
- $data['$taxes'] = ['value' => Number::formatMoney($this->entity_calc->getItemTotalTaxes(), $this->client) ?: ' ', 'label' => ctrans('texts.taxes')];
+ $data['$taxes'] = ['value' => Number::formatMoney($this->entity_calc->getItemTotalTaxes(), $this->client) ?: ' ', 'label' => ctrans('texts.taxes')];
$data['$invoice.taxes'] = &$data['$taxes'];
$data['$user.name'] = ['value' => $this->entity->user->present()->name(), 'label' => ctrans('texts.name')];
@@ -368,7 +372,7 @@ class HtmlEngine
$data['$created_by_user'] = &$data['$user.name'];
$data['$assigned_to_user'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->present()->name() : '', 'label' => ctrans('texts.name')];
- $data['$user_iban'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')];
+ $data['$user_iban'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')];
$data['$invoice.public_notes'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->public_notes ?: ''), $this->client) ?: '', 'label' => ctrans('texts.public_notes')];
@@ -379,16 +383,16 @@ class HtmlEngine
$data['$entity_issued_to'] = ['value' => '', 'label' => ctrans("texts.{$this->entity_string}_issued_to")];
$data['$your_entity'] = ['value' => '', 'label' => ctrans("texts.your_{$this->entity_string}")];
- $data['$quote.date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.quote_date')];
- $data['$quote.number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.quote_number')];
+ $data['$quote.date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.quote_date')];
+ $data['$quote.number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.quote_number')];
$data['$quote.po_number'] = &$data['$invoice.po_number'];
$data['$quote.quote_number'] = &$data['$quote.number'];
$data['$quote_no'] = &$data['$quote.number'];
$data['$quote.quote_no'] = &$data['$quote.number'];
$data['$quote.valid_until'] = ['value' => $this->translateDate($this->entity->due_date, $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.valid_until')];
$data['$valid_until'] = &$data['$quote.valid_until'];
- $data['$credit_amount'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_amount')];
- $data['$credit_balance'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
+ $data['$credit_amount'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_amount')];
+ $data['$credit_balance'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')];
$data['$credit_number'] = &$data['$number'];
@@ -397,21 +401,21 @@ class HtmlEngine
$data['$invoice_no'] = &$data['$number'];
$data['$invoice.invoice_no'] = &$data['$number'];
- $data['$client1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client1', $this->client->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client1')];
- $data['$client2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client2', $this->client->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client2')];
- $data['$client3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client3', $this->client->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client3')];
- $data['$client4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client4', $this->client->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client4')];
+ $data['$client1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client1', $this->client->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client1')];
+ $data['$client2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client2', $this->client->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client2')];
+ $data['$client3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client3', $this->client->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client3')];
+ $data['$client4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client4', $this->client->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client4')];
$data['$client.custom1'] = &$data['$client1'];
$data['$client.custom2'] = &$data['$client2'];
$data['$client.custom3'] = &$data['$client3'];
$data['$client.custom4'] = &$data['$client4'];
- $data['$address1'] = ['value' => $this->client->address1 ?: ' ', 'label' => ctrans('texts.address1')];
- $data['$address2'] = ['value' => $this->client->address2 ?: ' ', 'label' => ctrans('texts.address2')];
- $data['$id_number'] = ['value' => $this->client->id_number ?: ' ', 'label' => ctrans('texts.id_number')];
- $data['$client.number'] = ['value' => $this->client->number ?: ' ', 'label' => ctrans('texts.number')];
- $data['$vat_number'] = ['value' => $this->client->vat_number ?: ' ', 'label' => ctrans('texts.vat_number')];
- $data['$website'] = ['value' => $this->client->present()->website() ?: ' ', 'label' => ctrans('texts.website')];
- $data['$phone'] = ['value' => $this->client->present()->phone() ?: ' ', 'label' => ctrans('texts.phone')];
+ $data['$address1'] = ['value' => $this->client->address1 ?: ' ', 'label' => ctrans('texts.address1')];
+ $data['$address2'] = ['value' => $this->client->address2 ?: ' ', 'label' => ctrans('texts.address2')];
+ $data['$id_number'] = ['value' => $this->client->id_number ?: ' ', 'label' => ctrans('texts.id_number')];
+ $data['$client.number'] = ['value' => $this->client->number ?: ' ', 'label' => ctrans('texts.number')];
+ $data['$vat_number'] = ['value' => $this->client->vat_number ?: ' ', 'label' => ctrans('texts.vat_number')];
+ $data['$website'] = ['value' => $this->client->present()->website() ?: ' ', 'label' => ctrans('texts.website')];
+ $data['$phone'] = ['value' => $this->client->present()->phone() ?: ' ', 'label' => ctrans('texts.phone')];
$data['$country'] = ['value' => isset($this->client->country->name) ? ctrans('texts.country_' . $this->client->country->name) : '', 'label' => ctrans('texts.country')];
$data['$country_2'] = ['value' => isset($this->client->country) ? $this->client->country->iso_3166_2 : '', 'label' => ctrans('texts.country')];
$data['$email'] = ['value' => isset($this->contact) ? $this->contact->email : 'no contact email on record', 'label' => ctrans('texts.email')];
@@ -420,27 +424,27 @@ class HtmlEngine
$data['$email'] = ['value' => '', 'label' => ctrans('texts.email')];
}
- $data['$client_name'] = ['value' => $this->entity->present()->clientName() ?: ' ', 'label' => ctrans('texts.client_name')];
+ $data['$client_name'] = ['value' => $this->entity->present()->clientName() ?: ' ', 'label' => ctrans('texts.client_name')];
$data['$client.name'] = &$data['$client_name'];
$data['$client'] = &$data['$client_name'];
$data['$client.address1'] = &$data['$address1'];
$data['$client.address2'] = &$data['$address2'];
- $data['$client_address'] = ['value' => $this->client->present()->address() ?: ' ', 'label' => ctrans('texts.address')];
+ $data['$client_address'] = ['value' => $this->client->present()->address() ?: ' ', 'label' => ctrans('texts.address')];
$data['$client.address'] = &$data['$client_address'];
- $data['$client.postal_code'] = ['value' => $this->client->postal_code ?: ' ', 'label' => ctrans('texts.postal_code')];
- $data['$client.public_notes'] = ['value' => $this->client->public_notes ?: ' ', 'label' => ctrans('texts.notes')];
- $data['$client.city'] = ['value' => $this->client->city ?: ' ', 'label' => ctrans('texts.city')];
- $data['$client.state'] = ['value' => $this->client->state ?: ' ', 'label' => ctrans('texts.state')];
+ $data['$client.postal_code'] = ['value' => $this->client->postal_code ?: ' ', 'label' => ctrans('texts.postal_code')];
+ $data['$client.public_notes'] = ['value' => $this->client->public_notes ?: ' ', 'label' => ctrans('texts.notes')];
+ $data['$client.city'] = ['value' => $this->client->city ?: ' ', 'label' => ctrans('texts.city')];
+ $data['$client.state'] = ['value' => $this->client->state ?: ' ', 'label' => ctrans('texts.state')];
$data['$client.id_number'] = &$data['$id_number'];
$data['$client.vat_number'] = &$data['$vat_number'];
$data['$client.website'] = &$data['$website'];
$data['$client.phone'] = &$data['$phone'];
- $data['$city_state_postal'] = ['value' => $this->entity->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
+ $data['$city_state_postal'] = ['value' => $this->entity->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
$data['$client.city_state_postal'] = &$data['$city_state_postal'];
- $data['$postal_city_state'] = ['value' => $this->entity->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
+ $data['$postal_city_state'] = ['value' => $this->entity->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
$data['$client.postal_city_state'] = &$data['$postal_city_state'];
- $data['$postal_city'] = ['value' => $this->entity->present()->cityStateZip($this->client->city, null, $this->client->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city')];
+ $data['$postal_city'] = ['value' => $this->entity->present()->cityStateZip($this->client->city, null, $this->client->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city')];
$data['$client.postal_city'] = &$data['$postal_city'];
$data['$client.country'] = &$data['$country'];
$data['$client.email'] = &$data['$email'];
@@ -453,12 +457,12 @@ class HtmlEngine
$data['$client.billing_postal_code'] = &$data['$client.postal_code'];
$data['$client.billing_country'] = &$data['$client.country'];
- $data['$client.shipping_address'] = ['value' => $this->client->present()->shipping_address() ?: ' ', 'label' => ctrans('texts.shipping_address')];
- $data['$client.shipping_address1'] = ['value' => $this->client->shipping_address1 ?: ' ', 'label' => ctrans('texts.shipping_address1')];
- $data['$client.shipping_address2'] = ['value' => $this->client->shipping_address2 ?: ' ', 'label' => ctrans('texts.shipping_address2')];
- $data['$client.shipping_city'] = ['value' => $this->client->shipping_city ?: ' ', 'label' => ctrans('texts.shipping_city')];
- $data['$client.shipping_state'] = ['value' => $this->client->shipping_state ?: ' ', 'label' => ctrans('texts.shipping_state')];
- $data['$client.shipping_postal_code'] = ['value' => $this->client->shipping_postal_code ?: ' ', 'label' => ctrans('texts.shipping_postal_code')];
+ $data['$client.shipping_address'] = ['value' => $this->client->present()->shipping_address() ?: ' ', 'label' => ctrans('texts.shipping_address')];
+ $data['$client.shipping_address1'] = ['value' => $this->client->shipping_address1 ?: ' ', 'label' => ctrans('texts.shipping_address1')];
+ $data['$client.shipping_address2'] = ['value' => $this->client->shipping_address2 ?: ' ', 'label' => ctrans('texts.shipping_address2')];
+ $data['$client.shipping_city'] = ['value' => $this->client->shipping_city ?: ' ', 'label' => ctrans('texts.shipping_city')];
+ $data['$client.shipping_state'] = ['value' => $this->client->shipping_state ?: ' ', 'label' => ctrans('texts.shipping_state')];
+ $data['$client.shipping_postal_code'] = ['value' => $this->client->shipping_postal_code ?: ' ', 'label' => ctrans('texts.shipping_postal_code')];
$data['$client.shipping_country'] = ['value' => isset($this->client->shipping_country->name) ? ctrans('texts.country_' . $this->client->shipping_country->name) : '', 'label' => ctrans('texts.shipping_country')];
$data['$client.currency'] = ['value' => $this->client->currency()->code, 'label' => ''];
@@ -485,54 +489,54 @@ class HtmlEngine
$data['$contact.portal_button'] = &$data['$portal_button'];
$data['$portalButton'] = &$data['$portal_button'];
- $data['$contact.custom1'] = ['value' => isset($this->contact) ? $this->contact->custom_value1 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
- $data['$contact.custom2'] = ['value' => isset($this->contact) ? $this->contact->custom_value2 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact2')];
- $data['$contact.custom3'] = ['value' => isset($this->contact) ? $this->contact->custom_value3 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact3')];
- $data['$contact.custom4'] = ['value' => isset($this->contact) ? $this->contact->custom_value4 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact4')];
+ $data['$contact.custom1'] = ['value' => isset($this->contact) ? $this->contact->custom_value1 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
+ $data['$contact.custom2'] = ['value' => isset($this->contact) ? $this->contact->custom_value2 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact2')];
+ $data['$contact.custom3'] = ['value' => isset($this->contact) ? $this->contact->custom_value3 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact3')];
+ $data['$contact.custom4'] = ['value' => isset($this->contact) ? $this->contact->custom_value4 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact4')];
- $data['$company.city_state_postal'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
- $data['$company.postal_city_state'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
- $data['$company.postal_city'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, null, $this->settings->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city')];
+ $data['$company.city_state_postal'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
+ $data['$company.postal_city_state'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
+ $data['$company.postal_city'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, null, $this->settings->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city')];
$data['$company.name'] = ['value' => $this->settings->name ?: ctrans('texts.untitled_account'), 'label' => ctrans('texts.company_name')];
$data['$account'] = &$data['$company.name'];
- $data['$company.address1'] = ['value' => $this->settings->address1 ?: ' ', 'label' => ctrans('texts.address1')];
- $data['$company.address2'] = ['value' => $this->settings->address2 ?: ' ', 'label' => ctrans('texts.address2')];
- $data['$company.city'] = ['value' => $this->settings->city ?: ' ', 'label' => ctrans('texts.city')];
- $data['$company.state'] = ['value' => $this->settings->state ?: ' ', 'label' => ctrans('texts.state')];
- $data['$company.postal_code'] = ['value' => $this->settings->postal_code ?: ' ', 'label' => ctrans('texts.postal_code')];
+ $data['$company.address1'] = ['value' => $this->settings->address1 ?: ' ', 'label' => ctrans('texts.address1')];
+ $data['$company.address2'] = ['value' => $this->settings->address2 ?: ' ', 'label' => ctrans('texts.address2')];
+ $data['$company.city'] = ['value' => $this->settings->city ?: ' ', 'label' => ctrans('texts.city')];
+ $data['$company.state'] = ['value' => $this->settings->state ?: ' ', 'label' => ctrans('texts.state')];
+ $data['$company.postal_code'] = ['value' => $this->settings->postal_code ?: ' ', 'label' => ctrans('texts.postal_code')];
$data['$company.country'] = ['value' => $this->getCountryName(), 'label' => ctrans('texts.country')];
$data['$company.country_2'] = ['value' => $this->getCountryCode(), 'label' => ctrans('texts.country')];
- $data['$company.phone'] = ['value' => $this->settings->phone ?: ' ', 'label' => ctrans('texts.phone')];
- $data['$company.email'] = ['value' => $this->settings->email ?: ' ', 'label' => ctrans('texts.email')];
- $data['$company.vat_number'] = ['value' => $this->settings->vat_number ?: ' ', 'label' => ctrans('texts.vat_number')];
- $data['$company.id_number'] = ['value' => $this->settings->id_number ?: ' ', 'label' => ctrans('texts.id_number')];
- $data['$company.website'] = ['value' => $this->settings->website ?: ' ', 'label' => ctrans('texts.website')];
- $data['$company.address'] = ['value' => $this->company->present()->address($this->settings) ?: ' ', 'label' => ctrans('texts.address')];
+ $data['$company.phone'] = ['value' => $this->settings->phone ?: ' ', 'label' => ctrans('texts.phone')];
+ $data['$company.email'] = ['value' => $this->settings->email ?: ' ', 'label' => ctrans('texts.email')];
+ $data['$company.vat_number'] = ['value' => $this->settings->vat_number ?: ' ', 'label' => ctrans('texts.vat_number')];
+ $data['$company.id_number'] = ['value' => $this->settings->id_number ?: ' ', 'label' => ctrans('texts.id_number')];
+ $data['$company.website'] = ['value' => $this->settings->website ?: ' ', 'label' => ctrans('texts.website')];
+ $data['$company.address'] = ['value' => $this->company->present()->address($this->settings) ?: ' ', 'label' => ctrans('texts.address')];
- $data['$signature'] = ['value' => $this->settings->email_signature ?: ' ', 'label' => ''];
+ $data['$signature'] = ['value' => $this->settings->email_signature ?: ' ', 'label' => ''];
$data['$emailSignature'] = &$data['$signature'];
$data['$spc_qr_code'] = ['value' => $this->company->present()->getSpcQrCode($this->client->currency()->code, $this->entity->number, $this->entity->balance, $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client)), 'label' => ''];
$logo = $this->company->present()->logo_base64($this->settings);
- $data['$company.logo'] = ['value' => $logo ?: ' ', 'label' => ctrans('texts.logo')];
+ $data['$company.logo'] = ['value' => $logo ?: ' ', 'label' => ctrans('texts.logo')];
$data['$company_logo'] = &$data['$company.logo'];
- $data['$company1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')];
- $data['$company2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company2', $this->settings->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company2')];
- $data['$company3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company3', $this->settings->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company3')];
- $data['$company4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company4', $this->settings->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company4')];
+ $data['$company1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')];
+ $data['$company2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company2', $this->settings->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company2')];
+ $data['$company3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company3', $this->settings->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company3')];
+ $data['$company4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company4', $this->settings->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company4')];
$data['$company.custom1'] = &$data['$company1'];
$data['$company.custom2'] = &$data['$company2'];
$data['$company.custom3'] = &$data['$company3'];
$data['$company.custom4'] = &$data['$company4'];
- $data['$custom_surcharge1'] = ['value' => Number::formatMoney($this->entity->custom_surcharge1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge1')];
- $data['$custom_surcharge2'] = ['value' => Number::formatMoney($this->entity->custom_surcharge2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge2')];
- $data['$custom_surcharge3'] = ['value' => Number::formatMoney($this->entity->custom_surcharge3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge3')];
- $data['$custom_surcharge4'] = ['value' => Number::formatMoney($this->entity->custom_surcharge4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge4')];
+ $data['$custom_surcharge1'] = ['value' => Number::formatMoney($this->entity->custom_surcharge1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge1')];
+ $data['$custom_surcharge2'] = ['value' => Number::formatMoney($this->entity->custom_surcharge2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge2')];
+ $data['$custom_surcharge3'] = ['value' => Number::formatMoney($this->entity->custom_surcharge3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge3')];
+ $data['$custom_surcharge4'] = ['value' => Number::formatMoney($this->entity->custom_surcharge4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge4')];
$data['$product.item'] = ['value' => '', 'label' => ctrans('texts.item')];
$data['$product.date'] = ['value' => '', 'label' => ctrans('texts.date')];
@@ -584,13 +588,15 @@ class HtmlEngine
$data['$thanks'] = ['value' => '', 'label' => ctrans('texts.thanks')];
$data['$from'] = ['value' => '', 'label' => ctrans('texts.from')];
$data['$to'] = ['value' => '', 'label' => ctrans('texts.to')];
+ $data['$shipping'] = ['value' => '', 'label' => ctrans('texts.ship_to')];
+
$data['$details'] = ['value' => '', 'label' => ctrans('texts.details')];
$data['_rate1'] = ['value' => '', 'label' => ctrans('texts.tax')];
$data['_rate2'] = ['value' => '', 'label' => ctrans('texts.tax')];
$data['_rate3'] = ['value' => '', 'label' => ctrans('texts.tax')];
- $data['$font_size'] = ['value' => $this->settings->font_size . 'px', 'label' => ''];
+ $data['$font_size'] = ['value' => $this->settings->font_size . 'px !important;', 'label' => ''];
$data['$font_name'] = ['value' => Helpers::resolveFont($this->settings->primary_font)['name'], 'label' => ''];
$data['$font_url'] = ['value' => Helpers::resolveFont($this->settings->primary_font)['url'], 'label' => ''];
@@ -623,8 +629,8 @@ class HtmlEngine
$data['$dir'] = ['value' => in_array(optional($this->client->language())->locale, ['ar', 'he']) ? 'rtl' : 'ltr', 'label' => ''];
$data['$dir_text_align'] = ['value' => in_array(optional($this->client->language())->locale, ['ar', 'he']) ? 'right' : 'left', 'label' => ''];
- $data['$payment.date'] = ['value' => ' ', 'label' => ctrans('texts.payment_date')];
- $data['$method'] = ['value' => ' ', 'label' => ctrans('texts.method')];
+ $data['$payment.date'] = ['value' => ' ', 'label' => ctrans('texts.payment_date')];
+ $data['$method'] = ['value' => ' ', 'label' => ctrans('texts.method')];
$data['$statement_amount'] = ['value' => '', 'label' => ctrans('texts.amount')];
$data['$statement'] = ['value' => '', 'label' => ctrans('texts.statement')];
@@ -745,7 +751,7 @@ class HtmlEngine
return ctrans('texts.country_' . $country->name);
}
- return ' ';
+ return ' ';
}
@@ -760,7 +766,7 @@ class HtmlEngine
// return ctrans('texts.country_' . $country->iso_3166_2);
// }
- return ' ';
+ return ' ';
}
/**
* Due to the way we are compiling the blade template we
diff --git a/app/Utils/SystemHealth.php b/app/Utils/SystemHealth.php
index be4fce4fa454..55ff27f8d7b4 100644
--- a/app/Utils/SystemHealth.php
+++ b/app/Utils/SystemHealth.php
@@ -32,6 +32,7 @@ class SystemHealth
'mbstring',
'xml',
'bcmath',
+ 'iconv',
];
private static $php_version = 8.1;
diff --git a/config/ninja.php b/config/ninja.php
index e35ce9d2f44f..acc893f628c4 100644
--- a/config/ninja.php
+++ b/config/ninja.php
@@ -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.5.74',
- 'app_tag' => '5.5.74',
+ 'app_version' => '5.5.75',
+ 'app_tag' => '5.5.75',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),
diff --git a/database/factories/ClientFactory.php b/database/factories/ClientFactory.php
index 5535cca440fa..3b0463502f76 100644
--- a/database/factories/ClientFactory.php
+++ b/database/factories/ClientFactory.php
@@ -30,11 +30,11 @@ class ClientFactory extends Factory
'balance' => 0,
'paid_to_date' => 0,
'vat_number' => $this->faker->numberBetween(123456789, 987654321),
- 'id_number' => '',
- 'custom_value1' => '',
- 'custom_value2' => '',
- 'custom_value3' => '',
- 'custom_value4' => '',
+ 'id_number' => $this->faker->iban(),
+ 'custom_value1' => $this->faker->dateTime(),
+ 'custom_value2' => $this->faker->colorName(),
+ 'custom_value3' => $this->faker->word(),
+ 'custom_value4' => $this->faker->email(),
'address1' => $this->faker->buildingNumber(),
'address2' => $this->faker->streetAddress(),
'city' => $this->faker->city(),
diff --git a/database/factories/InvoiceFactory.php b/database/factories/InvoiceFactory.php
index 2236a0f104d2..0724a776b324 100644
--- a/database/factories/InvoiceFactory.php
+++ b/database/factories/InvoiceFactory.php
@@ -35,10 +35,10 @@ class InvoiceFactory extends Factory
'tax_rate2' => 17.5,
//'tax_name3' => 'THIRDTAX',
//'tax_rate3' => 5,
- // 'custom_value1' => $this->faker->date(),
- //'custom_value2' => rand(0, 1) ? 'yes' : 'no',
- // 'custom_value3' => $this->faker->numberBetween(1,4),
- // 'custom_value4' => $this->faker->numberBetween(1,4),
+ 'custom_value1' => $this->faker->date(),
+ 'custom_value2' => rand(0, 1) ? 'yes' : 'no',
+ 'custom_value3' => $this->faker->numberBetween(1,4),
+ 'custom_value4' => $this->faker->numberBetween(1,4),
'is_deleted' => false,
'po_number' => $this->faker->text(10),
'date' => $this->faker->date(),
diff --git a/database/factories/VendorContactFactory.php b/database/factories/VendorContactFactory.php
index 03b8556e8b74..9f26356f2213 100644
--- a/database/factories/VendorContactFactory.php
+++ b/database/factories/VendorContactFactory.php
@@ -26,7 +26,12 @@ class VendorContactFactory extends Factory
'first_name' => $this->faker->firstName(),
'last_name' => $this->faker->lastName(),
'phone' => $this->faker->phoneNumber(),
+ 'email_verified_at' => now(),
'email' => $this->faker->unique()->safeEmail(),
+ 'send_email' => true,
+ 'password' => bcrypt('password'),
+ 'remember_token' => \Illuminate\Support\Str::random(10),
+ 'contact_key' => \Illuminate\Support\Str::random(32),
];
}
}
diff --git a/lang/en/texts.php b/lang/en/texts.php
index fad5214c85cc..a3b1d7f30dd0 100644
--- a/lang/en/texts.php
+++ b/lang/en/texts.php
@@ -2189,7 +2189,6 @@ $LANG = array(
'logo_warning_too_large' => 'The image file is too large.',
'logo_warning_fileinfo' => 'Warning: To support gifs the fileinfo PHP extension needs to be enabled.',
'logo_warning_invalid' => 'There was a problem reading the image file, please try a different format.',
-
'error_refresh_page' => 'An error occurred, please refresh the page and try again.',
'data' => 'Data',
'imported_settings' => 'Successfully imported settings',
@@ -5002,6 +5001,13 @@ $LANG = array(
'gateway_payment_text_no_invoice' => 'Payment with no invoice for amount :amount for client :client',
'click_to_variables' => 'Client here to see all variables.',
'ship_to' => 'Ship to',
+ 'stripe_direct_debit_details' => 'Please transfer into the nominated bank account above.',
+ 'branch_name' => 'Branch Name',
+ 'branch_code' => 'Branch Code',
+ 'bank_name' => 'Bank Name',
+ 'bank_code' => 'Bank Code',
+ 'bic' => 'BIC',
+
);
diff --git a/resources/views/pdf-designs/bold.html b/resources/views/pdf-designs/bold.html
index 01d435cc1df8..e0c7021aad01 100644
--- a/resources/views/pdf-designs/bold.html
+++ b/resources/views/pdf-designs/bold.html
@@ -4,7 +4,7 @@
:root {
--primary-color: $primary_color;
--secondary-color: $secondary_color;
- --line-height: 1.6;
+ --line-height: 1.4;
}
body {
@@ -58,13 +58,13 @@
#header, #header-spacer {
height: 160px;
- padding: 3rem;
+ padding: 2rem;
margin-bottom: 1rem;
}
.company-logo {
- height: 100%;
- max-width: 100%;
- /* max-width: $company_logo_size;*/
+/* height: 100%;*/
+/* max-width: 100%;*/
+ max-width: $company_logo_size;
object-fit: contain;
object-position: left center;
}
@@ -82,10 +82,17 @@
}
#client-details {
- margin: 2rem;
+ padding-right:1rem;
display: flex;
flex-direction: column;
- line-height: var(--line-height);
+ line-height: var(--line-height) !important;
+ padding-left: 2rem;
+ }
+
+ #shipping-details {
+ display: $show_shipping_address;
+ flex-direction: column;
+ line-height: var(--line-height) !important;
}
#client-details > :first-child {
@@ -94,9 +101,13 @@
.client-entity-wrapper {
display: grid;
- grid-template-columns: 1.5fr 1fr;
+ grid-template-columns: 2fr 1fr;
padding-left: 1rem;
}
+
+ .client-wrapper-left-side {
+ display: flex;
+ }
.entity-details-wrapper {
background-color: var(--primary-color);
@@ -112,7 +123,7 @@
#entity-details > tr,
#entity-details th {
font-weight: normal;
- padding-bottom: 0.5rem;
+ line-height: var(--line-height) !important;
}
[data-ref="table"] {
@@ -325,6 +336,7 @@
opacity: 0.2;
z-index:200 !important;
position: fixed;
+ display: $show_paid_stamp;
}
.project-header {
@@ -393,14 +405,21 @@
@@ -419,7 +438,7 @@
-
+ $status_logo
|
diff --git a/resources/views/pdf-designs/business.html b/resources/views/pdf-designs/business.html
index 89263c580a47..a291ee4ff1b6 100644
--- a/resources/views/pdf-designs/business.html
+++ b/resources/views/pdf-designs/business.html
@@ -25,7 +25,6 @@
font-size: $font_size !important;
}
-
@page {
margin-left: $global_margin;
margin-right: $global_margin;
@@ -43,11 +42,12 @@
display: grid;
grid-template-columns: 1.8fr 1fr 1fr;
grid-gap: 20px;
+ margin-bottom: 2rem;
}
.company-logo {
- max-width: 65%;
- /* max-width: $company_logo_size;*/
+/* max-width: 65%;*/
+ max-width: $company_logo_size;
}
.header-container > span {
@@ -69,7 +69,7 @@
}
.entity-issued-to {
- margin-top: 2rem;
+ /* margin-top: 2rem; */
font-weight: bold;
}
@@ -77,17 +77,26 @@
display: flex;
justify-content: space-between;
gap: 20px;
+ margin-bottom: 2rem;
}
#client-details {
display: flex;
flex-direction: column;
- margin-top: 1rem;
line-height: var(--line-height);
+ vertical-align: top;
+ margin-left: 1rem;
}
- #client-details > p:nth-child(1) {
+ #client-details > p:nth-child(2) {
color: var(--primary-color);
+ font-size: 120%;
+ }
+
+ #shipping-details {
+ display: $show_shipping_address;
+ flex-direction: column;
+ line-height: var(--line-height);
}
#entity-details {
@@ -112,7 +121,7 @@
[data-ref="table"] {
margin-top: 0.5rem;
- margin-bottom: 50px;
+ margin-bottom: 5px;
min-width: 100%;
table-layout: fixed;
overflow-wrap: break-word;
@@ -174,14 +183,13 @@
}
#table-totals {
- margin-top: 0.5rem;
+ margin-top: 0rem;
display: grid;
grid-template-columns: 2fr 1fr;
gap: 80px;
- padding-left: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.8rem;
- margin-right: .5rem;
+ padding-left: 0.7rem;
page-break-inside:auto;
overflow: visible !important;
}
@@ -205,7 +213,7 @@
#table-totals>.totals-table-right-side>*> :nth-child(2) {
text-align: right;
- padding-right: 7px;
+ padding-right: 17px;
}
#table-totals
@@ -230,7 +238,7 @@
}
[data-ref="total_table-footer"] {
- padding-left: 1rem
+ padding-left: 0.8rem
}
#footer {
@@ -311,6 +319,7 @@
opacity: 0.2;
z-index:200 !important;
position: fixed;
+ display: $show_paid_stamp;
}
.project-header {
@@ -378,10 +387,10 @@
- $entity_issued_to_label:
-
+
+
@@ -395,7 +404,7 @@
-
+
$status_logo
diff --git a/resources/views/pdf-designs/calm.html b/resources/views/pdf-designs/calm.html
index 3797915b09cd..26f30e016587 100644
--- a/resources/views/pdf-designs/calm.html
+++ b/resources/views/pdf-designs/calm.html
@@ -39,22 +39,40 @@
.header-wrapper {
display: grid;
- grid-template-columns: 1fr 0.5fr;
- line-height: var(--line-height);
+ margin-top: 2rem;
+ gap: 20px;
+ grid-template-columns: 2fr 1fr 1fr;
+ grid-template-areas: "a b c";
+ grid-auto-columns: minmax(0, 5fr);
+ grid-auto-flow: column;
+ justify-content: left;
}
.header-wrapper2 {
display: grid;
- grid-template-columns: 1fr 0.5fr;
margin-top: 2rem;
- min-width: 100%;
+ gap: 20px;
+ grid-template-columns: 2fr 2fr auto;
+ grid-template-areas: "a b c";
+ grid-auto-columns: minmax(0, 5fr);
+ grid-auto-flow: column;
+ justify-content: left;
}
.company-logo {
- max-width: 65%;
- /* max-width: $company_logo_size;*/
+/* max-width: 65%;*/
+ max-width: $company_logo_size;
}
+ .logo-container {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .company-container {
+ display: flex;
+ flex-direction: column;
+ }
.client-and-entity-wrapper {
display: flex;
padding: 1rem;
@@ -68,18 +86,23 @@
line-height: var(--line-height);
}
+ .header-wrapper #company-details {
+ display: flex;
+ flex-direction: column;
+ line-height: var(--line-height);
+ }
+
.header-wrapper #entity-details {
- margin-top: 0.5rem;
+ padding-right: 0.5rem;
text-align: left;
+ line-height: var(--line-height);
width: 100%;
}
.header-wrapper #entity-details > tr,
.header-wrapper #entity-details th {
font-weight: normal;
- padding-left: 0.9rem;
- padding-top: 0.3rem;
- padding-bottom: 0.3rem;
+ white-space: nowrap;
}
.header-wrapper
@@ -91,12 +114,29 @@
background-color: #e6e6e6;
}
+ #entity-details {
+ text-align: left;
+ width: 100%;
+ }
+
+ #entity-details th {
+ font-weight:normal;
+ line-height: 1.5rem;
+ padding-right: 2rem;
+ }
+
#client-details {
display: flex;
flex-direction: column;
line-height: var(--line-height);
}
+ #shipping-details {
+ visibility: $show_shipping_address_visibility;
+ flex-direction: column;
+ line-height: var(--line-height);
+ }
+
[data-ref="table"] {
margin-top: 2rem;
min-width: 100%;
@@ -171,17 +211,6 @@
padding-right: 0px;
}
-
- #entity-details {
- text-align: left;
- width: 100%;
- }
-
- #entity-details th {
- font-weight:normal;
- line-height: 1.5rem;
- }
-
#table-totals
> *
[data-element='total-table-balance-due-label'],
@@ -270,6 +299,10 @@
[data-ref="total_table-public_notes"] { font-weight: normal; }
[data-ref="total_table-terms"] { font-weight: normal; }
+ /* [data-ref="shipping_address-label"] {
+ display: none;
+ } */
+
.stamp {
transform: rotate(12deg);
color: #555;
@@ -298,6 +331,7 @@
opacity: 0.2;
z-index:200 !important;
position: fixed;
+ display: $show_paid_stamp;
}
.project-header {
@@ -355,24 +389,22 @@
-
-
+
+
@@ -383,7 +415,7 @@
-
+ $status_logo
|
diff --git a/resources/views/pdf-designs/clean.html b/resources/views/pdf-designs/clean.html
index f20942b0cb69..0c197fe05efe 100644
--- a/resources/views/pdf-designs/clean.html
+++ b/resources/views/pdf-designs/clean.html
@@ -55,9 +55,12 @@
gap: 20px;
}
+ .company-logo-container {
+ display: inline-block;
+ }
+
.company-logo {
- max-width: 65%;
- /* max-width: $company_logo_size;*/
+ max-width: $company_logo_size;
}
#company-details {
@@ -86,35 +89,45 @@
}
.client-and-entity-wrapper {
- display: flex;
+/* display: flex;*/
padding: 1rem;
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
border-top: 1px solid #d8d8d8;
border-bottom: 1px solid #d8d8d8;
}
#entity-details {
+ display:flex;
text-align: left;
margin-right: 20px;
+ line-height: var(--line-height) !important;
}
#entity-details > tr,
#entity-details th {
font-weight: normal;
padding-right: 15px;
- padding-top: 2.5px;
- padding-bottom: 2.5px;
+ line-height: var(--line-height) !important;
}
#client-details {
display: flex;
flex-direction: column;
line-height: var(--line-height);
+ padding-right:30px;
}
#client-details > :first-child {
font-weight: bold;
}
+ #shipping-details {
+ display: $show_shipping_address;
+ flex-direction: column;
+ line-height: var(--line-height);
+ }
+
[data-ref="table"] {
margin-top: 1rem;
margin-bottom: 5px;
@@ -166,8 +179,9 @@
margin-top: 0rem;
display: grid;
grid-template-columns: 2fr 1fr;
- padding-top: .5rem;
+ padding-top: 0rem;
padding-right: 1rem;
+ padding-left: 1rem;
gap: 80px;
page-break-inside:avoid;
overflow: visible !important;
@@ -210,11 +224,12 @@
#table-totals > * > :last-child {
text-align: right;
- padding-right: 1rem;
+ padding-right: 0.5rem;
}
#footer {
margin-top: 10px;
+ margin-left: 1rem;
}
/** Markdown-specific styles. **/
@@ -225,12 +240,6 @@
margin-bottom: 0;
}
- .company-logo-container {
- display: flex;
- flex-direction: column;
- justify-content: flex-end;
- }
-
[data-ref="statement-totals"] {
margin-top: 1rem;
text-align: right;
@@ -277,6 +286,8 @@
z-index:200 !important;
position: fixed;
text-align: center;
+ float:right;
+
}
.is-paid {
@@ -290,6 +301,7 @@
opacity: 0.2;
z-index:200 !important;
position: fixed;
+ display: $show_paid_stamp;
}
.project-header {
@@ -359,10 +371,11 @@
$entity_label
@@ -373,7 +386,7 @@
-
+ $status_logo
@@ -402,7 +415,7 @@ $entity_images
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table',
- 'client-details','vendor-details', 'swiss-qr'
+ 'client-details','vendor-details', 'swiss-qr','shipping-details'
];
tables.forEach((tableIdentifier) => {
@@ -415,4 +428,4 @@ $entity_images
});
-
+
\ No newline at end of file
diff --git a/resources/views/pdf-designs/creative.html b/resources/views/pdf-designs/creative.html
index 880c31ed7433..ff267027f181 100644
--- a/resources/views/pdf-designs/creative.html
+++ b/resources/views/pdf-designs/creative.html
@@ -40,23 +40,43 @@
.header-wrapper {
display: grid;
- grid-template-columns: 1fr 1fr 1fr;
+ grid-template-rows:0.5fr;
+ grid-template-columns: auto auto auto auto;
+ grid-template-areas: "a b c d e";
+ grid-auto-columns: minmax(0, 1fr);
+ grid-auto-flow: column;
+ justify-content:left;
gap: 20px;
line-height: var(--line-height);
}
.company-logo {
- max-width: 65%;
- /* max-width: $company_logo_size;*/
+ max-width: $company_logo_size;
+ float:right;
}
- #entity-details p { margin-top: 5px; }
+ ,logo-wrapper {
+ grid-area: e;
+ align-content: right;
+ border:1px solid #000;
+ }
+
+ #entity-details {
+ width: 100%;
+ white-space: nowrap;
+ margin-right: 3rem;
+ }
+
+ #entity-details p {
+ margin-top: 5px;
+ }
.header-wrapper #client-details,
.header-wrapper #company-details,
.header-wrapper #company-address {
display: flex;
flex-direction: column;
+ line-height: var(--line-height) !important;
}
[data-ref="company_details-company.name"] {
@@ -70,11 +90,12 @@
.header-wrapper .company-info-wrapper > * {
margin-bottom: 1rem;
+ grid-row-end: 4;
}
.entity-label-wrapper {
display: grid;
- grid-template-columns: 2fr 1fr;
+ grid-template-columns: 3fr 1fr;
margin-top: 1rem;
}
@@ -95,13 +116,19 @@
text-align: left;
}
+ #shipping-details {
+ display: $show_shipping_address;
+ flex-direction: column;
+ line-height: var(--line-height) !important;
+ }
+
.entity-label-wrapper #entity-details > tr,
.entity-label-wrapper #entity-details th {
font-weight: normal;
}
[data-ref="table"] {
- margin-bottom: 50px;
+ margin-bottom: 5px;
min-width: 100%;
table-layout: fixed;
overflow-wrap: break-word;
@@ -182,7 +209,7 @@
}
#table-totals > * > * {
- padding-left: 1rem;
+ padding-left: 0.5rem;
}
#table-totals > * > :last-child {
@@ -191,7 +218,8 @@
}
[data-ref="total_table-footer"] {
- padding-left: 1rem
+ padding-left: 0.5rem;
+ padding-right:0.8rem;
}
#footer {
@@ -263,6 +291,8 @@
opacity: 0.2;
z-index:200 !important;
position: fixed;
+ display: $show_paid_stamp;
+
}
.project-header {
@@ -325,22 +355,28 @@
$entity_label
#$entity_number
-
+
@@ -351,7 +387,7 @@
-
+ $status_logo
diff --git a/resources/views/pdf-designs/elegant.html b/resources/views/pdf-designs/elegant.html
index 3eab4fe810f1..312719b34123 100644
--- a/resources/views/pdf-designs/elegant.html
+++ b/resources/views/pdf-designs/elegant.html
@@ -35,8 +35,8 @@
}
.company-logo {
- max-width: 55%;
- /* max-width: $company_logo_size;*/
+/* max-width: 55%;*/
+ max-width: $company_logo_size;
margin-left: auto;
margin-right: auto;
display: block;
@@ -55,31 +55,53 @@
display: flex;
margin-top: 1rem;
gap: 20px;
- margin-left: 10px;
- line-height: var(--line-height);
+ margin-left: 0px;
+ line-height: var(--line-height) !important;
}
- #entity-details p { margin-right: 20px; margin-top: 5px; }
-
+ #entity-details p {
+ margin-right: 0px;
+ margin-top: 0px;
+ white-space: nowrap;
+ line-height: var(--line-height) !important;
+ }
+
.client-entity-wrapper .wrapper-info-text {
display: block;
font-size: 1.5rem;
font-weight: normal;
}
+
+ .client-entity-wrapper .shipping-info-text {
+ display: block;
+ font-size: 1.5rem;
+ font-weight: normal;
+ display: $show_shipping_address;
+ }
.client-entity-wrapper .wrapper-left-side {
display: grid;
- grid-template-columns: 1fr 1fr;
+ grid-template-columns: auto auto ;
+ grid-template-areas: "a b c d";
+ grid-auto-columns: minmax(0, 1fr);
+ grid-auto-flow: column;
+ justify-content:left;
}
- .text-with-client { margin-right: 15px; }
+ .text-with-client { margin-right: 1px; }
.client-entity-wrapper .wrapper-left-side #client-details,
.client-entity-wrapper .wrapper-left-side #company-details,
.client-entity-wrapper .wrapper-left-side #company-address {
display: flex;
flex-direction: column;
- margin-top: 0.5rem;
+ margin-right: 5px;
+ }
+
+ .client-entity-wrapper .wrapper-left-side #shipping-details {
+ display: $show_shipping_address;
+ flex-direction: column;
+ margin-right: 5px;
}
.client-entity-wrapper .wrapper-left-side .company-info {
@@ -89,18 +111,21 @@
.client-entity-wrapper #entity-details {
text-align: left;
- margin-top: 0.5rem;
min-width: 100%;
+ line-height: var(--line-height) !important;
}
.client-entity-wrapper #entity-details > tr,
.client-entity-wrapper #entity-details th {
font-weight: normal;
+ padding-right:8px;
+ line-height: var(--line-height) !important;
}
[data-ref="table"] {
margin-top: 3rem;
- margin-bottom: 50px;
+ margin-bottom: 5
+ px;
min-width: 100%;
table-layout: fixed;
overflow-wrap: break-word;
@@ -126,6 +151,7 @@
[data-ref="table"] > thead > tr > th:last-child {
text-align: right;
+ padding-right: 1rem;
}
[data-ref="table"] > tbody > tr > td {
@@ -140,6 +166,11 @@
[data-ref="table"] > tbody > tr > td:last-child {
text-align: right;
+ padding-right: 1rem;
+ }
+
+ [data-ref="shipping_address-label"] {
+ display: none;
}
#table-totals {
@@ -267,6 +298,8 @@
opacity: 0.2;
z-index:200 !important;
position: fixed;
+ display: $show_paid_stamp;
+
}
.project-header {
@@ -329,23 +362,27 @@
-
+
@@ -356,7 +393,7 @@
-
+
$status_logo
diff --git a/resources/views/pdf-designs/hipster.html b/resources/views/pdf-designs/hipster.html
index 20579f679dab..7d3ae9964ebf 100644
--- a/resources/views/pdf-designs/hipster.html
+++ b/resources/views/pdf-designs/hipster.html
@@ -36,7 +36,7 @@
.header-wrapper {
display: grid;
- grid-template-columns: 1.2fr 1.8fr;
+ grid-template-columns: 0.5fr 1.5fr;
gap: 20px;
line-height: var(--line-height);
}
@@ -67,10 +67,17 @@
.header-wrapper .header-right-side-wrapper {
display: grid;
- grid-template-columns: 1fr 1fr;
+ /* grid-template-columns: 1fr 1fr; */
gap: 10px;
border-left: 1px solid #303030;
padding-left: 1rem;
+
+ grid-template-columns: auto auto auto;
+ grid-template-areas: "a b c";
+ grid-auto-columns: minmax(0, 1fr);
+ grid-auto-flow: column;
+ justify-content:left;
+
}
.header-wrapper .header-right-side-wrapper #client-details {
@@ -79,13 +86,26 @@
margin-top: 0.8rem;
}
+ .header-wrapper .header-right-side-wrapper #shipping-details {
+ display: $show_shipping_address;
+ flex-direction: column;
+ margin-top: 0.8rem;
+ }
+
+ .shipping-text-label {
+ font-size: 1.1rem;
+ color: var(--primary-color);
+ text-transform: uppercase;
+ font-weight: bold;
+ display: $show_shipping_address;
+ }
+
.header-wrapper {
margin-left: auto;
}
.company-logo {
- max-width: 65%;
- /* max-width: $company_logo_size;*/
+ max-width: $company_logo_size;
}
.entity-label {
@@ -96,6 +116,14 @@
.entity-details-wrapper > * {
margin-right: 1.5rem;
+ direction: $dir;
+ }
+
+ .entity-details-wrapper {
+ display: flex;
+ flex-wrap: wrap;
+ direction: $dir;
+ justify-content: space-between;
}
.entity-details-wrapper .entity-property-label {
@@ -110,9 +138,13 @@
font-weight: bold;
}
+ [data-ref="shipping_address-label"] {
+ display: none;
+ }
+
[data-ref="table"] {
margin-top: 1rem;
- margin-bottom: 50px;
+ margin-bottom: 5px;
min-width: 100%;
table-layout: fixed;
overflow-wrap: break-word;
@@ -139,6 +171,12 @@
[data-ref="table"] > thead > tr > th:last-child {
text-align: right;
+ padding-right: 1rem;
+ }
+
+ [data-ref="table"] > thead > tr > th:nth-last-child(2) {
+ text-align: right;
+ padding-right: 1rem;
}
[data-ref="table"] > tbody > tr > td {
@@ -155,7 +193,7 @@
display: grid;
grid-template-columns: 2fr 1fr;
padding-top: 0.5rem;
- margin-right: .75rem;
+ margin-right: 1rem;
gap: 80px;
page-break-inside:auto;
overflow: visible !important;
@@ -211,16 +249,6 @@
margin-bottom: 0;
}
- .entity-details-wrapper > * {
- direction: $dir;
- }
-
- .entity-details-wrapper {
- display: flex;
- flex-wrap: wrap;
- direction: $dir;
- }
-
[data-ref="product_table-product.unit_cost-td"] { text-align: right; }
[data-ref="totals_table-outstanding"] { color: var(--primary-color); }
@@ -284,6 +312,7 @@
opacity: 0.2;
z-index:200 !important;
position: fixed;
+ display: $show_paid_stamp;
}
.project-header {
@@ -354,6 +383,10 @@
+
-
+ $status_logo
diff --git a/resources/views/pdf-designs/modern.html b/resources/views/pdf-designs/modern.html
index a1d5f93f50f8..011f307f872a 100644
--- a/resources/views/pdf-designs/modern.html
+++ b/resources/views/pdf-designs/modern.html
@@ -64,6 +64,7 @@
.company-name {
text-align: left;
+ margin-left: 1rem;
}
#header .company-name {
@@ -73,28 +74,39 @@
#entity-details {
text-align: left;
color: #fff4e9 !important;
+ line-height: 1.6;
}
#entity-details > tr,
#entity-details th {
font-weight: normal;
- padding-bottom: 0.5rem;
}
.logo-client-wrapper {
- margin: 0 2rem 0rem;
+ margin-left: 2rem;
display: grid;
- grid-template-columns: 1.5fr 1fr;
+ grid-template-columns: 1fr auto auto;
+ margin-top: 1rem;
+ margin-bottom: 1rem;
}
.company-logo {
- max-width: 55%;
- /* max-width: $company_logo_size;*/
+ max-width: $company_logo_size;
}
#client-details {
display: flex;
flex-direction: column;
+ margin-left: 10px;
+ margin-right: 10px;
+
+ }
+
+ #shipping-details {
+ display: $show_shipping_address;
+ flex-direction: column;
+ margin-left: 10px;
+ margin-right: 10px;
}
#client-details > * {
@@ -138,7 +150,7 @@
[data-ref="table"] > tbody > tr > td {
border-bottom: 1px solid var(--secondary-color);
- padding: 1rem;
+ padding: 0.8rem;
}
[data-ref="table"] > tbody > tr > td:first-child {
@@ -154,6 +166,7 @@
width: 100%;
position: fixed;
bottom: 0;
+ padding-left: 1rem;
}
@@ -202,7 +215,7 @@
display: grid;
grid-template-columns: 2fr 1fr;
padding-top: 0.5rem;
- padding-left: 3rem;
+ padding-left: 2.5rem;
padding-right: 3rem;
gap: 80px;
}
@@ -311,6 +324,8 @@
opacity: 0.2;
z-index:200 !important;
position: fixed;
+ display: $show_paid_stamp;
+
}
.project-header {
@@ -358,7 +373,7 @@
@@ -376,6 +391,7 @@
+
-
+ $status_logo
@@ -411,7 +427,7 @@ $entity_images