1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-21 00:41:34 +02:00

Merge pull request #4591 from turbo124/v5-stable

V5.0.41
This commit is contained in:
David Bomba 2020-12-30 06:09:20 +11:00 committed by GitHub
commit 66f23abea1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 57115 additions and 56833 deletions

View File

@ -1 +1 @@
5.0.40
5.0.41

View File

@ -0,0 +1,55 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Console\Commands;
use Illuminate\Console\Command;
class GenerateSetupKey extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:generate-setup-key';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate random APP_KEY value';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$randomString = base64_encode(\Illuminate\Support\Str::random(32));
$this->info('Success! Copy the following content into your .env or docker-compose.yml:');
$this->warn('base64:' . $randomString);
}
}

View File

@ -35,6 +35,8 @@ class UserWasRestored
public $event_vars;
public $fromDeleted;
/**
* Create a new event instance.
*
@ -42,11 +44,12 @@ class UserWasRestored
* @param Company $company
* @param array $event_vars
*/
public function __construct(User $user, Company $company, array $event_vars)
public function __construct(User $user, bool $fromDeleted, Company $company, array $event_vars)
{
$this->user = $user;
$this->company = $company;
$this->event_vars = $event_vars;
$this->fromDeleted = $fromDeleted;
}
/**

View File

@ -1,4 +1,5 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

31
app/Helpers/Generic.php Normal file
View File

@ -0,0 +1,31 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
/**
* Simple helper function that will log into "invoiceninja.log" file
* only when extended logging is enabled.
*
* @param mixed $output
* @param array $context
*
* @return void
*/
function nlog($output, $context = []): void
{
if (config('ninja.expanded_logging')) {
if (gettype($output) == 'object') {
$output = print_r($output, 1);
}
\Illuminate\Support\Facades\Log::channel('invoiceninja')->info($output, $context);
}
}

View File

@ -65,8 +65,8 @@ class BaseController extends Controller
'company.task_statuses',
'company.expense_categories',
'company.documents',
'company.users',
//'company.users.company_user',
//'company.users',
'company.users.company_user',
'company.clients.contacts.company',
'company.clients.gateway_tokens',
'company.clients.documents',

View File

@ -125,7 +125,7 @@ class PreviewController extends BaseController
->design($design)
->build();
if (request()->query('html') == true) {
if (request()->query('html') == 'true') {
return $maker->getCompiledHTML;
}
@ -209,6 +209,10 @@ class PreviewController extends BaseController
->design($design)
->build();
if (request()->query('html') == 'true') {
return $maker->getCompiledHTML();
}
if (config('ninja.phantomjs_pdf_generation')) {
return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
}

View File

@ -221,15 +221,19 @@ class SetupController extends Controller
return $this->testPhantom();
}
$snappdf = new Snappdf();
$pdf = new Snappdf();
$pdf = $snappdf
if (config('ninja.snappdf_chromium_path')) {
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
}
$pdf = $pdf
->setHtml('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!')
->generate();
Storage::put('public/test.pdf', $pdf);
return response(['url' => asset('storage/test.pdf')], 200);
return response(['url' => asset('test.pdf')], 200);
} catch (Exception $e) {
info($e->getMessage());

View File

@ -98,7 +98,7 @@ class CSVImport implements ShouldQueue
//sort the array by key
ksort($this->column_map);
info("import".ucfirst($this->entity_type));
nlog("import".ucfirst($this->entity_type));
$this->{"import".ucfirst($this->entity_type)}();
$data = [
@ -110,7 +110,7 @@ class CSVImport implements ShouldQueue
'settings' => $this->company->settings
];
info(print_r($data, 1));
//info(print_r($data, 1));
MailRouter::dispatch(new ImportCompleted($data), $this->company, auth()->user());
}

View File

@ -110,6 +110,8 @@ class Import implements ShouldQueue
'payment_terms',
'tax_rates',
'clients',
'company_gateways',
'client_gateway_tokens',
'vendors',
'projects',
'products',
@ -118,8 +120,6 @@ class Import implements ShouldQueue
'recurring_invoices',
'quotes',
'payments',
'company_gateways',
'client_gateway_tokens',
'expense_categories',
'task_statuses',
'expenses',
@ -459,6 +459,24 @@ class Import implements ShouldQueue
$saveable_contacts['contacts'] = $modified_contacts;
$contact_repository->save($saveable_contacts, $client);
//link contact ids
$client->fresh();
$new_contacts = $client->contacts;
foreach($resource['contacts'] as $key => $old_contact)
{
$contact_match = $new_contacts->where('contact_key', $old_contact['contact_key'])->first();
if($contact_match)
{
$this->ids['client_contacts']['client_contacts_'.$old_contact['id']] = [
'old' => $old_contact['id'],
'new' => $contact_match->id,
];
}
}
}
$key = "clients_{$resource['id']}";
@ -611,6 +629,16 @@ class Import implements ShouldQueue
unset($modified['id']);
foreach($resource['invitations'] as $key => $invite)
{
$resource['invitations'][$key]['client_contact_id'] = $this->transformId('client_contacts', $invite['client_contact_id']);
$resource['invitations'][$key]['user_id'] = $modified['user_id'];
$resource['invitations'][$key]['company_id'] = $this->company->id;
unset($resource['invitations'][$key]['recurring_invoice_id']);
}
$invoice = $invoice_repository->save(
$modified,
RecurringInvoiceFactory::create($this->company->id, $modified['user_id'])
@ -660,6 +688,18 @@ class Import implements ShouldQueue
$modified['line_items'] = $this->cleanItems($modified['line_items']);
unset($modified['id']);
foreach($resource['invitations'] as $key => $invite)
{
$resource['invitations'][$key]['client_contact_id'] = $this->transformId('client_contacts', $invite['client_contact_id']);
$resource['invitations'][$key]['user_id'] = $modified['user_id'];
$resource['invitations'][$key]['company_id'] = $this->company->id;
unset($resource['invitations'][$key]['invoice_id']);
nlog("find a match for " . $invite['client_contact_id'] . " " .$resource['invitations'][$key]['client_contact_id']);
}
$modified['invitations'] = $resource['invitations'];
$invoice = $invoice_repository->save(
$modified,
@ -830,6 +870,12 @@ class Import implements ShouldQueue
PaymentFactory::create($this->company->id, $modified['user_id'])
);
if($resource['company_gateway_id'] != 'NULL' && $resource['company_gateway_id'] != NULL){
$payment->company_gateway_id = $this->transformId('company_gateways', $resource['company_gateway_id']);
$payment->save();
}
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$this->ids['payments'] = [
@ -1026,13 +1072,11 @@ class Import implements ShouldQueue
$company_gateway = CompanyGateway::create($modified);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$key = "company_gateways_{$resource['id']}";
$this->ids['company_gateways'] = [
"company_gateways_{$old_user_key}" => [
$this->ids['company_gateways'][$key] = [
'old' => $resource['id'],
'new' => $company_gateway->id,
],
];
}

View File

@ -12,6 +12,7 @@
namespace App\Mail\Engine;
use App\DataMapper\EmailTemplateDefaults;
use App\Utils\Helpers;
use App\Utils\Number;
use App\Utils\Traits\MakesDates;
@ -30,6 +31,8 @@ class PaymentEmailEngine extends BaseEmailEngine
public $company;
public $contact;
private $helpers;
public function __construct($payment, $contact, $template_data = null)
{
@ -39,6 +42,7 @@ class PaymentEmailEngine extends BaseEmailEngine
$this->contact = $contact ?: $this->client->primary_contact()->first();
$this->settings = $this->client->getMergedSettings();
$this->template_data = $template_data;
$this->helpers = new Helpers();
}
public function build()
@ -106,16 +110,16 @@ class PaymentEmailEngine extends BaseEmailEngine
$data['$transaction_reference'] = ['value' => $this->payment->transaction_reference, 'label' => ctrans('texts.transaction_reference')];
$data['$public_notes'] = ['value' => $this->payment->public_notes, 'label' => ctrans('texts.notes')];
$data['$payment1'] = ['value' => $this->formatCustomFieldValue('payment1', $this->payment->custom_value1) ?: '&nbsp;', 'label' => $this->makeCustomField('payment1')];
$data['$payment2'] = ['value' => $this->formatCustomFieldValue('payment2', $this->payment->custom_value2) ?: '&nbsp;', 'label' => $this->makeCustomField('payment2')];
$data['$payment3'] = ['value' => $this->formatCustomFieldValue('payment3', $this->payment->custom_value3) ?: '&nbsp;', 'label' => $this->makeCustomField('payment3')];
$data['$payment4'] = ['value' => $this->formatCustomFieldValue('payment4', $this->payment->custom_value4) ?: '&nbsp;', 'label' => $this->makeCustomField('payment4')];
$data['$payment1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'payment1', $this->payment->custom_value1, $this->client) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'payment1')];
$data['$payment2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'payment2', $this->payment->custom_value2, $this->client) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'payment2')];
$data['$payment3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'payment3', $this->payment->custom_value3, $this->client) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'payment3')];
$data['$payment4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'payment4', $this->payment->custom_value4, $this->client) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'payment4')];
// $data['$type'] = ['value' => $this->payment->type->name ?: '', 'label' => ctrans('texts.payment_type')];
$data['$client1'] = ['value' => $this->formatCustomFieldValue('client1', $this->client->custom_value1) ?: '&nbsp;', 'label' => $this->makeCustomField('client1')];
$data['$client2'] = ['value' => $this->formatCustomFieldValue('client2', $this->client->custom_value2) ?: '&nbsp;', 'label' => $this->makeCustomField('client2')];
$data['$client3'] = ['value' => $this->formatCustomFieldValue('client3', $this->client->custom_value3) ?: '&nbsp;', 'label' => $this->makeCustomField('client3')];
$data['$client4'] = ['value' => $this->formatCustomFieldValue('client4', $this->client->custom_value4) ?: '&nbsp;', 'label' => $this->makeCustomField('client4')];
$data['$client1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client1', $this->client->custom_value1, $this->client) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client4')];
$data['$address1'] = ['value' => $this->client->address1 ?: '&nbsp;', 'label' => ctrans('texts.address1')];
$data['$address2'] = ['value' => $this->client->address2 ?: '&nbsp;', 'label' => ctrans('texts.address2')];
$data['$id_number'] = ['value' => $this->client->id_number ?: '&nbsp;', 'label' => ctrans('texts.id_number')];
@ -153,10 +157,10 @@ class PaymentEmailEngine extends BaseEmailEngine
$data['$contact.name'] = ['value' => isset($this->contact) ? $this->contact->present()->name() : 'no contact name on record', 'label' => ctrans('texts.contact_name')];
$data['$contact.first_name'] = ['value' => isset($this->contact) ? $this->contact->first_name : '', 'label' => ctrans('texts.first_name')];
$data['$contact.last_name'] = ['value' => isset($this->contact) ? $this->contact->last_name : '', 'label' => ctrans('texts.last_name')];
$data['$contact.custom1'] = ['value' => isset($this->contact) ? $this->contact->custom_value1 : '&nbsp;', 'label' => $this->makeCustomField('contact1')];
$data['$contact.custom2'] = ['value' => isset($this->contact) ? $this->contact->custom_value2 : '&nbsp;', 'label' => $this->makeCustomField('contact1')];
$data['$contact.custom3'] = ['value' => isset($this->contact) ? $this->contact->custom_value3 : '&nbsp;', 'label' => $this->makeCustomField('contact1')];
$data['$contact.custom4'] = ['value' => isset($this->contact) ? $this->contact->custom_value4 : '&nbsp;', 'label' => $this->makeCustomField('contact1')];
$data['$contact.custom1'] = ['value' => isset($this->contact) ? $this->contact->custom_value1 : '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
$data['$contact.custom2'] = ['value' => isset($this->contact) ? $this->contact->custom_value2 : '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
$data['$contact.custom3'] = ['value' => isset($this->contact) ? $this->contact->custom_value3 : '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
$data['$contact.custom4'] = ['value' => isset($this->contact) ? $this->contact->custom_value4 : '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
$data['$company.city_state_postal'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, false) ?: '&nbsp;', '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) ?: '&nbsp;', 'label' => ctrans('texts.postal_city_state')];
@ -178,10 +182,10 @@ class PaymentEmailEngine extends BaseEmailEngine
$data['$company.logo'] = ['value' => $logo ?: '&nbsp;', 'label' => ctrans('texts.logo')];
$data['$company_logo'] = &$data['$company.logo'];
$data['$company1'] = ['value' => $this->formatCustomFieldValue('company1', $this->settings->custom_value1) ?: '&nbsp;', 'label' => $this->makeCustomField('company1')];
$data['$company2'] = ['value' => $this->formatCustomFieldValue('company2', $this->settings->custom_value2) ?: '&nbsp;', 'label' => $this->makeCustomField('company2')];
$data['$company3'] = ['value' => $this->formatCustomFieldValue('company3', $this->settings->custom_value3) ?: '&nbsp;', 'label' => $this->makeCustomField('company3')];
$data['$company4'] = ['value' => $this->formatCustomFieldValue('company4', $this->settings->custom_value4) ?: '&nbsp;', 'label' => $this->makeCustomField('company4')];
$data['$company1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company4')];
$data['$view_link'] = ['value' => '<a href="'.$this->payment->getLink().'">'.ctrans('texts.view_payment').'</a>', 'label' => ctrans('texts.view_payment')];
$data['$view_url'] = ['value' => $this->payment->getLink(), 'label' => ctrans('texts.view_payment')];
@ -202,46 +206,6 @@ class PaymentEmailEngine extends BaseEmailEngine
return $invoice_list;
}
private function makeCustomField($field) :string
{
$custom_fields = $this->company->custom_fields;
if ($custom_fields && property_exists($custom_fields, $field)) {
$custom_field = $custom_fields->{$field};
$custom_field_parts = explode('|', $custom_field);
return $custom_field_parts[0];
}
return '';
}
private function formatCustomFieldValue($field, $value) :string
{
$custom_fields = $this->company->custom_fields;
$custom_field = '';
if ($custom_fields && property_exists($custom_fields, $field)) {
$custom_field = $custom_fields->{$field};
$custom_field_parts = explode('|', $custom_field);
if (count($custom_field_parts) >= 2) {
$custom_field = $custom_field_parts[1];
}
}
switch ($custom_field) {
case 'date':
return $this->formatDate($value, $this->client->date_format());
break;
default:
return is_null($value) ? '' : $value;
break;
}
}
public function makeValues() :array
{
$data = [];

View File

@ -191,6 +191,11 @@ class User extends Authenticatable implements MustVerifyEmail
return $this->hasMany(CompanyUser::class)->withTrashed();
}
public function co_user()
{
return $this->company_user();
}
public function company_user()
{
if (! $this->id && auth()->user()) {

View File

@ -94,6 +94,7 @@ use App\Listeners\Activity\ExpenseArchivedActivity;
use App\Listeners\Activity\ExpenseDeletedActivity;
use App\Listeners\Activity\ExpenseRestoredActivity;
use App\Listeners\Activity\ExpenseUpdatedActivity;
use App\Listeners\Activity\PaymentArchivedActivity;
use App\Listeners\Activity\PaymentCreatedActivity;
use App\Listeners\Activity\PaymentDeletedActivity;
use App\Listeners\Activity\PaymentRefundedActivity;
@ -122,8 +123,8 @@ use App\Listeners\Invoice\InvoiceArchivedActivity;
use App\Listeners\Invoice\InvoiceCancelledActivity;
use App\Listeners\Invoice\InvoiceDeletedActivity;
use App\Listeners\Invoice\InvoiceEmailActivity;
use App\Listeners\Invoice\InvoiceEmailedNotification;
use App\Listeners\Invoice\InvoiceEmailFailedActivity;
use App\Listeners\Invoice\InvoiceEmailedNotification;
use App\Listeners\Invoice\InvoicePaidActivity;
use App\Listeners\Invoice\InvoiceReminderEmailActivity;
use App\Listeners\Invoice\InvoiceRestoredActivity;
@ -131,8 +132,8 @@ use App\Listeners\Invoice\InvoiceReversedActivity;
use App\Listeners\Invoice\InvoiceViewedActivity;
use App\Listeners\Invoice\UpdateInvoiceActivity;
use App\Listeners\Misc\InvitationViewedListener;
use App\Listeners\Payment\PaymentEmailedActivity;
use App\Listeners\Payment\PaymentEmailFailureActivity;
use App\Listeners\Payment\PaymentEmailedActivity;
use App\Listeners\Payment\PaymentNotification;
use App\Listeners\Payment\PaymentRestoredActivity;
use App\Listeners\Quote\QuoteApprovedActivity;

View File

@ -59,8 +59,8 @@ class ClientRepository extends BaseRepository
}
$client->fill($data);
if (!isset($client->id_number)) {
if (!isset($client->id_number) || empty($client->id_number)) {
$client->id_number = $this->getNextClientNumber($client);
}

View File

@ -16,8 +16,10 @@ use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\Quote;
use App\Models\RecurringInvoice;
use App\Models\RecurringInvoiceInvitation;
use App\Repositories\BaseRepository;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
@ -86,10 +88,25 @@ class InvoiceMigrationRepository extends BaseRepository
}
}
InvoiceInvitation::unguard();
RecurringInvoiceInvitation::unguard();
foreach($data['invitations'] as $invitation)
{
nlog($invitation);
$new_invitation = $invitation_factory_class::create($model->company_id, $model->user_id);
$new_invitation->{$lcfirst_resource_id} = $model->id;
$new_invitation->fill($invitation);
$new_invitation->save();
}
InvoiceInvitation::reguard();
RecurringInvoiceInvitation::reguard();
/*
if (isset($data['invitations'])) {
$invitations = collect($data['invitations']);
/* Get array of Keys which have been removed from the invitations array and soft delete each invitation */
$model->invitations->pluck('key')->diff($invitations->pluck('key'))->each(function ($invitation) use ($resource) {
$this->getInvitation($invitation, $resource)->delete();
});
@ -114,7 +131,7 @@ class InvoiceMigrationRepository extends BaseRepository
}
}
}
*/
$model->load('invitations');
/* If no invitations have been created, this is our fail safe to maintain state*/

View File

@ -310,6 +310,10 @@ class Design extends BaseDesign
foreach ($this->context['pdf_variables']["{$type}_columns"] as $column) {
if (array_key_exists($column, $aliases)) {
$elements[] = ['element' => 'th', 'content' => $aliases[$column] . '_label'];
} elseif ($column == '$product.discount' && !$this->client->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->client->company->enable_product_quantity) {
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
} else {
$elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th']];
}
@ -384,6 +388,10 @@ class Design extends BaseDesign
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->client->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->client->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 == '$task.description') {

View File

@ -211,7 +211,12 @@ class CompanyTransformer extends EntityTransformer
{
$transformer = new UserTransformer($this->serializer);
return $this->includeCollection($company->users, $transformer, User::class);
$users = $company->users->map(function ($user) use ($company){
$user->company_id = $company->id;
return $user;
});
return $this->includeCollection($users, $transformer, User::class);
}
public function includeCompanyGateways(Company $company)
@ -346,4 +351,4 @@ class CompanyTransformer extends EntityTransformer
return $this->includeCollection($company->system_logs, $transformer, SystemLog::class);
}
}
}

View File

@ -40,16 +40,16 @@ class Helpers
}
/**
* A centralised way to format the custom fields content.
* A centralised method to format the custom fields content.
*
* @param mixed $custom_fields
* @param mixed|null $custom_fields
* @param mixed $field
* @param mixed $value
* @param null|\App\Models\Client $client
* @param \App\Models\Client|null $client
*
* @return null|string
*/
public function formatCustomFieldValue($custom_fields, $field, $value, ?Client $client): ?string
public function formatCustomFieldValue($custom_fields = null, $field, $value, Client $client = null): ?string
{
$custom_field = '';
@ -64,7 +64,7 @@ class Helpers
switch ($custom_field) {
case 'date':
return $this->formatDate($value, $client->date_format());
return is_null($client) ? $value : $this->formatDate($value, $client->date_format());
break;
case 'switch':
@ -76,4 +76,24 @@ class Helpers
break;
}
}
/**
* A centralised method to make custom field.
* @param mixed|null $custom_fields
* @param mixed $field
*
* @return string
*/
public function makeCustomField($custom_fields = null, $field): string
{
if ($custom_fields && property_exists($custom_fields, $field)) {
$custom_field = $custom_fields->{$field};
$custom_field_parts = explode('|', $custom_field);
return $custom_field_parts[0];
}
return '';
}
}

View File

@ -41,6 +41,8 @@ class HtmlEngine
public $entity_string;
private $helpers;
public function __construct($invitation)
{
$this->invitation = $invitation;
@ -58,6 +60,8 @@ class HtmlEngine
$this->settings = $this->client->getMergedSettings();
$this->entity_calc = $this->entity->calc();
$this->helpers = new Helpers();
}
@ -179,10 +183,10 @@ class HtmlEngine
$data['$taxes'] = ['value' => Number::formatMoney($this->entity_calc->getItemTotalTaxes(), $this->client) ?: '&nbsp;', 'label' => ctrans('texts.taxes')];
$data['$invoice.taxes'] = &$data['$taxes'];
$data['$invoice.custom1'] = ['value' => $this->formatCustomFieldValue('invoice1', $this->entity->custom_value1) ?: '&nbsp;', 'label' => $this->makeCustomField('invoice1')];
$data['$invoice.custom2'] = ['value' => $this->formatCustomFieldValue('invoice2', $this->entity->custom_value2) ?: '&nbsp;', 'label' => $this->makeCustomField('invoice2')];
$data['$invoice.custom3'] = ['value' => $this->formatCustomFieldValue('invoice3', $this->entity->custom_value3) ?: '&nbsp;', 'label' => $this->makeCustomField('invoice3')];
$data['$invoice.custom4'] = ['value' => $this->formatCustomFieldValue('invoice4', $this->entity->custom_value4) ?: '&nbsp;', 'label' => $this->makeCustomField('invoice4')];
$data['$invoice.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice1', $this->entity->custom_value1, $this->client) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
$data['$invoice.public_notes'] = ['value' => nl2br($this->entity->public_notes) ?: '&nbsp;', 'label' => ctrans('texts.public_notes')];
$data['$entity.public_notes'] = &$data['$invoice.public_notes'];
@ -214,10 +218,10 @@ class HtmlEngine
// $data['$details'] = ;
$data['$invoice_no'] = &$data['$number'];
$data['$invoice.invoice_no'] = &$data['$number'];
$data['$client1'] = ['value' => $this->formatCustomFieldValue('client1', $this->client->custom_value1) ?: '&nbsp;', 'label' => $this->makeCustomField('client1')];
$data['$client2'] = ['value' => $this->formatCustomFieldValue('client2', $this->client->custom_value2) ?: '&nbsp;', 'label' => $this->makeCustomField('client2')];
$data['$client3'] = ['value' => $this->formatCustomFieldValue('client3', $this->client->custom_value3) ?: '&nbsp;', 'label' => $this->makeCustomField('client3')];
$data['$client4'] = ['value' => $this->formatCustomFieldValue('client4', $this->client->custom_value4) ?: '&nbsp;', 'label' => $this->makeCustomField('client4')];
$data['$client1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client1', $this->client->custom_value1, $this->client) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client4')];
$data['$address1'] = ['value' => $this->client->address1 ?: '&nbsp;', 'label' => ctrans('texts.address1')];
$data['$address2'] = ['value' => $this->client->address2 ?: '&nbsp;', 'label' => ctrans('texts.address2')];
$data['$id_number'] = ['value' => $this->client->id_number ?: '&nbsp;', 'label' => ctrans('texts.id_number')];
@ -259,10 +263,10 @@ class HtmlEngine
$data['$contact.last_name'] = ['value' => isset($this->contact) ? $this->contact->last_name : '', 'label' => ctrans('texts.last_name')];
$data['$contact.custom1'] = ['value' => isset($this->contact) ? $this->contact->custom_value1 : '&nbsp;', 'label' => $this->makeCustomField('contact1')];
$data['$contact.custom2'] = ['value' => isset($this->contact) ? $this->contact->custom_value2 : '&nbsp;', 'label' => $this->makeCustomField('contact2')];
$data['$contact.custom3'] = ['value' => isset($this->contact) ? $this->contact->custom_value3 : '&nbsp;', 'label' => $this->makeCustomField('contact3')];
$data['$contact.custom4'] = ['value' => isset($this->contact) ? $this->contact->custom_value4 : '&nbsp;', 'label' => $this->makeCustomField('contact4')];
$data['$contact.custom1'] = ['value' => isset($this->contact) ? $this->contact->custom_value1 : '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
$data['$contact.custom2'] = ['value' => isset($this->contact) ? $this->contact->custom_value2 : '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact2')];
$data['$contact.custom3'] = ['value' => isset($this->contact) ? $this->contact->custom_value3 : '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact3')];
$data['$contact.custom4'] = ['value' => isset($this->contact) ? $this->contact->custom_value4 : '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', 'label' => ctrans('texts.postal_city_state')];
@ -284,15 +288,15 @@ class HtmlEngine
$data['$company.logo'] = ['value' => $logo ?: '&nbsp;', 'label' => ctrans('texts.logo')];
$data['$company_logo'] = &$data['$company.logo'];
$data['$company1'] = ['value' => $this->formatCustomFieldValue('company1', $this->settings->custom_value1) ?: '&nbsp;', 'label' => $this->makeCustomField('company1')];
$data['$company2'] = ['value' => $this->formatCustomFieldValue('company2', $this->settings->custom_value2) ?: '&nbsp;', 'label' => $this->makeCustomField('company2')];
$data['$company3'] = ['value' => $this->formatCustomFieldValue('company3', $this->settings->custom_value3) ?: '&nbsp;', 'label' => $this->makeCustomField('company3')];
$data['$company4'] = ['value' => $this->formatCustomFieldValue('company4', $this->settings->custom_value4) ?: '&nbsp;', 'label' => $this->makeCustomField('company4')];
$data['$company1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company4')];
$data['$custom_surcharge1'] = ['value' => $this->entity->custom_surcharge1 ?: '&nbsp;', 'label' => $this->makeCustomField('custom_surcharge1')];
$data['$custom_surcharge2'] = ['value' => $this->entity->custom_surcharge2 ?: '&nbsp;', 'label' => $this->makeCustomField('custom_surcharge2')];
$data['$custom_surcharge3'] = ['value' => $this->entity->custom_surcharge3 ?: '&nbsp;', 'label' => $this->makeCustomField('custom_surcharge3')];
$data['$custom_surcharge4'] = ['value' => $this->entity->custom_surcharge4 ?: '&nbsp;', 'label' => $this->makeCustomField('custom_surcharge4')];
$data['$custom_surcharge1'] = ['value' => $this->entity->custom_surcharge1 ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'custom_surcharge1')];
$data['$custom_surcharge2'] = ['value' => $this->entity->custom_surcharge2 ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'custom_surcharge2')];
$data['$custom_surcharge3'] = ['value' => $this->entity->custom_surcharge3 ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'custom_surcharge3')];
$data['$custom_surcharge4'] = ['value' => $this->entity->custom_surcharge4 ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'custom_surcharge4')];
$data['$product.item'] = ['value' => '', 'label' => ctrans('texts.item')];
$data['$product.date'] = ['value' => '', 'label' => ctrans('texts.date')];
@ -308,10 +312,10 @@ class HtmlEngine
$data['$product.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')];
$data['$product.description'] = ['value' => '', 'label' => ctrans('texts.description')];
$data['$product.unit_cost'] = ['value' => '', 'label' => ctrans('texts.unit_cost')];
$data['$product.product1'] = ['value' => '', 'label' => $this->makeCustomField('product1')];
$data['$product.product2'] = ['value' => '', 'label' => $this->makeCustomField('product2')];
$data['$product.product3'] = ['value' => '', 'label' => $this->makeCustomField('product3')];
$data['$product.product4'] = ['value' => '', 'label' => $this->makeCustomField('product4')];
$data['$product.product1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product1')];
$data['$product.product2'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product2')];
$data['$product.product3'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product3')];
$data['$product.product4'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product4')];
$data['$task.date'] = ['value' => '', 'label' => ctrans('texts.date')];
$data['$task.discount'] = ['value' => '', 'label' => ctrans('texts.discount')];
@ -517,46 +521,6 @@ class HtmlEngine
return $data;
}
private function makeCustomField($field) :string
{
$custom_fields = $this->company->custom_fields;
if ($custom_fields && property_exists($custom_fields, $field)) {
$custom_field = $custom_fields->{$field};
$custom_field_parts = explode('|', $custom_field);
return $custom_field_parts[0];
}
return '';
}
private function formatCustomFieldValue($field, $value) :string
{
$custom_fields = $this->company->custom_fields;
$custom_field = '';
if ($custom_fields && property_exists($custom_fields, $field)) {
$custom_field = $custom_fields->{$field};
$custom_field_parts = explode('|', $custom_field);
if (count($custom_field_parts) >= 2) {
$custom_field = $custom_field_parts[1];
}
}
switch ($custom_field) {
case 'date':
return $this->formatDate($value, $this->client->date_format());
break;
default:
return is_null($value) ? '' : $value;
break;
}
}
private function makeTotalTaxes() :string
{
$data = '';

View File

@ -68,20 +68,6 @@ trait MakesInvoiceValues
'company4',
];
public function makeCustomField($field) :string
{
$custom_fields = $this->company->custom_fields;
if ($custom_fields && property_exists($custom_fields, $field)) {
$custom_field = $custom_fields->{$field};
$custom_field_parts = explode('|', $custom_field);
return $custom_field_parts[0];
}
return '';
}
private function findCustomType($field)
{
$custom_fields = $this->company->custom_fields;
@ -96,35 +82,6 @@ trait MakesInvoiceValues
return '';
}
/**
* This method produces the key /value pairs for
* custom fields.
*
* We need to explode the field name and search for the |
* we split on the pipe, the first value is the field name
* and the second is the field _type_
*
* We transform the $value depending the $field type
*
* @param string $field The full field name
* @param string $value The custom value
* @return array The key value pair
*/
private function makeCustomFieldKeyValuePair($field, $value)
{
if ($this->findCustomType($field) == 'date') {
$value = $this->formatDate($value, $this->client->date_format());
} elseif ($this->findCustomType($field) == 'switch') {
$value = ctrans('texts.'.$value);
}
if (! $value) {
$value = '';
}
return ['value' => $value, 'field' => $this->makeCustomField($field)];
}
public function makeLabels($contact = null) :array
{
$data = [];

View File

@ -29,6 +29,10 @@ trait PdfMaker
{
$pdf = new Snappdf();
if (config('ninja.snappdf_chromium_path')) {
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
}
return $pdf
->setHtml($html)
->generate();

View File

@ -102,6 +102,12 @@
}
},
"scripts": {
"post-install-cmd": [
"vendor/bin/snappdf download"
],
"post-update-cmd": [
"vendor/bin/snappdf download"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],

38
composer.lock generated
View File

@ -116,16 +116,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.171.4",
"version": "3.171.6",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "6a6b5b2e48ad18c2bfa48b7035e09d220d0e3984"
"reference": "5587d22e63ef82ef74dffca5d47f307b84137b51"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6a6b5b2e48ad18c2bfa48b7035e09d220d0e3984",
"reference": "6a6b5b2e48ad18c2bfa48b7035e09d220d0e3984",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5587d22e63ef82ef74dffca5d47f307b84137b51",
"reference": "5587d22e63ef82ef74dffca5d47f307b84137b51",
"shasum": ""
},
"require": {
@ -200,22 +200,22 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.171.4"
"source": "https://github.com/aws/aws-sdk-php/tree/3.171.6"
},
"time": "2020-12-22T19:23:05+00:00"
"time": "2020-12-23T19:12:28+00:00"
},
{
"name": "beganovich/snappdf",
"version": "v1.0.0",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/beganovich/snappdf.git",
"reference": "96e0e2176f01ae712ab06dad0da6d2d79f7c1832"
"reference": "5f5e9bb17ddc9d9f16df7c20b7af35f04ffcddbd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/beganovich/snappdf/zipball/96e0e2176f01ae712ab06dad0da6d2d79f7c1832",
"reference": "96e0e2176f01ae712ab06dad0da6d2d79f7c1832",
"url": "https://api.github.com/repos/beganovich/snappdf/zipball/5f5e9bb17ddc9d9f16df7c20b7af35f04ffcddbd",
"reference": "5f5e9bb17ddc9d9f16df7c20b7af35f04ffcddbd",
"shasum": ""
},
"require": {
@ -252,9 +252,9 @@
"description": "Convert webpages or HTML into the PDF file using Chromium or Google Chrome.",
"support": {
"issues": "https://github.com/beganovich/snappdf/issues",
"source": "https://github.com/beganovich/snappdf/tree/v1.0.0"
"source": "https://github.com/beganovich/snappdf/tree/v1.2.0"
},
"time": "2020-12-21T14:55:21+00:00"
"time": "2020-12-28T11:57:06+00:00"
},
{
"name": "brick/math",
@ -10619,16 +10619,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v2.17.2",
"version": "v2.17.3",
"source": {
"type": "git",
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
"reference": "aaee4f3d16a996fc0b570be0c69d3b80c909c507"
"reference": "bd32f5dd72cdfc7b53f54077f980e144bfa2f595"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/aaee4f3d16a996fc0b570be0c69d3b80c909c507",
"reference": "aaee4f3d16a996fc0b570be0c69d3b80c909c507",
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/bd32f5dd72cdfc7b53f54077f980e144bfa2f595",
"reference": "bd32f5dd72cdfc7b53f54077f980e144bfa2f595",
"shasum": ""
},
"require": {
@ -10654,7 +10654,7 @@
"justinrainbow/json-schema": "^5.0",
"keradus/cli-executor": "^1.4",
"mikey179/vfsstream": "^1.6",
"php-coveralls/php-coveralls": "^2.4.1",
"php-coveralls/php-coveralls": "^2.4.2",
"php-cs-fixer/accessible-object": "^1.0",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1",
@ -10711,7 +10711,7 @@
"description": "A tool to automatically fix PHP code style",
"support": {
"issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues",
"source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.17.2"
"source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v2.17.3"
},
"funding": [
{
@ -10719,7 +10719,7 @@
"type": "github"
}
],
"time": "2020-12-17T16:41:55+00:00"
"time": "2020-12-24T11:14:44+00:00"
},
{
"name": "hamcrest/hamcrest-php",

View File

@ -99,6 +99,11 @@ return [
'emergency' => [
'path' => storage_path('logs/laravel.log'),
],
'invoiceninja' => [
'driver' => 'single',
'path' => storage_path('logs/invoiceninja.log'),
],
],
];

View File

@ -12,7 +12,7 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', ''),
'app_version' => '5.0.40',
'app_version' => '5.0.41',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false),
@ -136,4 +136,6 @@ return [
'base_path' => resource_path('views/pdf-designs/'),
],
'log_pdf_html' => env('LOG_PDF_HTML', false),
'expanded_logging' => env('EXPANDED_LOGGING', false),
'snappdf_chromium_path' => env('SNAPPDF_CHROMIUM_PATH', false),
];

View File

@ -30,8 +30,8 @@ const RESOURCES = {
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"assets/fonts/MaterialIcons-Regular.otf": "1288c9e28052e028aba623321f7826ac",
"/": "23224b5e03519aaa87594403d54412cf",
"version.json": "2337a2140fc2ea8baeb10b0e5a200f59",
"main.dart.js": "cb45263da95ac53b92a95e621c5880a8",
"version.json": "304f9dfb96375c4f92a1f5eb00536410",
"main.dart.js": "f64e2f489dc693583352428978fbbf8f",
"favicon.png": "dca91c54388f52eded692718d5a98b8b"
};

View File

@ -1,2 +1,2 @@
/*! For license information please see stripe-sofort.js.LICENSE.txt */
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=6)}({6:function(e,t,n){e.exports=n("RFiP")},RFiP:function(e,t){function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}new function e(t){var r=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),n(this,"setupStripe",(function(){return r.stripe=Stripe(r.key),r})),n(this,"handle",(function(){var e={type:"sofort",amount:document.querySelector('meta[name="amount"]').content,currency:"eur",redirect:{return_url:document.querySelector('meta[name="return-url"]').content},sofort:{country:document.querySelector('meta[name="country"').content}};document.getElementById("pay-now").addEventListener("click",(function(t){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),r.stripe.createSource(e).then((function(e){if(e.hasOwnProperty("source"))return window.location=e.source.redirect.url;document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),this.errors.textContent="",this.errors.textContent=e.error.message,this.errors.hidden=!1,document.getElementById("pay-now").disabled=!1}))}))})),this.key=t,this.errors=document.getElementById("errors")}(document.querySelector('meta[name="stripe-publishable-key"]').content).setupStripe().handle()}});
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=6)}({6:function(e,t,n){e.exports=n("RFiP")},RFiP:function(e,t){function n(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}new function e(t){var r=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),n(this,"setupStripe",(function(){return r.stripe=Stripe(r.key),r})),n(this,"handle",(function(){var e={type:"sofort",amount:document.querySelector('meta[name="amount"]').content,currency:"eur",redirect:{return_url:document.querySelector('meta[name="return-url"]').content},sofort:{country:document.querySelector('meta[name="country"]').content}};document.getElementById("pay-now").addEventListener("click",(function(t){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),r.stripe.createSource(e).then((function(e){if(e.hasOwnProperty("source"))return window.location=e.source.redirect.url;document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden"),this.errors.textContent="",this.errors.textContent=e.error.message,this.errors.hidden=!1,document.getElementById("pay-now").disabled=!1}))}))})),this.key=t,this.errors=document.getElementById("errors")}(document.querySelector('meta[name="stripe-publishable-key"]').content).setupStripe().handle()}});

File diff suppressed because one or more lines are too long

113274
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -10,11 +10,11 @@
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=c4012ad90f17d60432ad",
"/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=6dbe9316b98deea55421",
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=f4659d26a26d2f408397",
"/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=bb7c55ca3da2d29e55d2",
"/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=9b9fd56d655ad238f149",
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1b8f9325aa6e8595e7fa",
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12",
"/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=5c35d28cf0a3286e7c45",
"/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=fa54bb4229aba6b0817c",
"/js/setup/setup.js": "/js/setup/setup.js?id=b264d828086fdf87b710",
"/js/setup/setup.js": "/js/setup/setup.js?id=29e88ab480038cba57df",
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad"
}

View File

@ -1 +1 @@
{"app_name":"invoiceninja_flutter","version":"5.0.33","build_number":"33"}
{"app_name":"invoiceninja_flutter","version":"5.0.35","build_number":"35"}

View File

@ -25,13 +25,18 @@ class Setup {
handleDatabaseCheck() {
let data = {
db_host: document.querySelector('input[name="db_host"]').value,
db_database: document.querySelector('input[name="db_database"]').value,
db_username: document.querySelector('input[name="db_username"]').value,
db_password: document.querySelector('input[name="db_password"]').value,
db_database: document.querySelector('input[name="db_database"]')
.value,
db_username: document.querySelector('input[name="db_username"]')
.value,
db_password: document.querySelector('input[name="db_password"]')
.value,
};
Axios.post('/setup/check_db', data)
.then((response) => this.handleSuccess(this.checkDbAlert, 'mail-wrapper'))
.then((response) =>
this.handleSuccess(this.checkDbAlert, 'mail-wrapper')
)
.catch((e) =>
this.handleFailure(this.checkDbAlert, e.response.data.message)
);
@ -39,7 +44,8 @@ class Setup {
handleSmtpCheck() {
let data = {
mail_driver: document.querySelector('select[name="mail_driver"]').value,
mail_driver: document.querySelector('select[name="mail_driver"]')
.value,
mail_name: document.querySelector('input[name="mail_name"]').value,
mail_address: document.querySelector('input[name="mail_address"]')
.value,
@ -59,7 +65,7 @@ class Setup {
this.handleSuccess(this.checkSmtpAlert, 'account-wrapper');
this.handleSuccess(this.checkSmtpAlert, 'submit-wrapper');
return this.checkSmtpButton.disabled = false;
return (this.checkSmtpButton.disabled = false);
}
Axios.post('/setup/check_mail', data)
@ -74,13 +80,18 @@ class Setup {
}
handleTestPdfCheck() {
this.checkPdfButton.disabled = true;
Axios.post('/setup/check_pdf', {})
.then((response) => {
try {
let win = window.open(response.data.url, '_blank');
win.focus();
return this.handleSuccess(this.checkPdfAlert, 'database-wrapper');
return this.handleSuccess(
this.checkPdfAlert,
'database-wrapper'
);
} catch (error) {
this.handleSuccess(this.checkPdfAlert, 'database-wrapper');
this.checkPdfAlert.textContent = `Success! You can preview test PDF here: ${response.data.url}`;
@ -89,7 +100,8 @@ class Setup {
.catch((error) => {
console.log(error);
this.handleFailure(this.checkPdfAlert);
});
})
.finally(() => (this.checkPdfButton.disabled = false));
}
handleSuccess(element, nextStep = null) {
@ -99,7 +111,9 @@ class Setup {
if (nextStep) {
document.getElementById(nextStep).classList.remove('hidden');
document.getElementById(nextStep).scrollIntoView({behavior: 'smooth', block: 'center'});
document
.getElementById(nextStep)
.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}

View File

@ -37,7 +37,7 @@ if(!isset($design)) $design = 'light';
<div class="grid grid-cols-6">
<div class="col-span-4 col-start-2">
<div class="{{ $design == 'light' ? 'bg-white' : 'bg-gray-900' }} shadow border-t-2 {{ $design == 'light' ? 'border-primary' : 'border-gray-800' }}">
<div class="px-10">
<div class="px-10 break-all">
{{ $header }}
</div>
<div id="text" class="flex flex-col px-10 py-6">

View File

@ -56,7 +56,7 @@ class ImportCsvTest extends TestCase
public function testInvoiceCsvImport()
{
$this->markTestSkipped();
$this->markTestSkipped();
$csv = file_get_contents(base_path().'/tests/Feature/Import/invoice.csv');
$hash = Str::random(32);
@ -132,7 +132,7 @@ class ImportCsvTest extends TestCase
public function testProductCsvImport()
{
$this->markTestSkipped();
$this->markTestSkipped();
$csv = file_get_contents(base_path().'/tests/Feature/Import/products.csv');

View File

@ -0,0 +1,86 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace Tests\Feature;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
* @covers App\Http\Controllers\PreviewController
*/
class PreviewTest extends TestCase
{
use DatabaseTransactions;
use MockAccountData;
public function setUp() :void
{
parent::setUp();
$this->makeTestData();
$this->withoutMiddleware(
ThrottleRequests::class
);
}
public function testPreviewRoute()
{
$data = $this->getData();
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/preview/', $data);
$response->assertStatus(200);
}
public function testPreviewHtmlResponse()
{
$data = $this->getData();
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/preview?html=true', $data);
$response->assertStatus(200);
}
private function getData()
{
$data =
[
'entity_type' => 'invoice',
'entity_id' => '',
'design' => [
'name' => '',
'design' => [
'includes' => '</style>',
'header' => '<div id="header"></div>',
'body' => '<div id="body">',
'product' => '',
'task' => '',
'footer' => '<div id="footer">$entity_footer</div>'
],
],
'is_custom' => 1,
];
return $data;
}
}

View File

@ -1,4 +1,5 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -8,6 +9,7 @@
*
* @license https://opensource.org/licenses/AAL
*/
namespace Tests\Pdf;
use Beganovich\Snappdf\Snappdf;
@ -19,16 +21,20 @@ use Tests\TestCase;
*/
class PdfGenerationTest extends TestCase
{
public function setUp() :void
public function setUp(): void
{
parent::setUp();
}
public function testPdfGeneration()
{
$snappdf = new Snappdf();
$pdf = new Snappdf();
$pdf = $snappdf
if (config('ninja.snappdf_chromium_path')) {
$pdf->setChromiumPath(config('ninja.snappdf_chromium_path'));
}
$pdf = $pdf
->setHtml('<h1>Invoice Ninja</h1>')
->generate();