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/Http/Controllers/PreviewController.php b/app/Http/Controllers/PreviewController.php index af91880a926c..8b95f7113566 100644 --- a/app/Http/Controllers/PreviewController.php +++ b/app/Http/Controllers/PreviewController.php @@ -36,6 +36,7 @@ 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; @@ -176,6 +177,23 @@ class PreviewController extends BaseController } public function design(DesignPreviewRequest $request) + { + if (Ninja::isHosted() && !in_array($request->getHost(), ['preview.invoicing.co','staging.invoicing.co'])) { + return response()->json(['message' => 'This server cannot handle this request.'], 400); + } + + $stub = new StubBuilder(auth()->user()->company(), auth()->user()); + $stub->setEntityType($request->entity_type) + $pdf = $stub->build()->getPdf(); + + $response = Response::make($pdf, 200); + $response->header('Content-Type', 'application/pdf'); + + return $response; + + } + + public function oldDesign(DesignPreviewRequest $request) { if(Ninja::isHosted() && !in_array($request->getHost(), ['preview.invoicing.co','staging.invoicing.co'])){ return response()->json(['message' => 'This server cannot handle this request.'], 400); 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/Services/Preview/StubBuilder.php b/app/Services/Preview/StubBuilder.php new file mode 100644 index 000000000000..e7bb106bb54a --- /dev/null +++ b/app/Services/Preview/StubBuilder.php @@ -0,0 +1,239 @@ +entity_type = $entity_type; + + return $this; + } + + public function build(): self + { + + try{ + DB::connection($this->company->db)->beginTransaction(); + + $this->createRecipient() + ->createEntity() + ->linkRelations() + ->buildHtml(); + + DB::connection($this->company->db)->rollBack(); + } + catch(\Exception $e) + { + return $e->getMessage(); + + DB::connection($this->company->db)->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 buildHtml(): self + { + + $html = new HtmlEngine($this->invitation); + + $design = new Design(Design::CUSTOM, ['custom_partials' => request()->design['design']]); + + $state = [ + 'template' => $design->elements([ + 'client' => $this->recipient, + 'entity' => $this->entity, + 'pdf_variables' => (array) $this->company->settings->pdf_variables, + 'products' => request()->design['design']['product'], + ]), + 'variables' => $html->generateLabelsAndValues(), + 'process_markdown' => $this->company->markdown_enabled, + ]; + + $maker = new PdfMaker($state); + + $this->html = $maker->design($design) + ->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, + ]); + + $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/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(),