Fixes for api.php

This commit is contained in:
David Bomba 2022-12-05 17:35:15 +11:00
commit d481ac3d9a
192 changed files with 264141 additions and 62422 deletions

View File

@ -1 +1 @@
5.5.42 5.5.45

View File

@ -25,6 +25,9 @@ use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyTaskStatuses; use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Models\Account; use App\Models\Account;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\BankTransactionRule;
use App\Models\Client; use App\Models\Client;
use App\Models\ClientContact; use App\Models\ClientContact;
use App\Models\Company; use App\Models\Company;
@ -47,6 +50,7 @@ use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Carbon\Carbon; use Carbon\Carbon;
use Database\Factories\BankTransactionRuleFactory;
use Faker\Factory; use Faker\Factory;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
@ -87,7 +91,7 @@ class CreateSingleAccount extends Command
MultiDB::setDb($this->option('database')); MultiDB::setDb($this->option('database'));
$this->info(date('r').' Create Single Sample Account...'); $this->info(date('r').' Create Single Sample Account...');
$this->count = 1; $this->count = 5;
$this->gateway = $this->argument('gateway'); $this->gateway = $this->argument('gateway');
$this->info('Warming up cache'); $this->info('Warming up cache');
@ -179,6 +183,23 @@ class CreateSingleAccount extends Command
'rate' => 5 'rate' => 5
]); ]);
$bi = BankIntegration::factory()->create([
'account_id' => $account->id,
'company_id' => $company->id,
'user_id' => $user->id,
]);
BankTransaction::factory()->count(50)->create([
'bank_integration_id' => $bi->id,
'user_id' => $user->id,
'company_id' => $company->id,
]);
$btr = BankTransactionRule::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'applies_to' => (bool)rand(0,1) ? 'CREDIT' : 'DEBIT',
]);
$this->info('Creating '.$this->count.' clients'); $this->info('Creating '.$this->count.' clients');
@ -358,7 +379,7 @@ class CreateSingleAccount extends Command
private function createExpense($client) private function createExpense($client)
{ {
Expense::factory()->count(rand(1, 2))->create([ Expense::factory()->count(rand(1, 20))->create([
'user_id' => $client->user->id, 'user_id' => $client->user->id,
'client_id' => $client->id, 'client_id' => $client->id,
'company_id' => $client->company->id, 'company_id' => $client->company->id,
@ -590,7 +611,6 @@ class CreateSingleAccount extends Command
$cached_tables = config('ninja.cached_tables'); $cached_tables = config('ninja.cached_tables');
foreach ($cached_tables as $name => $class) { foreach ($cached_tables as $name => $class) {
if (! Cache::has($name)) {
// check that the table exists in case the migration is pending // check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) { if (! Schema::hasTable((new $class())->getTable())) {
continue; continue;
@ -608,7 +628,6 @@ class CreateSingleAccount extends Command
if ($tableData->count()) { if ($tableData->count()) {
Cache::forever($name, $tableData); Cache::forever($name, $tableData);
} }
}
} }
} }

View File

@ -41,6 +41,7 @@ use App\Models\Vendor;
use App\Models\VendorContact; use App\Models\VendorContact;
use App\Repositories\InvoiceRepository; use App\Repositories\InvoiceRepository;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\AppSetup;
use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Carbon\Carbon; use Carbon\Carbon;
@ -53,7 +54,7 @@ use Illuminate\Support\Str;
class DemoMode extends Command class DemoMode extends Command
{ {
use MakesHash, GeneratesCounter; use MakesHash, GeneratesCounter, AppSetup;
protected $signature = 'ninja:demo-mode'; protected $signature = 'ninja:demo-mode';
@ -83,34 +84,14 @@ class DemoMode extends Command
$cached_tables = config('ninja.cached_tables'); $cached_tables = config('ninja.cached_tables');
foreach ($cached_tables as $name => $class) {
if (! Cache::has($name)) {
// check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) {
continue;
}
if ($name == 'payment_terms') {
$orderBy = 'num_days';
} elseif ($name == 'fonts') {
$orderBy = 'sort_order';
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
$orderBy = 'name';
} else {
$orderBy = 'id';
}
$tableData = $class::orderBy($orderBy)->get();
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
}
$this->info('Migrating'); $this->info('Migrating');
Artisan::call('migrate:fresh --force'); Artisan::call('migrate:fresh --force');
$this->info('Seeding'); $this->info('Seeding');
Artisan::call('db:seed --force'); Artisan::call('db:seed --force');
$this->buildCache(true);
$this->info('Seeding Random Data'); $this->info('Seeding Random Data');
$this->createSmallAccount(); $this->createSmallAccount();

View File

@ -26,6 +26,7 @@ use App\Utils\Traits\MakesReminders;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
//@deprecated 27-11-2022 - only ever should be used for testing
class SendRemindersCron extends Command class SendRemindersCron extends Command
{ {
use MakesReminders, MakesDates; use MakesReminders, MakesDates;

View File

@ -54,13 +54,13 @@ class Kernel extends ConsoleKernel
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping()->name('reminder-job')->onOneServer(); $schedule->job(new ReminderJob)->hourly()->withoutOverlapping()->name('reminder-job')->onOneServer();
/* Returns the number of jobs in the queue */ /* Returns the number of jobs in the queue */
$schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping(); $schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping()->name('queue-size-job')->onOneServer();
/* Checks for large companies and marked them as is_large */ /* Checks for large companies and marked them as is_large */
$schedule->job(new CompanySizeCheck)->dailyAt('23:20')->withoutOverlapping()->name('company-size-job')->onOneServer(); $schedule->job(new CompanySizeCheck)->dailyAt('23:20')->withoutOverlapping()->name('company-size-job')->onOneServer();
/* Pulls in the latest exchange rates */ /* Pulls in the latest exchange rates */
$schedule->job(new UpdateExchangeRates)->dailyAt('23:30')->withoutOverlapping(); $schedule->job(new UpdateExchangeRates)->dailyAt('23:30')->withoutOverlapping()->name('exchange-rate-job')->onOneServer();
/* Runs cleanup code for subscriptions */ /* Runs cleanup code for subscriptions */
$schedule->job(new SubscriptionCron)->dailyAt('00:01')->withoutOverlapping()->name('subscription-job')->onOneServer(); $schedule->job(new SubscriptionCron)->dailyAt('00:01')->withoutOverlapping()->name('subscription-job')->onOneServer();
@ -105,11 +105,11 @@ class Kernel extends ConsoleKernel
//not used @deprecate //not used @deprecate
// $schedule->job(new SendFailedEmails)->daily()->withoutOverlapping(); // $schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
$schedule->command('ninja:check-data --database=db-ninja-01')->daily('02:00')->withoutOverlapping(); $schedule->command('ninja:check-data --database=db-ninja-01')->dailyAt('02:10')->withoutOverlapping()->name('check-data-db-1-job')->onOneServer();
$schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('02:05')->withoutOverlapping(); $schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('02:20')->withoutOverlapping()->name('check-data-db-2-job')->onOneServer();
$schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping(); $schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping()->name('s3-cleanup-job')->onOneServer();
} }

View File

@ -0,0 +1,50 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class BankAccountsCreated extends GenericMixedMetric
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'mixed_metric';
/**
* The name of the counter.
* @var string
*/
public $name = 'bank_accounts.created';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
public $int_metric1 = 0;
public function __construct($int_metric1)
{
$this->int_metric1 = $int_metric1;
}
}

View File

@ -32,6 +32,7 @@ use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use PDOException; use PDOException;
use Sentry\Laravel\Integration;
use Sentry\State\Scope; use Sentry\State\Scope;
use Swift_TransportException; use Swift_TransportException;
use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\CommandNotFoundException;
@ -84,7 +85,7 @@ class Handler extends ExceptionHandler
} }
if (Ninja::isHosted() && ! ($exception instanceof ValidationException)) { if (Ninja::isHosted() && ! ($exception instanceof ValidationException)) {
app('sentry')->configureScope(function (Scope $scope): void { Integration::configureScope(function (Scope $scope): void {
$name = 'hosted@invoiceninja.com'; $name = 'hosted@invoiceninja.com';
if (auth()->guard('contact') && auth()->guard('contact')->user()) { if (auth()->guard('contact') && auth()->guard('contact')->user()) {
@ -104,9 +105,9 @@ class Handler extends ExceptionHandler
]); ]);
}); });
app('sentry')->captureException($exception); Integration::captureUnhandledException($exception);
} elseif (app()->bound('sentry') && $this->shouldReport($exception)) { } elseif (app()->bound('sentry') && $this->shouldReport($exception)) {
app('sentry')->configureScope(function (Scope $scope): void { Integration::configureScope(function (Scope $scope): void {
if (auth()->guard('contact') && auth()->guard('contact')->user() && auth()->guard('contact')->user()->company->account->report_errors) { if (auth()->guard('contact') && auth()->guard('contact')->user() && auth()->guard('contact')->user()->company->account->report_errors) {
$scope->setUser([ $scope->setUser([
'id' => auth()->guard('contact')->user()->company->account->key, 'id' => auth()->guard('contact')->user()->company->account->key,
@ -123,7 +124,7 @@ class Handler extends ExceptionHandler
}); });
if ($this->validException($exception)) { if ($this->validException($exception)) {
app('sentry')->captureException($exception); Integration::captureUnhandledException($exception);
} }
} }

View File

@ -76,6 +76,7 @@ class ClientExport extends BaseExport
'contact_custom_value3' => 'contact.custom_value3', 'contact_custom_value3' => 'contact.custom_value3',
'contact_custom_value4' => 'contact.custom_value4', 'contact_custom_value4' => 'contact.custom_value4',
'email' => 'contact.email', 'email' => 'contact.email',
'status' => 'status'
]; ];
private array $decorate_keys = [ private array $decorate_keys = [
@ -173,6 +174,19 @@ class ClientExport extends BaseExport
$entity['industry_id'] = $client->industry ? ctrans("texts.industry_{$client->industry->name}") : ''; $entity['industry_id'] = $client->industry ? ctrans("texts.industry_{$client->industry->name}") : '';
} }
$entity['status'] = $this->calculateStatus($client);
return $entity; return $entity;
} }
private function calculateStatus($client)
{
if($client->is_deleted)
return ctrans('texts.deleted');
if($client->deleted_at)
return ctrans('texts.arcvived');
return ctrans('texts.active');
}
} }

View File

@ -54,6 +54,7 @@ class RecurringInvoiceExport extends BaseExport
'po_number' => 'po_number', 'po_number' => 'po_number',
'private_notes' => 'private_notes', 'private_notes' => 'private_notes',
'public_notes' => 'public_notes', 'public_notes' => 'public_notes',
'next_send_date' => 'next_send_date',
'status' => 'status_id', 'status' => 'status_id',
'tax_name1' => 'tax_name1', 'tax_name1' => 'tax_name1',
'tax_name2' => 'tax_name2', 'tax_name2' => 'tax_name2',
@ -66,6 +67,7 @@ class RecurringInvoiceExport extends BaseExport
'currency' => 'currency_id', 'currency' => 'currency_id',
'vendor' => 'vendor_id', 'vendor' => 'vendor_id',
'project' => 'project_id', 'project' => 'project_id',
'frequency' => 'frequency_id'
]; ];
private array $decorate_keys = [ private array $decorate_keys = [
@ -162,6 +164,8 @@ class RecurringInvoiceExport extends BaseExport
$entity['vendor'] = $invoice->vendor ? $invoice->vendor->name : ''; $entity['vendor'] = $invoice->vendor ? $invoice->vendor->name : '';
} }
$entity['frequency'] = $invoice->frequencyForKey($invoice->frequency_id);
return $entity; return $entity;
} }
} }

View File

@ -29,6 +29,7 @@ class RecurringInvoiceFactory
$invoice->private_notes = ''; $invoice->private_notes = '';
$invoice->date = null; $invoice->date = null;
$invoice->due_date = null; $invoice->due_date = null;
$invoice->due_date_days = 'terms';
$invoice->partial_due_date = null; $invoice->partial_due_date = null;
$invoice->is_deleted = false; $invoice->is_deleted = false;
$invoice->line_items = json_encode([]); $invoice->line_items = json_encode([]);

View File

@ -11,6 +11,7 @@
namespace App\Filters; namespace App\Filters;
use App\Models\Company;
use App\Models\User; use App\Models\User;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
@ -54,6 +55,15 @@ class DocumentFilters extends QueryFilters
return $this->builder->orderBy($sort_col[0], $sort_col[1]); return $this->builder->orderBy($sort_col[0], $sort_col[1]);
} }
public function company_documents($value = 'false')
{
if($value == 'true')
return $this->builder->where('documentable_type', Company::class);
return $this->builder;
}
/** /**
* Returns the base query. * Returns the base query.
* *

View File

@ -56,8 +56,6 @@ class InvoiceFilters extends QueryFilters
if (in_array('unpaid', $status_parameters)) { if (in_array('unpaid', $status_parameters)) {
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]); $this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]);
} }
//->where('due_date', '>', Carbon::now())
//->orWhere('partial_due_date', '>', Carbon::now());
if (in_array('overdue', $status_parameters)) { if (in_array('overdue', $status_parameters)) {
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) $this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
@ -167,7 +165,7 @@ class InvoiceFilters extends QueryFilters
->orderBy('due_date', 'ASC'); ->orderBy('due_date', 'ASC');
} }
public function payable(string $client_id) public function payable(string $client_id = '')
{ {
if (strlen($client_id) == 0) { if (strlen($client_id) == 0) {
return $this->builder; return $this->builder;
@ -185,7 +183,7 @@ class InvoiceFilters extends QueryFilters
* @param string sort formatted as column|asc * @param string sort formatted as column|asc
* @return Builder * @return Builder
*/ */
public function sort(string $sort) : Builder public function sort(string $sort = '') : Builder
{ {
$sort_col = explode('|', $sort); $sort_col = explode('|', $sort);

View File

@ -539,7 +539,7 @@ class BaseController extends Controller
$query->where('bank_integrations.user_id', $user->id); $query->where('bank_integrations.user_id', $user->id);
} }
}, },
'company.bank_transaction_rules'=> function ($query) use ($updated_at, $user) { 'company.bank_transaction_rules'=> function ($query) use ($user) {
if (! $user->isAdmin()) { if (! $user->isAdmin()) {
$query->where('bank_transaction_rules.user_id', $user->id); $query->where('bank_transaction_rules.user_id', $user->id);

View File

@ -131,22 +131,22 @@ class EmailController extends BaseController
if(Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified) if(Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified)
return response(['message' => 'Please verify your account to send emails.'], 400); return response(['message' => 'Please verify your account to send emails.'], 400);
nlog($entity);
if($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order' || $entity == 'App\Models\PurchaseOrder'){ if($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order' || $entity == 'App\Models\PurchaseOrder'){
return $this->sendPurchaseOrder($entity_obj, $data, $template); return $this->sendPurchaseOrder($entity_obj, $data, $template);
} }
$entity_obj->invitations->each(function ($invitation) use ($data, $entity_string, $entity_obj, $template) { $entity_obj->invitations->each(function ($invitation) use ($data, $entity_string, $entity_obj, $template) {
if (! $invitation->contact->trashed() && $invitation->contact->email) { if (! $invitation->contact->trashed() && $invitation->contact->email) {
$entity_obj->service()->markSent()->save(); $entity_obj->service()->markSent()->save();
EmailEntity::dispatch($invitation->fresh(), $invitation->company, $template, $data); EmailEntity::dispatch($invitation->fresh(), $invitation->company, $template, $data)->delay(now()->addSeconds(2));
} }
}); });
$entity_obj = $entity_obj->fresh();
$entity_obj->last_sent_date = now(); $entity_obj->last_sent_date = now();
$entity_obj->save(); $entity_obj->save();
/*Only notify the admin ONCE, not once per contact/invite*/ /*Only notify the admin ONCE, not once per contact/invite*/
@ -194,7 +194,7 @@ class EmailController extends BaseController
$data['template'] = $template; $data['template'] = $template;
PurchaseOrderEmail::dispatch($entity_obj, $entity_obj->company, $data); PurchaseOrderEmail::dispatch($entity_obj, $entity_obj->company, $data)->delay(now()->addSeconds(2));
return $this->itemResponse($entity_obj); return $this->itemResponse($entity_obj);

View File

@ -136,6 +136,8 @@ class ImportController extends Controller
} }
$csv = Reader::createFromString($csvfile); $csv = Reader::createFromString($csvfile);
$csvdelimiter = self::detectDelimiter($csvfile);
$csv->setDelimiter($csvdelimiter);
$stmt = new Statement(); $stmt = new Statement();
$data = iterator_to_array($stmt->process($csv)); $data = iterator_to_array($stmt->process($csv));
@ -156,4 +158,17 @@ class ImportController extends Controller
return $data; return $data;
} }
public function detectDelimiter($csvfile)
{
$delimiters = array(',', '.', ';');
$bestDelimiter = false;
$count = 0;
foreach ($delimiters as $delimiter)
if (substr_count($csvfile, $delimiter) > $count) {
$count = substr_count($csvfile, $delimiter);
$bestDelimiter = $delimiter;
}
return $bestDelimiter;
}
} }

View File

@ -428,13 +428,13 @@ class InvoiceController extends BaseController
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$transaction = [ // $transaction = [
'invoice' => $invoice->transaction_event(), // 'invoice' => $invoice->transaction_event(),
'payment' => [], // 'payment' => [],
'client' => $invoice->client->transaction_event(), // 'client' => $invoice->client->transaction_event(),
'credit' => [], // 'credit' => [],
'metadata' => [], // 'metadata' => [],
]; // ];
// TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db); // TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db);

View File

@ -183,7 +183,7 @@ class MigrationController extends BaseController
$company->expenses()->forceDelete(); $company->expenses()->forceDelete();
$company->bank_transaction_rules()->forceDelete(); $company->bank_transaction_rules()->forceDelete();
$company->bank_transactions()->forceDelete(); $company->bank_transactions()->forceDelete();
$company->bank_integrations()->forceDelete(); // $company->bank_integrations()->forceDelete();
$company->all_activities()->forceDelete(); $company->all_activities()->forceDelete();

View File

@ -26,7 +26,7 @@
* @OA\Property(property="city", type="string", example="", description="________"), * @OA\Property(property="city", type="string", example="", description="________"),
* @OA\Property(property="state", type="string", example="", description="________"), * @OA\Property(property="state", type="string", example="", description="________"),
* @OA\Property(property="postal_code", type="string", example="", description="________"), * @OA\Property(property="postal_code", type="string", example="", description="________"),
* @OA\Property(property="work_phone", type="string", example="555-3434-3434", description="The client phone number"), * @OA\Property(property="phone", type="string", example="555-3434-3434", description="The client phone number"),
* @OA\Property(property="country_id", type="string", example="", description="________"), * @OA\Property(property="country_id", type="string", example="", description="________"),
* @OA\Property(property="currency_id", type="string", example="4", description="________"), * @OA\Property(property="currency_id", type="string", example="4", description="________"),
* @OA\Property(property="custom_value1", type="string", example="", description="________"), * @OA\Property(property="custom_value1", type="string", example="", description="________"),

View File

@ -11,6 +11,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Http\Requests\TwoFactor\EnableTwoFactorRequest;
use App\Models\User; use App\Models\User;
use App\Transformers\UserTransformer; use App\Transformers\UserTransformer;
use Crypt; use Crypt;
@ -51,17 +52,16 @@ class TwoFactorController extends BaseController
return response()->json(['data' => $data], 200); return response()->json(['data' => $data], 200);
} }
public function enableTwoFactor() public function enableTwoFactor(EnableTwoFactorRequest $request)
{ {
$google2fa = new Google2FA(); $google2fa = new Google2FA();
$user = auth()->user(); $user = auth()->user();
$secret = request()->input('secret'); $secret = $request->input('secret');
$oneTimePassword = request()->input('one_time_password'); $oneTimePassword = $request->input('one_time_password');
if ($google2fa->verifyKey($secret, $oneTimePassword) && $user->phone && $user->email_verified_at) { if ($google2fa->verifyKey($secret, $oneTimePassword) && $user->phone && $user->email_verified_at) {
$user->google_2fa_secret = encrypt($secret); $user->google_2fa_secret = encrypt($secret);
$user->save(); $user->save();
return response()->json(['message' => ctrans('texts.enabled_two_factor')], 200); return response()->json(['message' => ctrans('texts.enabled_two_factor')], 200);
@ -72,6 +72,11 @@ class TwoFactorController extends BaseController
return response()->json(['message' => 'No phone record or user is not confirmed'], 400); return response()->json(['message' => 'No phone record or user is not confirmed'], 400);
} }
/*
* @param App\Models\User $user
* @param App\Models\User auth()->user()
*/
public function disableTwoFactor() public function disableTwoFactor()
{ {
$user = auth()->user(); $user = auth()->user();

View File

@ -39,14 +39,6 @@ class BillingPortalPurchasev2 extends Component
*/ */
public $hash; public $hash;
/**
* Top level text on the left side of billing page.
*
* @var string
*/
public $heading_text;
/** /**
* E-mail address model for user input. * E-mail address model for user input.
* *
@ -80,9 +72,13 @@ class BillingPortalPurchasev2 extends Component
* *
* @var \string[][] * @var \string[][]
*/ */
protected $rules = [ // protected $rules = [
'email' => ['required', 'email'], // 'email' => ['required', 'email'],
]; // 'data' => ['required', 'array'],
// 'data.*.recurring_qty' => ['required', 'between:100,1000'],
// 'data.*.optional_recurring_qty' => ['required', 'between:100,1000'],
// 'data.*.optional_qty' => ['required', 'between:100,1000'],
// ];
/** /**
* Id for CompanyGateway record. * Id for CompanyGateway record.
@ -120,6 +116,8 @@ class BillingPortalPurchasev2 extends Component
'payment_required' => true, 'payment_required' => true,
]; ];
public $data = [];
/** /**
* List of payment methods fetched from client. * List of payment methods fetched from client.
* *
@ -187,6 +185,8 @@ class BillingPortalPurchasev2 extends Component
$this->quantity = 1; $this->quantity = 1;
$this->data = [];
$this->price = $this->subscription->price; $this->price = $this->subscription->price;
if (request()->query('coupon')) { if (request()->query('coupon')) {
@ -198,6 +198,44 @@ class BillingPortalPurchasev2 extends Component
} }
} }
public function updatingData()
{
nlog('updating');
// nlog($this->data);
}
public function updatedData()
{
nlog('updated');
nlog($this->data);
$validatedData = $this->validate();
nlog( $validatedData );
}
public function updated($propertyName)
{
nlog("validating {$propertyName}");
$this->errors = $this->validateOnly($propertyName);
nlog($this->errors);
$validatedData = $this->validate();
nlog( $validatedData );
}
public function rules()
{
$rules = [
'email' => ['required', 'email'],
'data' => ['required', 'array'],
'data.*.recurring_qty' => ['required', 'between:100,1000'],
'data.*.optional_recurring_qty' => ['required', 'between:100,1000'],
'data.*.optional_qty' => ['required', 'between:100,1000'],
];
return $rules;
}
/** /**
* Handle user authentication * Handle user authentication
* *
@ -265,9 +303,6 @@ class BillingPortalPurchasev2 extends Component
} }
} }
// nlog($this->subscription->group_settings->settings);
// nlog($this->subscription->group_settings->settings->currency_id);
if(array_key_exists('currency_id', $this->request_data)) { if(array_key_exists('currency_id', $this->request_data)) {
$currency = Cache::get('currencies')->filter(function ($item){ $currency = Cache::get('currencies')->filter(function ($item){
@ -525,4 +560,11 @@ class BillingPortalPurchasev2 extends Component
return render('components.livewire.billing-portal-purchasev2'); return render('components.livewire.billing-portal-purchasev2');
} }
public function changeData()
{
nlog($this->data);
}
} }

View File

@ -44,7 +44,7 @@ class StoreBankIntegrationRequest extends Request
{ {
$input = $this->all(); $input = $this->all();
if(!array_key_exists('provider_name', $input) || strlen($input['provider_name']) == 0) if((!array_key_exists('provider_name', $input) || strlen($input['provider_name']) == 0) && array_key_exists('bank_account_name', $input))
$input['provider_name'] = $input['bank_account_name']; $input['provider_name'] = $input['bank_account_name'];
$this->replace($input); $this->replace($input);

View File

@ -13,6 +13,8 @@ namespace App\Http\Requests\BankTransaction;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Models\BankTransaction; use App\Models\BankTransaction;
use App\Models\Expense;
use App\Models\Payment;
class MatchBankTransactionRequest extends Request class MatchBankTransactionRequest extends Request
{ {
@ -35,8 +37,10 @@ class MatchBankTransactionRequest extends Request
]; ];
$rules['transactions.*.ninja_category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $rules['transactions.*.ninja_category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
$rules['transactions.*.vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $rules['transactions.*.vendor_id'] = 'bail|nullable|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
$rules['transactions.*.id'] = 'bail|required|exists:bank_transactions,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $rules['transactions.*.id'] = 'bail|required|exists:bank_transactions,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
$rules['transactions.*.payment_id'] = 'bail|sometimes|nullable|exists:payments,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
$rules['transactions.*.expense_id'] = 'bail|sometimes|nullable|exists:expenses,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
return $rules; return $rules;
@ -58,7 +62,26 @@ class MatchBankTransactionRequest extends Request
if(array_key_exists('vendor_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['vendor_id']) >= 1) if(array_key_exists('vendor_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['vendor_id']) >= 1)
$inputs['transactions'][$key]['vendor_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['vendor_id']); $inputs['transactions'][$key]['vendor_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['vendor_id']);
// $input = $this->decodePrimaryKeys($input); if(array_key_exists('payment_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['payment_id']) >= 1){
$inputs['transactions'][$key]['payment_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['payment_id']);
$p = Payment::withTrashed()->find($inputs['transactions'][$key]['payment_id']);
/*Ensure we don't relink an existing payment*/
if(!$p || $p->transaction_id)
$inputs['transactions'][$key]['payment_id'] = null;
}
if(array_key_exists('expense_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['expense_id']) >= 1){
$inputs['transactions'][$key]['expense_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['expense_id']);
$e = Expense::withTrashed()->find($inputs['transactions'][$key]['expense_id']);
/*Ensure we don't relink an existing expense*/
if(!$e || $e->transaction_id)
$inputs['transactions'][$key]['expense_id'] = null;
}
} }
$this->replace($inputs); $this->replace($inputs);

View File

@ -34,8 +34,7 @@ class StoreBankTransactionRequest extends Request
$rules = []; $rules = [];
if(isset($this->bank_integration_id)) $rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
$rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
return $rules; return $rules;
} }
@ -44,7 +43,9 @@ class StoreBankTransactionRequest extends Request
{ {
$input = $this->all(); $input = $this->all();
if(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1 && !is_numeric($input['bank_integration_id'])) if(array_key_exists('bank_integration_id', $input) && $input['bank_integration_id'] == "")
unset($input['bank_integration_id']);
elseif(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1 && !is_numeric($input['bank_integration_id']))
$input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']); $input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']);
$this->replace($input); $this->replace($input);

View File

@ -45,8 +45,7 @@ class UpdateBankTransactionRequest extends Request
if(isset($this->expense_id)) if(isset($this->expense_id))
$rules['expense_id'] = 'bail|required|exists:expenses,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $rules['expense_id'] = 'bail|required|exists:expenses,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
if(isset($this->bank_integration_id)) $rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
$rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
return $rules; return $rules;
@ -69,7 +68,9 @@ class UpdateBankTransactionRequest extends Request
if(array_key_exists('ninja_category_id', $input) && strlen($input['ninja_category_id']) > 1) if(array_key_exists('ninja_category_id', $input) && strlen($input['ninja_category_id']) > 1)
$input['ninja_category_id'] = $this->decodePrimaryKey($input['ninja_category_id']); $input['ninja_category_id'] = $this->decodePrimaryKey($input['ninja_category_id']);
if(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1) if(array_key_exists('bank_integration_id', $input) && $input['bank_integration_id'] == "")
unset($input['bank_integration_id']);
elseif(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1)
$input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']); $input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']);
$this->replace($input); $this->replace($input);

View File

@ -15,6 +15,7 @@ use App\Http\Requests\Request;
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule; use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
use App\Models\Gateway; use App\Models\Gateway;
use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver; use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver;
use Illuminate\Validation\Rule;
class StoreCompanyGatewayRequest extends Request class StoreCompanyGatewayRequest extends Request
{ {
@ -33,7 +34,7 @@ class StoreCompanyGatewayRequest extends Request
public function rules() public function rules()
{ {
$rules = [ $rules = [
'gateway_key' => 'required|alpha_num', 'gateway_key' => ['bail', 'required','alpha_num',Rule::exists('gateways','key')],
'fees_and_limits' => new ValidCompanyGatewayFeesAndLimitsRule(), 'fees_and_limits' => new ValidCompanyGatewayFeesAndLimitsRule(),
]; ];
@ -67,4 +68,5 @@ class StoreCompanyGatewayRequest extends Request
$this->replace($input); $this->replace($input);
} }
} }

View File

@ -45,6 +45,8 @@ class StoreExpenseRequest extends Request
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id; $rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
} }
$rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
return $this->globalRules($rules); return $this->globalRules($rules);
} }
@ -54,10 +56,6 @@ class StoreExpenseRequest extends Request
$input = $this->decodePrimaryKeys($input); $input = $this->decodePrimaryKeys($input);
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
}
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) { if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
$input['currency_id'] = (string) auth()->user()->company()->settings->currency_id; $input['currency_id'] = (string) auth()->user()->company()->settings->currency_id;
} }
@ -66,7 +64,6 @@ class StoreExpenseRequest extends Request
$input['color'] = ''; $input['color'] = '';
} }
/* Ensure the project is related */ /* Ensure the project is related */
if (array_key_exists('project_id', $input) && isset($input['project_id'])) { if (array_key_exists('project_id', $input) && isset($input['project_id'])) {
$project = Project::withTrashed()->where('id', $input['project_id'])->company()->first(); $project = Project::withTrashed()->where('id', $input['project_id'])->company()->first();

View File

@ -41,6 +41,8 @@ class UpdateExpenseRequest extends Request
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id)->ignore($this->expense->id); $rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id)->ignore($this->expense->id);
} }
$rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
return $this->globalRules($rules); return $this->globalRules($rules);
} }
@ -50,10 +52,6 @@ class UpdateExpenseRequest extends Request
$input = $this->decodePrimaryKeys($input); $input = $this->decodePrimaryKeys($input);
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
}
if (array_key_exists('documents', $input)) { if (array_key_exists('documents', $input)) {
unset($input['documents']); unset($input['documents']);
} }

View File

@ -65,6 +65,7 @@ class StoreRecurringInvoiceRequest extends Request
$rules['tax_name1'] = 'bail|sometimes|string|nullable'; $rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['due_date_days'] = 'bail|sometimes|string';
return $rules; return $rules;
} }
@ -73,6 +74,10 @@ class StoreRecurringInvoiceRequest extends Request
{ {
$input = $this->all(); $input = $this->all();
if (array_key_exists('due_date_days', $input) && is_null($input['due_date_days'])){
$input['due_date_days'] = 'terms';
}
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) { if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
$input['next_send_date_client'] = $input['next_send_date']; $input['next_send_date_client'] = $input['next_send_date'];
} }

View File

@ -68,6 +68,10 @@ class UpdateRecurringInvoiceRequest extends Request
{ {
$input = $this->all(); $input = $this->all();
if (array_key_exists('due_date_days', $input) && is_null($input['due_date_days'])){
$input['due_date_days'] = 'terms';
}
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) { if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
$input['next_send_date_client'] = $input['next_send_date']; $input['next_send_date_client'] = $input['next_send_date'];
} }

View File

@ -0,0 +1,41 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\TwoFactor;
use App\Http\Requests\Request;
use Illuminate\Validation\Rule;
class EnableTwoFactorRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return true;;
}
public function rules()
{
return [
'secret' => 'bail|required|string',
'one_time_password' => 'bail|required|string',
];
}
public function prepareForValidation()
{
}
}

View File

@ -39,6 +39,6 @@ class ValidCompanyQuantity implements Rule
*/ */
public function message() public function message()
{ {
return ctrans('texts.company_limit_reached'); return ctrans('texts.company_limit_reached', ['limit' => Ninja::isSelfHost() ? 10 : auth()->user()->company()->account->hosted_company_count]);
} }
} }

View File

@ -38,7 +38,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'name' => $this->getString($data, 'client.name'), 'name' => $this->getString($data, 'client.name'),
'work_phone' => $this->getString($data, 'client.phone'), 'phone' => $this->getString($data, 'client.phone'),
'address1' => $this->getString($data, 'client.address1'), 'address1' => $this->getString($data, 'client.address1'),
'address2' => $this->getString($data, 'client.address2'), 'address2' => $this->getString($data, 'client.address2'),
'postal_code' => $this->getString($data, 'client.postal_code'), 'postal_code' => $this->getString($data, 'client.postal_code'),

View File

@ -42,7 +42,7 @@ class ClientTransformer extends BaseTransformer
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'name' => $this->getString($data, 'customer_name'), 'name' => $this->getString($data, 'customer_name'),
'number' => $this->getValueOrNull($data, 'account_number'), 'number' => $this->getValueOrNull($data, 'account_number'),
'work_phone' => $this->getString($data, 'phone'), 'phone' => $this->getString($data, 'phone'),
'website' => $this->getString($data, 'website'), 'website' => $this->getString($data, 'website'),
'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null, 'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null,
'state' => $this->getString($data, 'province/state'), 'state' => $this->getString($data, 'province/state'),

View File

@ -59,7 +59,7 @@ class BankTransformer extends BaseTransformer
if(array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'CREDIT') || strtolower($transaction['transaction.base_type']) == 'deposit')) if(array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'CREDIT') || strtolower($transaction['transaction.base_type']) == 'deposit'))
return 'CREDIT'; return 'CREDIT';
if(array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'DEBIT') || strtolower($transaction['transaction.bank_type']) == 'withdrawal')) if(array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'DEBIT') || strtolower($transaction['transaction.base_type']) == 'withdrawal'))
return 'DEBIT'; return 'DEBIT';
if(array_key_exists('transaction.category_id', $transaction)) if(array_key_exists('transaction.category_id', $transaction))

View File

@ -35,7 +35,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'client.name'), 'name' => $this->getString($data, 'client.name'),
'work_phone' => $this->getString($data, 'client.phone'), 'phone' => $this->getString($data, 'client.phone'),
'address1' => $this->getString($data, 'client.address1'), 'address1' => $this->getString($data, 'client.address1'),
'address2' => $this->getString($data, 'client.address2'), 'address2' => $this->getString($data, 'client.address2'),
'city' => $this->getString($data, 'client.city'), 'city' => $this->getString($data, 'client.city'),

View File

@ -37,7 +37,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'client.name'), 'name' => $this->getString($data, 'client.name'),
'work_phone' => $this->getString($data, 'client.phone'), 'phone' => $this->getString($data, 'client.phone'),
'address1' => $this->getString($data, 'client.address1'), 'address1' => $this->getString($data, 'client.address1'),
'address2' => $this->getString($data, 'client.address2'), 'address2' => $this->getString($data, 'client.address2'),
'postal_code' => $this->getString($data, 'client.postal_code'), 'postal_code' => $this->getString($data, 'client.postal_code'),

View File

@ -34,7 +34,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'Organization'), 'name' => $this->getString($data, 'Organization'),
'work_phone' => $this->getString($data, 'Phone'), 'phone' => $this->getString($data, 'Phone'),
'address1' => $this->getString($data, 'Street'), 'address1' => $this->getString($data, 'Street'),
'city' => $this->getString($data, 'City'), 'city' => $this->getString($data, 'City'),
'state' => $this->getString($data, 'Province/State'), 'state' => $this->getString($data, 'Province/State'),

View File

@ -34,7 +34,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'Client Name'), 'name' => $this->getString($data, 'Client Name'),
'work_phone' => $this->getString($data, 'Phone'), 'phone' => $this->getString($data, 'Phone'),
'country_id' => isset($data['Country']) ? $this->getCountryIdBy2($data['Country']) : null, 'country_id' => isset($data['Country']) ? $this->getCountryIdBy2($data['Country']) : null,
'credit_balance' => 0, 'credit_balance' => 0,
'settings' => new \stdClass, 'settings' => new \stdClass,

View File

@ -42,7 +42,7 @@ class ClientTransformer extends BaseTransformer
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'customer_name'), 'name' => $this->getString($data, 'customer_name'),
'number' => $this->getString($data, 'account_number'), 'number' => $this->getString($data, 'account_number'),
'work_phone' => $this->getString($data, 'phone'), 'phone' => $this->getString($data, 'phone'),
'website' => $this->getString($data, 'website'), 'website' => $this->getString($data, 'website'),
'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null, 'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null,
'state' => $this->getString($data, 'province/state'), 'state' => $this->getString($data, 'province/state'),

View File

@ -41,7 +41,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'Company Name'), 'name' => $this->getString($data, 'Company Name'),
'work_phone' => $this->getString($data, 'Phone'), 'phone' => $this->getString($data, 'Phone'),
'private_notes' => $this->getString($data, 'Notes'), 'private_notes' => $this->getString($data, 'Notes'),
'website' => $this->getString($data, 'Website'), 'website' => $this->getString($data, 'Website'),
'id_number' => $this->getString($data, 'Customer ID'), 'id_number' => $this->getString($data, 'Customer ID'),

View File

@ -46,7 +46,7 @@ class InvoiceTransformer extends BaseTransformer
'due_date' => isset($invoice_data['Due Date']) ? date('Y-m-d', strtotime($invoice_data['Due Date'])) : null, 'due_date' => isset($invoice_data['Due Date']) ? date('Y-m-d', strtotime($invoice_data['Due Date'])) : null,
'po_number' => $this->getString($invoice_data, 'PurchaseOrder'), 'po_number' => $this->getString($invoice_data, 'PurchaseOrder'),
'public_notes' => $this->getString($invoice_data, 'Notes'), 'public_notes' => $this->getString($invoice_data, 'Notes'),
'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'), // 'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'),
'amount' => $this->getFloat($invoice_data, 'Total'), 'amount' => $this->getFloat($invoice_data, 'Total'),
'balance' => $this->getFloat($invoice_data, 'Balance'), 'balance' => $this->getFloat($invoice_data, 'Balance'),
'status_id' => $invoiceStatusMap[$status = 'status_id' => $invoiceStatusMap[$status =

View File

@ -23,6 +23,7 @@ use App\Models\BankIntegration;
use App\Models\BankTransaction; use App\Models\BankTransaction;
use App\Models\Company; use App\Models\Company;
use App\Models\Currency; use App\Models\Currency;
use App\Models\Expense;
use App\Models\ExpenseCategory; use App\Models\ExpenseCategory;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
@ -112,6 +113,10 @@ class MatchBankTransactions implements ShouldQueue
{ {
if(array_key_exists('invoice_ids', $input) && strlen($input['invoice_ids']) > 1) if(array_key_exists('invoice_ids', $input) && strlen($input['invoice_ids']) > 1)
$this->matchInvoicePayment($input); $this->matchInvoicePayment($input);
elseif(array_key_exists('payment_id', $input) && strlen($input['payment_id']) > 1)
$this->linkPayment($input);
elseif(array_key_exists('expense_id', $input) && strlen($input['expense_id']) > 1)
$this->linkExpense($input);
else else
$this->matchExpense($input); $this->matchExpense($input);
} }
@ -156,6 +161,55 @@ class MatchBankTransactions implements ShouldQueue
} }
private function linkExpense($input)
{
$this->bt = BankTransaction::find($input['id']);
if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
return $this;
$expense = Expense::withTrashed()->find($input['expense_id']);
if($expense && !$expense->transaction_id) {
$expense->transaction_id = $this->bt->id;
$expense->save();
$this->bt->expense_id = $expense->id;
$this->bt->status_id = BankTransaction::STATUS_CONVERTED;
$this->bt->vendor_id = $expense->vendor_id;
$this->bt->ninja_category_id = $expense->category_id;
$this->bt->save();
}
}
private function linkPayment($input)
{
$this->bt = BankTransaction::find($input['id']);
if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
return $this;
$payment = Payment::withTrashed()->find($input['payment_id']);
if($payment && !$payment->transaction_id) {
$payment->transaction_id = $this->bt->id;
$payment->save();
$this->bt->payment_id = $payment->id;
$this->bt->status_id = BankTransaction::STATUS_CONVERTED;
$this->bt->invoice_ids = collect($payment->invoices)->pluck('hashed_id')->implode(',');
$this->bt->save();
}
}
private function matchInvoicePayment($input) :self private function matchInvoicePayment($input) :self
{ {
$this->bt = BankTransaction::find($input['id']); $this->bt = BankTransaction::find($input['id']);
@ -266,7 +320,7 @@ class MatchBankTransactions implements ShouldQueue
/* Create Payment */ /* Create Payment */
$payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id); $payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id);
$payment->amount = $amount; $payment->amount = $this->bt->amount;
$payment->applied = $this->applied_amount; $payment->applied = $this->applied_amount;
$payment->status_id = Payment::STATUS_COMPLETED; $payment->status_id = Payment::STATUS_COMPLETED;
$payment->client_id = $this->invoice->client_id; $payment->client_id = $this->invoice->client_id;
@ -315,7 +369,7 @@ class MatchBankTransactions implements ShouldQueue
$this->invoice $this->invoice
->client ->client
->service() ->service()
->updateBalanceAndPaidToDate($amount*-1, $amount) ->updateBalanceAndPaidToDate($this->applied_amount*-1, $amount)
->save(); ->save();
$this->invoice = $this->invoice $this->invoice = $this->invoice

View File

@ -36,6 +36,8 @@ use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use function Amp\call;
class CompanyExport implements ShouldQueue class CompanyExport implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
@ -531,6 +533,15 @@ class CompanyExport implements ShouldQueue
$path = 'backups'; $path = 'backups';
Storage::makeDirectory(public_path('storage/backups/'));
try {
mkdir(public_path('storage/backups/'));
}
catch(\Exception $e) {
nlog("could not create directory");
}
$zip_path = public_path('storage/backups/'.$file_name); $zip_path = public_path('storage/backups/'.$file_name);
$zip = new \ZipArchive(); $zip = new \ZipArchive();

View File

@ -62,8 +62,15 @@ class AutoBillCron
nlog($auto_bill_partial_invoices->count().' partial invoices to auto bill'); nlog($auto_bill_partial_invoices->count().' partial invoices to auto bill');
$auto_bill_partial_invoices->cursor()->each(function ($invoice) { $auto_bill_partial_invoices->chunk(400, function ($invoices) {
AutoBill::dispatch($invoice->id, false);
foreach($invoices as $invoice)
{
AutoBill::dispatch($invoice->id, false);
}
sleep(2);
}); });
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now()) $auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
@ -79,8 +86,15 @@ class AutoBillCron
nlog($auto_bill_invoices->count().' full invoices to auto bill'); nlog($auto_bill_invoices->count().' full invoices to auto bill');
$auto_bill_invoices->cursor()->each(function ($invoice) { $auto_bill_invoices->chunk(400, function ($invoices) {
AutoBill::dispatch($invoice->id, false);
foreach($invoices as $invoice)
{
AutoBill::dispatch($invoice->id, false);
}
sleep(2);
}); });
} else { } else {
//multiDB environment, need to //multiDB environment, need to
@ -100,8 +114,14 @@ class AutoBillCron
nlog($auto_bill_partial_invoices->count()." partial invoices to auto bill db = {$db}"); nlog($auto_bill_partial_invoices->count()." partial invoices to auto bill db = {$db}");
$auto_bill_partial_invoices->cursor()->each(function ($invoice) use ($db) { $auto_bill_partial_invoices->chunk(400, function ($invoices) use($db){
AutoBill::dispatch($invoice->id, $db);
foreach($invoices as $invoice)
{
AutoBill::dispatch($invoice->id, $db);
}
sleep(2);
}); });
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now()) $auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
@ -117,10 +137,15 @@ class AutoBillCron
nlog($auto_bill_invoices->count()." full invoices to auto bill db = {$db}"); nlog($auto_bill_invoices->count()." full invoices to auto bill db = {$db}");
$auto_bill_invoices->cursor()->each(function ($invoice) use ($db) { $auto_bill_invoices->chunk(400, function ($invoices) use($db){
nlog($this->counter);
AutoBill::dispatch($invoice->id, $db); foreach($invoices as $invoice)
$this->counter++; {
AutoBill::dispatch($invoice->id, $db);
}
sleep(2);
}); });
} }

View File

@ -46,39 +46,62 @@ class RecurringExpensesCron
nlog('Sending recurring expenses '.Carbon::now()->format('Y-m-d h:i:s')); nlog('Sending recurring expenses '.Carbon::now()->format('Y-m-d h:i:s'));
if (! config('ninja.db.multi_db_enabled')) { if (! config('ninja.db.multi_db_enabled')) {
$this->getRecurringExpenses();
$recurring_expenses = RecurringExpense::where('next_send_date', '<=', now()->toDateTimeString())
->whereNotNull('next_send_date')
->whereNull('deleted_at')
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
->where('remaining_cycles', '!=', '0')
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->with('company')
->cursor();
nlog(now()->format('Y-m-d').' Generating Recurring Expenses. Count = '.$recurring_expenses->count());
$recurring_expenses->each(function ($recurring_expense, $key) {
nlog('Current date = '.now()->format('Y-m-d').' Recurring date = '.$recurring_expense->next_send_date);
if (! $recurring_expense->company->is_disabled) {
$this->generateExpense($recurring_expense);
}
});
} else { } else {
//multiDB environment, need to //multiDB environment, need to
foreach (MultiDB::$dbs as $db) { foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db); MultiDB::setDB($db);
$this->getRecurringExpenses(); $recurring_expenses = RecurringExpense::where('next_send_date', '<=', now()->toDateTimeString())
->whereNotNull('next_send_date')
->whereNull('deleted_at')
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
->where('remaining_cycles', '!=', '0')
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->with('company')
->cursor();
nlog(now()->format('Y-m-d').' Generating Recurring Expenses. Count = '.$recurring_expenses->count());
$recurring_expenses->each(function ($recurring_expense, $key) {
nlog('Current date = '.now()->format('Y-m-d').' Recurring date = '.$recurring_expense->next_send_date);
if (! $recurring_expense->company->is_disabled) {
$this->generateExpense($recurring_expense);
}
});
} }
} }
} }
private function getRecurringExpenses() private function getRecurringExpenses()
{ {
$recurring_expenses = RecurringExpense::where('next_send_date', '<=', now()->toDateTimeString()) //extracting this back to the if/else block to test duplicate crons
->whereNotNull('next_send_date')
->whereNull('deleted_at')
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
->where('remaining_cycles', '!=', '0')
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->with('company')
->cursor();
nlog(now()->format('Y-m-d').' Generating Recurring Expenses. Count = '.$recurring_expenses->count());
$recurring_expenses->each(function ($recurring_expense, $key) {
nlog('Current date = '.now()->format('Y-m-d').' Recurring date = '.$recurring_expense->next_send_date);
if (! $recurring_expense->company->is_disabled) {
$this->generateExpense($recurring_expense);
}
});
} }
private function generateExpense(RecurringExpense $recurring_expense) private function generateExpense(RecurringExpense $recurring_expense)

View File

@ -41,44 +41,62 @@ class SubscriptionCron
nlog('Subscription Cron'); nlog('Subscription Cron');
if (! config('ninja.db.multi_db_enabled')) { if (! config('ninja.db.multi_db_enabled')) {
$this->loopSubscriptions();
$invoices = Invoice::where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0)
->whereDate('due_date', '<=', now()->addDay()->startOfDay())
->whereNull('deleted_at')
->whereNotNull('subscription_id')
->cursor();
$invoices->each(function ($invoice) {
$subscription = $invoice->subscription;
$body = [
'context' => 'plan_expired',
'client' => $invoice->client->hashed_id,
'invoice' => $invoice->hashed_id,
'subscription' => $subscription->hashed_id,
];
$this->sendLoad($subscription, $body);
//This will send the notification daily.
//We'll need to handle this by performing some action on the invoice to either archive it or delete it?
});
} else { } else {
//multiDB environment, need to //multiDB environment, need to
foreach (MultiDB::$dbs as $db) { foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db); MultiDB::setDB($db);
$this->loopSubscriptions(); $invoices = Invoice::where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0)
->whereDate('due_date', '<=', now()->addDay()->startOfDay())
->whereNull('deleted_at')
->whereNotNull('subscription_id')
->cursor();
$invoices->each(function ($invoice) {
$subscription = $invoice->subscription;
$body = [
'context' => 'plan_expired',
'client' => $invoice->client->hashed_id,
'invoice' => $invoice->hashed_id,
'subscription' => $subscription->hashed_id,
];
$this->sendLoad($subscription, $body);
//This will send the notification daily.
//We'll need to handle this by performing some action on the invoice to either archive it or delete it?
});
} }
} }
} }
private function loopSubscriptions()
{
$invoices = Invoice::where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0)
->whereDate('due_date', '<=', now()->addDay()->startOfDay())
->whereNull('deleted_at')
->whereNotNull('subscription_id')
->cursor();
$invoices->each(function ($invoice) {
$subscription = $invoice->subscription;
$body = [
'context' => 'plan_expired',
'client' => $invoice->client->hashed_id,
'invoice' => $invoice->hashed_id,
'subscription' => $subscription->hashed_id,
];
$this->sendLoad($subscription, $body);
//This will send the notification daily.
//We'll need to handle this by performing some action on the invoice to either archive it or delete it?
});
}
private function handleWebhook($invoice, $subscription)
{
}
} }

View File

@ -102,6 +102,10 @@ class CreateRawPdf implements ShouldQueue
/* Set customized translations _NOW_ */ /* Set customized translations _NOW_ */
$t->replace(Ninja::transformTranslations($this->entity->client->getMergedSettings())); $t->replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
return (new Phantom)->generate($this->invitation, true);
}
$entity_design_id = ''; $entity_design_id = '';
if ($this->entity instanceof Invoice) { if ($this->entity instanceof Invoice) {

View File

@ -90,7 +90,6 @@ class EmailEntity implements ShouldQueue
$this->template_data = $template_data; $this->template_data = $template_data;
$this->email_entity_builder = $this->resolveEmailBuilder();
} }
/** /**
@ -99,13 +98,15 @@ class EmailEntity implements ShouldQueue
* *
* @return void * @return void
*/ */
public function handle() public function handle() :void
{ {
/* Don't fire emails if the company is disabled */ /* Don't fire emails if the company is disabled */
if ($this->company->is_disabled) { if ($this->company->is_disabled) {
return true; return;
} }
$this->email_entity_builder = $this->resolveEmailBuilder();
/* Set DB */ /* Set DB */
MultiDB::setDB($this->company->db); MultiDB::setDB($this->company->db);

View File

@ -44,38 +44,30 @@ class BankTransactionSync implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
// if (! Ninja::isHosted()) {
// return;
// }
//multiDB environment, need to //multiDB environment, need to
foreach (MultiDB::$dbs as $db) { foreach (MultiDB::$dbs as $db)
{
MultiDB::setDB($db); MultiDB::setDB($db);
nlog("syncing transactions"); nlog("syncing transactions");
$this->syncTransactions(); $a = Account::with('bank_integrations')->whereNotNull('bank_integration_account_id')->cursor()->each(function ($account){
// $queue = Ninja::isHosted() ? 'bank' : 'default';
if($account->isPaid() && $account->plan == 'enterprise')
{
$account->bank_integrations()->where('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account){
ProcessBankTransactions::dispatchSync($account->bank_integration_account_id, $bank_integration);
});
}
});
} }
} }
public function syncTransactions()
{
$a = Account::with('bank_integrations')->whereNotNull('bank_integration_account_id')->cursor()->each(function ($account){
// $queue = Ninja::isHosted() ? 'bank' : 'default';
if($account->isPaid() && $account->plan == 'enterprise')
{
$account->bank_integrations()->where('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account){
ProcessBankTransactions::dispatchSync($account->bank_integration_account_id, $bank_integration);
});
}
});
}
} }

View File

@ -42,39 +42,54 @@ class CompanySizeCheck implements ShouldQueue
public function handle() public function handle()
{ {
if (! config('ninja.db.multi_db_enabled')) { if (! config('ninja.db.multi_db_enabled')) {
$this->check();
Company::where('is_large', false)->withCount(['invoices', 'clients', 'products'])->cursor()->each(function ($company) {
if ($company->invoices_count > 500 || $company->products_count > 500 || $company->clients_count > 500) {
nlog("Marking company {$company->id} as large");
$company->account->companies()->update(['is_large' => true]);
}
});
nlog("updating all client credit balances");
Client::where('updated_at', '>', now()->subDay())
->cursor()
->each(function ($client){
$client->credit_balance = $client->service()->getCreditBalance();
$client->save();
});
} else { } else {
//multiDB environment, need to //multiDB environment, need to
foreach (MultiDB::$dbs as $db) { foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db); MultiDB::setDB($db);
$this->check(); nlog("Company size check db {$db}");
Company::where('is_large', false)->withCount(['invoices', 'clients', 'products'])->cursor()->each(function ($company) {
if ($company->invoices_count > 500 || $company->products_count > 500 || $company->clients_count > 500) {
nlog("Marking company {$company->id} as large");
$company->account->companies()->update(['is_large' => true]);
}
});
nlog("updating all client credit balances");
Client::where('updated_at', '>', now()->subDay())
->cursor()
->each(function ($client){
$client->credit_balance = $client->service()->getCreditBalance();
$client->save();
});
} }
} }
} }
private function check()
{
nlog("Checking all company sizes");
Company::where('is_large', false)->withCount(['invoices', 'clients', 'products'])->cursor()->each(function ($company) {
if ($company->invoices_count > 500 || $company->products_count > 500 || $company->clients_count > 500) {
nlog("Marking company {$company->id} as large");
$company->account->companies()->update(['is_large' => true]);
}
});
nlog("updating all client credit balances");
Client::where('updated_at', '>', now()->subDay())
->cursor()
->each(function ($client){
$client->credit_balance = $client->service()->getCreditBalance();
$client->save();
});
}
} }

View File

@ -40,7 +40,7 @@ class QueueSize implements ShouldQueue
* *
* @return void * @return void
*/ */
public function handle() public function handle() :void
{ {
LightLogs::create(new QueueSizeAnalytic(Queue::size())) LightLogs::create(new QueueSizeAnalytic(Queue::size()))
->send(); ->send();

View File

@ -42,39 +42,61 @@ class QuoteCheckExpired implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
if (! config('ninja.db.multi_db_enabled')) if (! config('ninja.db.multi_db_enabled')){
return $this->checkForExpiredQuotes();
foreach (MultiDB::$dbs as $db) { Quote::query()
->where('status_id', Quote::STATUS_SENT)
->where('is_deleted', false)
->whereNull('deleted_at')
->whereNotNull('due_date')
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
// ->where('due_date', '<='. now()->toDateTimeString())
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor()
->each(function ($quote){
$this->queueExpiredQuoteNotification($quote);
});
MultiDB::setDB($db); }
else {
$this->checkForExpiredQuotes(); foreach (MultiDB::$dbs as $db)
{
MultiDB::setDB($db);
Quote::query()
->where('status_id', Quote::STATUS_SENT)
->where('is_deleted', false)
->whereNull('deleted_at')
->whereNotNull('due_date')
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
// ->where('due_date', '<='. now()->toDateTimeString())
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor()
->each(function ($quote){
$this->queueExpiredQuoteNotification($quote);
});
}
} }
} }
private function checkForExpiredQuotes() private function checkForExpiredQuotes()
{ {
Quote::query()
->where('status_id', Quote::STATUS_SENT)
->where('is_deleted', false)
->whereNull('deleted_at')
->whereNotNull('due_date')
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
// ->where('due_date', '<='. now()->toDateTimeString())
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor()
->each(function ($quote){
$this->queueExpiredQuoteNotification($quote);
});
} }
private function queueExpiredQuoteNotification(Quote $quote) private function queueExpiredQuoteNotification(Quote $quote)

View File

@ -161,6 +161,11 @@ class SendRecurring implements ShouldQueue
*/ */
private function createRecurringInvitations($invoice) :Invoice private function createRecurringInvitations($invoice) :Invoice
{ {
if($this->recurring_invoice->invitations->count() == 0) {
$this->recurring_invoice = $this->recurring_invoice->service()->createInvitations()->save();
}
$this->recurring_invoice->invitations->each(function ($recurring_invitation) use ($invoice) { $this->recurring_invoice->invitations->each(function ($recurring_invitation) use ($invoice) {
$ii = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id); $ii = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id);
$ii->key = $this->createDbHash($invoice->company->db); $ii->key = $this->createDbHash($invoice->company->db);

View File

@ -190,7 +190,7 @@ class Import implements ShouldQueue
public function middleware() public function middleware()
{ {
return [new WithoutOverlapping($this->company->company_key)]; return [new WithoutOverlapping("only_one_migration_at_a_time_ever")];
} }
/** /**

View File

@ -44,92 +44,128 @@ class ReminderJob implements ShouldQueue
* *
* @return void * @return void
*/ */
public function handle() public function handle() :void
{ {
if (! config('ninja.db.multi_db_enabled')) {
$this->processReminders(); set_time_limit(0);
if (! config('ninja.db.multi_db_enabled'))
{
nlog("Sending invoice reminders on ".now()->format('Y-m-d h:i:s'));
Invoice::query()
->where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->whereNull('deleted_at')
->where('balance', '>', 0)
->where('next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->with('invitations')->chunk(50, function ($invoices) {
foreach($invoices as $invoice)
{
$this->sendReminderForInvoice($invoice);
}
sleep(2);
});
} else { } else {
//multiDB environment, need to //multiDB environment, need to
/*
foreach (MultiDB::$dbs as $db) { foreach (MultiDB::$dbs as $db)
{
MultiDB::setDB($db); MultiDB::setDB($db);
nlog("set db {$db}");
$this->processReminders(); nlog("Sending invoice reminders on db {$db} ".now()->format('Y-m-d h:i:s'));
}
*/ Invoice::query()
//24-11-2022 fix for potential memory leak during a long running process, the second reminder may run twice ->where('is_deleted', 0)
foreach (config('ninja.dbs') as $db) { ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
MultiDB::setDB($db); ->whereNull('deleted_at')
nlog("set db {$db}"); ->where('balance', '>', 0)
$this->processReminders(); ->where('next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->with('invitations')->chunk(50, function ($invoices) {
// if ($invoice->refresh() && $invoice->isPayable()) {
foreach($invoices as $invoice)
{
$this->sendReminderForInvoice($invoice);
}
sleep(2);
});
} }
} }
} }
private function processReminders() private function sendReminderForInvoice($invoice) {
{
nlog('Sending invoice reminders '.now()->format('Y-m-d h:i:s'));
set_time_limit(0); if ($invoice->isPayable()) {
Invoice::query() //Attempts to prevent duplicates from sending
->where('is_deleted', 0) if($invoice->reminder_last_sent && Carbon::parse($invoice->reminder_last_sent)->startOfDay()->eq(now()->startOfDay())){
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) nlog("caught a duplicate reminder for invoice {$invoice->number}");
->whereNull('deleted_at') return;
->where('balance', '>', 0) }
->where('next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) {
$query->where('is_deleted', 0)
->where('deleted_at', null);
})
->whereHas('company', function ($query) {
$query->where('is_disabled', 0);
})
->with('invitations')->cursor()->each(function ($invoice) {
// if ($invoice->refresh() && $invoice->isPayable()) {
if ($invoice->isPayable()) {
//Attempts to prevent duplicates from sending $reminder_template = $invoice->calculateTemplate('invoice');
if($invoice->reminder_last_sent && Carbon::parse($invoice->reminder_last_sent)->startOfDay()->eq(now()->startOfDay())){ nlog("reminder template = {$reminder_template}");
nlog("caught a duplicate reminder for invoice {$invoice->number}"); $invoice->service()->touchReminder($reminder_template)->save();
return; $invoice = $this->calcLateFee($invoice, $reminder_template);
//20-04-2022 fixes for endless reminders - generic template naming was wrong
$enabled_reminder = 'enable_'.$reminder_template;
if ($reminder_template == 'endless_reminder') {
$enabled_reminder = 'enable_reminder_endless';
}
//check if this reminder needs to be emailed
//15-01-2022 - insert addition if block if send_reminders is definitely set
if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'endless_reminder']) &&
$invoice->client->getSetting($enabled_reminder) &&
$invoice->client->getSetting('send_reminders') &&
(Ninja::isSelfHost() || $invoice->company->account->isPaidHostedClient())) {
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
if($invitation->contact && !$invitation->contact->trashed() && $invitation->contact->email) {
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template)->delay(now()->addSeconds(3));
nlog("Firing reminder email for invoice {$invoice->number} - {$reminder_template}");
} }
$reminder_template = $invoice->calculateTemplate('invoice'); });
nlog("reminder template = {$reminder_template}");
$invoice->service()->touchReminder($reminder_template)->save();
$invoice = $this->calcLateFee($invoice, $reminder_template);
//20-04-2022 fixes for endless reminders - generic template naming was wrong if ($invoice->invitations->count() > 0) {
$enabled_reminder = 'enable_'.$reminder_template; event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
if ($reminder_template == 'endless_reminder') {
$enabled_reminder = 'enable_reminder_endless';
}
//check if this reminder needs to be emailed
//15-01-2022 - insert addition if block if send_reminders is definitely set
if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'endless_reminder']) &&
$invoice->client->getSetting($enabled_reminder) &&
$invoice->client->getSetting('send_reminders') &&
(Ninja::isSelfHost() || $invoice->company->account->isPaidHostedClient())) {
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
nlog("Firing reminder email for invoice {$invoice->number} - {$reminder_template}");
});
if ($invoice->invitations->count() > 0) {
event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
}
}
$invoice->service()->setReminder()->save();
} else {
$invoice->next_send_date = null;
$invoice->save();
} }
}
$invoice->service()->setReminder()->save();
} else {
$invoice->next_send_date = null;
$invoice->save();
}
});
} }
/** /**

View File

@ -34,19 +34,7 @@ class UpdateExchangeRates implements ShouldQueue
* *
* @return void * @return void
*/ */
public function handle() public function handle() :void
{
if (config('ninja.db.multi_db_enabled')) {
foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db);
$this->updateCurrencies();
}
} else {
$this->updateCurrencies();
}
}
private function updateCurrencies()
{ {
info('updating currencies'); info('updating currencies');
@ -56,20 +44,47 @@ class UpdateExchangeRates implements ShouldQueue
$cc_endpoint = sprintf('https://openexchangerates.org/api/latest.json?app_id=%s', config('ninja.currency_converter_api_key')); $cc_endpoint = sprintf('https://openexchangerates.org/api/latest.json?app_id=%s', config('ninja.currency_converter_api_key'));
$client = new Client(); if (config('ninja.db.multi_db_enabled')) {
$response = $client->get($cc_endpoint); foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db);
$currency_api = json_decode($response->getBody()); $client = new Client();
$response = $client->get($cc_endpoint);
/* Update all currencies */ $currency_api = json_decode($response->getBody());
Currency::all()->each(function ($currency) use ($currency_api) {
$currency->exchange_rate = $currency_api->rates->{$currency->code};
$currency->save();
});
/* Rebuild the cache */ /* Update all currencies */
$currencies = Currency::orderBy('name')->get(); Currency::all()->each(function ($currency) use ($currency_api) {
$currency->exchange_rate = $currency_api->rates->{$currency->code};
$currency->save();
});
Cache::forever('currencies', $currencies); /* Rebuild the cache */
$currencies = Currency::orderBy('name')->get();
Cache::forever('currencies', $currencies);
}
} else {
$client = new Client();
$response = $client->get($cc_endpoint);
$currency_api = json_decode($response->getBody());
/* Update all currencies */
Currency::all()->each(function ($currency) use ($currency_api) {
$currency->exchange_rate = $currency_api->rates->{$currency->code};
$currency->save();
});
/* Rebuild the cache */
$currencies = Currency::orderBy('name')->get();
Cache::forever('currencies', $currencies);
}
} }
} }

View File

@ -127,7 +127,7 @@ class CreatePurchaseOrderPdf implements ShouldQueue
$t->replace(Ninja::transformTranslations($this->company->settings)); $t->replace(Ninja::transformTranslations($this->company->settings));
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
return (new Phantom)->generate($this->invitation); return (new Phantom)->generate($this->invitation, true);
} }
$entity_design_id = ''; $entity_design_id = '';

View File

@ -40,7 +40,7 @@ class CreditEmailedNotification implements ShouldQueue
// $first_notification_sent = true; // $first_notification_sent = true;
$credit = $event->invitation->credit; $credit = $event->invitation->credit->fresh();
$credit->last_sent_date = now(); $credit->last_sent_date = now();
$credit->saveQuietly(); $credit->saveQuietly();

View File

@ -42,7 +42,7 @@ class InvoiceEmailedNotification implements ShouldQueue
$first_notification_sent = true; $first_notification_sent = true;
$invoice = $event->invitation->invoice; $invoice = $event->invitation->invoice->fresh();
$invoice->last_sent_date = now(); $invoice->last_sent_date = now();
$invoice->saveQuietly(); $invoice->saveQuietly();

View File

@ -29,8 +29,6 @@ class InvoiceFailedEmailNotification
use UserNotifies, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use UserNotifies, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $delay = 10;
public function __construct() public function __construct()
{ {
} }
@ -48,7 +46,7 @@ class InvoiceFailedEmailNotification
$first_notification_sent = true; $first_notification_sent = true;
$invoice = $event->invitation->invoice; $invoice = $event->invitation->invoice;
$invoice->update(['last_sent_date' => now()]); // $invoice->update(['last_sent_date' => now()]);
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntityFailedSendObject($event->invitation, 'invoice', $event->template, $event->message))->build()); $nmo->mailable = new NinjaMailer((new EntityFailedSendObject($event->invitation, 'invoice', $event->template, $event->message))->build());

View File

@ -42,7 +42,7 @@ class QuoteEmailedNotification implements ShouldQueue
// $first_notification_sent = true; // $first_notification_sent = true;
$quote = $event->invitation->quote; $quote = $event->invitation->quote->fresh();
$quote->last_sent_date = now(); $quote->last_sent_date = now();
$quote->saveQuietly(); $quote->saveQuietly();

View File

@ -11,6 +11,7 @@
namespace App\Mail\Engine; namespace App\Mail\Engine;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Account; use App\Models\Account;
use App\Utils\HtmlEngine; use App\Utils\HtmlEngine;
use App\Utils\Ninja; use App\Utils\Ninja;
@ -117,11 +118,17 @@ class CreditEmailEngine extends BaseEmailEngine
->setTextBody($text_body); ->setTextBody($text_body);
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
if (Ninja::isHosted()) { // if (Ninja::isHosted()) {
$this->setAttachments([$this->credit->pdf_file_path($this->invitation, 'url', true)]); // $this->setAttachments([$this->credit->pdf_file_path($this->invitation, 'url', true)]);
} else { // } else {
$this->setAttachments([$this->credit->pdf_file_path($this->invitation)]); // $this->setAttachments([$this->credit->pdf_file_path($this->invitation)]);
} // }
$pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle());
$this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->credit->numberFormatter().'.pdf']]);
} }
//attach third party documents //attach third party documents
@ -129,11 +136,11 @@ class CreditEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->credit->documents as $document) { foreach ($this->credit->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]);
} }
foreach ($this->credit->company->documents as $document) { foreach ($this->credit->company->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]);
} }
} }

View File

@ -13,6 +13,7 @@ namespace App\Mail\Engine;
use App\DataMapper\EmailTemplateDefaults; use App\DataMapper\EmailTemplateDefaults;
use App\Jobs\Entity\CreateEntityPdf; use App\Jobs\Entity\CreateEntityPdf;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Account; use App\Models\Account;
use App\Models\Expense; use App\Models\Expense;
use App\Models\Task; use App\Models\Task;
@ -126,11 +127,10 @@ class InvoiceEmailEngine extends BaseEmailEngine
->setTextBody($text_body); ->setTextBody($text_body);
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->client->getSetting('pdf_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
if (Ninja::isHosted()) {
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation, 'url', true)]); $pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle());
} else {
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation)]); $this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->invoice->numberFormatter().'.pdf']]);
}
} }
//attach third party documents //attach third party documents
@ -138,11 +138,11 @@ class InvoiceEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->invoice->documents as $document) { foreach ($this->invoice->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
foreach ($this->invoice->company->documents as $document) { foreach ($this->invoice->company->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
$line_items = $this->invoice->line_items; $line_items = $this->invoice->line_items;
@ -160,7 +160,7 @@ class InvoiceEmailEngine extends BaseEmailEngine
->cursor() ->cursor()
->each(function ($expense) { ->each(function ($expense) {
foreach ($expense->documents as $document) { foreach ($expense->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
}); });
} }
@ -176,7 +176,7 @@ class InvoiceEmailEngine extends BaseEmailEngine
->cursor() ->cursor()
->each(function ($task) { ->each(function ($task) {
foreach ($task->documents as $document) { foreach ($task->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
}); });
} }

View File

@ -12,6 +12,7 @@
namespace App\Mail\Engine; namespace App\Mail\Engine;
use App\DataMapper\EmailTemplateDefaults; use App\DataMapper\EmailTemplateDefaults;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Account; use App\Models\Account;
use App\Utils\Helpers; use App\Utils\Helpers;
use App\Utils\Ninja; use App\Utils\Ninja;
@ -89,11 +90,15 @@ class PaymentEmailEngine extends BaseEmailEngine
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->client->getSetting('pdf_email_attachment') !== false && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
$this->payment->invoices->each(function ($invoice) { $this->payment->invoices->each(function ($invoice) {
if (Ninja::isHosted()) { // if (Ninja::isHosted()) {
$this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first(), 'url', true)]); // $this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first(), 'url', true)]);
} else { // } else {
$this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first())]); // $this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first())]);
} // }
$pdf = ((new CreateRawPdf($invoice->invitations->first(), $invoice->company->db))->handle());
$this->setAttachments([['file' => base64_encode($pdf), 'name' => $invoice->numberFormatter().'.pdf']]);
}); });
} }

View File

@ -13,6 +13,7 @@ namespace App\Mail\Engine;
use App\DataMapper\EmailTemplateDefaults; use App\DataMapper\EmailTemplateDefaults;
use App\Jobs\Entity\CreateEntityPdf; use App\Jobs\Entity\CreateEntityPdf;
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
use App\Models\Account; use App\Models\Account;
use App\Models\Expense; use App\Models\Expense;
use App\Models\PurchaseOrder; use App\Models\PurchaseOrder;
@ -125,11 +126,16 @@ class PurchaseOrderEmailEngine extends BaseEmailEngine
->setTextBody($text_body); ->setTextBody($text_body);
if ($this->vendor->getSetting('pdf_email_attachment') !== false && $this->purchase_order->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->vendor->getSetting('pdf_email_attachment') !== false && $this->purchase_order->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
if (Ninja::isHosted()) { // if (Ninja::isHosted()) {
$this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation, 'url', true)]); // $this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation, 'url', true)]);
} else { // } else {
$this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation)]); // $this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation)]);
} // }
$pdf = (new CreatePurchaseOrderPdf($this->invitation))->rawPdf();
$this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->purchase_order->numberFormatter().'.pdf']]);
} }
//attach third party documents //attach third party documents
@ -138,10 +144,12 @@ class PurchaseOrderEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->purchase_order->documents as $document) { foreach ($this->purchase_order->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
// $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]);
} }
foreach ($this->purchase_order->company->documents as $document) { foreach ($this->purchase_order->company->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
// $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]);
} }
} }

View File

@ -11,6 +11,7 @@
namespace App\Mail\Engine; namespace App\Mail\Engine;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Account; use App\Models\Account;
use App\Utils\HtmlEngine; use App\Utils\HtmlEngine;
use App\Utils\Ninja; use App\Utils\Ninja;
@ -116,11 +117,15 @@ class QuoteEmailEngine extends BaseEmailEngine
->setTextBody($text_body); ->setTextBody($text_body);
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
if (Ninja::isHosted()) { // if (Ninja::isHosted()) {
$this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]); // $this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]);
} else { // } else {
$this->setAttachments([$this->quote->pdf_file_path($this->invitation)]); // $this->setAttachments([$this->quote->pdf_file_path($this->invitation)]);
} // }
$pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle());
$this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->quote->numberFormatter().'.pdf']]);
} }
//attach third party documents //attach third party documents
@ -128,11 +133,11 @@ class QuoteEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->quote->documents as $document) { foreach ($this->quote->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
foreach ($this->quote->company->documents as $document) { foreach ($this->quote->company->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
} }
} }

View File

@ -118,47 +118,14 @@ class TemplateEmail extends Mailable
'logo' => $this->company->present()->logo($settings), 'logo' => $this->company->present()->logo($settings),
]); ]);
/*In the hosted platform we need to slow things down a little for Storage to catch up.*/
if(Ninja::isHosted() && $this->invitation){
$path = false;
if($this->invitation->invoice)
$path = $this->client->invoice_filepath($this->invitation).$this->invitation->invoice->numberFormatter().'.pdf';
elseif($this->invitation->quote)
$path = $this->client->quote_filepath($this->invitation).$this->invitation->quote->numberFormatter().'.pdf';
elseif($this->invitation->credit)
$path = $this->client->credit_filepath($this->invitation).$this->invitation->credit->numberFormatter().'.pdf';
sleep(1);
if($path && !Storage::disk(config('filesystems.default'))->exists($path)){
sleep(2);
if(!Storage::disk(config('filesystems.default'))->exists($path)) {
(new CreateEntityPdf($this->invitation))->handle();
sleep(2);
}
}
}
//22-10-2022 - Performance - To improve the performance/reliability of sending emails, attaching as Data is much better, stubs in place //22-10-2022 - Performance - To improve the performance/reliability of sending emails, attaching as Data is much better, stubs in place
foreach ($this->build_email->getAttachments() as $file) { foreach ($this->build_email->getAttachments() as $file) {
if (is_string($file)) { if(array_key_exists('file', $file))
// nlog($file); $this->attachData(base64_decode($file['file']), $file['name']);
// $file_data = file_get_contents($file); else
// $this->attachData($file_data, basename($file));
$this->attach($file);
} elseif (is_array($file)) {
// nlog($file['path']);
// $file_data = file_get_contents($file['path']);
// $this->attachData($file_data, $file['name']);
$this->attach($file['path'], ['as' => $file['name'], 'mime' => null]); $this->attach($file['path'], ['as' => $file['name'], 'mime' => null]);
}
} }
if ($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {

View File

@ -110,40 +110,15 @@ class VendorTemplateEmail extends Mailable
'whitelabel' => $this->vendor->user->account->isPaid() ? true : false, 'whitelabel' => $this->vendor->user->account->isPaid() ? true : false,
'logo' => $this->company->present()->logo($settings), 'logo' => $this->company->present()->logo($settings),
]); ]);
//->withSymfonyMessage(function ($message) {
// $message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
// $message->invitation = $this->invitation;
//});
// ->tag($this->company->company_key);
if(Ninja::isHosted() && $this->invitation){
$path = false;
if($this->invitation->purchase_order)
$path = $this->vendor->purchase_order_filepath($this->invitation).$this->invitation->purchase_order->numberFormatter().'.pdf';
sleep(1);
if($path && !Storage::disk(config('filesystems.default'))->exists($path)){
sleep(2);
if(!Storage::disk(config('filesystems.default'))->exists($path)) {
(new CreatePurchaseOrderPdf($this->invitation))->handle();
sleep(2);
}
}
}
foreach ($this->build_email->getAttachments() as $file) { foreach ($this->build_email->getAttachments() as $file) {
if (is_string($file)) {
$this->attach($file); if(array_key_exists('file', $file))
} elseif (is_array($file)) { $this->attachData(base64_decode($file['file']), $file['name']);
else
$this->attach($file['path'], ['as' => $file['name'], 'mime' => null]); $this->attach($file['path'], ['as' => $file['name'], 'mime' => null]);
}
} }
return $this; return $this;

View File

@ -119,7 +119,7 @@ class CompanyPresenter extends EntityPresenter
$str .= e($country->name).'<br/>'; $str .= e($country->name).'<br/>';
} }
if ($settings->phone) { if ($settings->phone) {
$str .= ctrans('texts.work_phone').': '.e($settings->phone).'<br/>'; $str .= ctrans('texts.phone').': '.e($settings->phone).'<br/>';
} }
if ($settings->email) { if ($settings->email) {
$str .= ctrans('texts.work_email').': '.e($settings->email).'<br/>'; $str .= ctrans('texts.work_email').': '.e($settings->email).'<br/>';

View File

@ -557,6 +557,7 @@ class RecurringInvoice extends BaseModel
switch ($this->due_date_days) { switch ($this->due_date_days) {
case 'terms': case 'terms':
case '': case '':
case '0':
return $this->calculateDateFromTerms($date); return $this->calculateDateFromTerms($date);
break; break;

View File

@ -76,6 +76,9 @@ class Webhook extends BaseModel
const EVENT_DELETE_CREDIT = 29; const EVENT_DELETE_CREDIT = 29;
const EVENT_PROJECT_DELETE = 30;
public static $valid_events = [ public static $valid_events = [
self::EVENT_CREATE_CLIENT, self::EVENT_CREATE_CLIENT,
self::EVENT_CREATE_INVOICE, self::EVENT_CREATE_INVOICE,
@ -106,6 +109,7 @@ class Webhook extends BaseModel
self::EVENT_CREATE_CREDIT, self::EVENT_CREATE_CREDIT,
self::EVENT_UPDATE_CREDIT, self::EVENT_UPDATE_CREDIT,
self::EVENT_DELETE_CREDIT, self::EVENT_DELETE_CREDIT,
self::EVENT_PROJECT_DELETE
]; ];
protected $fillable = [ protected $fillable = [

View File

@ -61,7 +61,14 @@ class ProjectObserver
*/ */
public function deleted(Project $project) public function deleted(Project $project)
{ {
// //EVENT_PROJECT_DELETE
$subscriptions = Webhook::where('company_id', $project->company_id)
->where('event_id', Webhook::EVENT_PROJECT_DELETE)
->exists();
if ($subscriptions) {
WebhookHandler::dispatch(Webhook::EVENT_PROJECT_DELETE, $project, $project->company, 'client')->delay(now()->addSeconds(2));
}
} }
/** /**

View File

@ -94,8 +94,6 @@ class CreditCard implements MethodInterface
$customerRequest = $this->checkout->getCustomer(); $customerRequest = $this->checkout->getCustomer();
nlog($customerRequest);
$request = $this->bootRequest($gateway_response->token); $request = $this->bootRequest($gateway_response->token);
$request->capture = false; $request->capture = false;
$request->reference = '$1 payment for authorization.'; $request->reference = '$1 payment for authorization.';

View File

@ -34,6 +34,7 @@ use Checkout\CheckoutArgumentException;
use Checkout\CheckoutAuthorizationException; use Checkout\CheckoutAuthorizationException;
use Checkout\CheckoutDefaultSdk; use Checkout\CheckoutDefaultSdk;
use Checkout\CheckoutFourSdk; use Checkout\CheckoutFourSdk;
use Checkout\Common\Phone;
use Checkout\Customers\CustomerRequest; use Checkout\Customers\CustomerRequest;
use Checkout\Customers\Four\CustomerRequest as FourCustomerRequest; use Checkout\Customers\Four\CustomerRequest as FourCustomerRequest;
use Checkout\Environment; use Checkout\Environment;
@ -300,9 +301,13 @@ class CheckoutComPaymentDriver extends BaseDriver
$request = new CustomerRequest(); $request = new CustomerRequest();
} }
$request->email = $this->client->present()->email(); $phone = new Phone();
$request->name = $this->client->present()->name(); // $phone->number = $this->client->present()->phone();
$request->phone = $this->client->present()->phone(); $phone->number = substr(str_pad($this->client->present()->phone(),6, "0", STR_PAD_RIGHT), 0 , 24);
$request->email = $this->client->present()->email();
$request->name = $this->client->present()->name();
$request->phone = $phone;
try { try {
$response = $this->gateway->getCustomersClient()->create($request); $response = $this->gateway->getCustomersClient()->create($request);

View File

@ -36,6 +36,7 @@ use Stripe\Exception\CardException;
use Stripe\Exception\InvalidRequestException; use Stripe\Exception\InvalidRequestException;
use Stripe\Exception\RateLimitException; use Stripe\Exception\RateLimitException;
use Stripe\PaymentIntent; use Stripe\PaymentIntent;
use App\Utils\Number;
class ACH class ACH
{ {
@ -172,9 +173,9 @@ class ACH
->first(); ->first();
if ($invoice) { if ($invoice) {
$description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
} else { } else {
$description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; $description = ctrans('texts.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
} }
@ -210,9 +211,9 @@ class ACH
->first(); ->first();
if ($invoice) { if ($invoice) {
$description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
} else { } else {
$description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; $description = ctrans('texts.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
} }
if (substr($cgt->token, 0, 2) === 'pm') { if (substr($cgt->token, 0, 2) === 'pm') {
@ -331,7 +332,7 @@ class ACH
$data = [ $data = [
'gateway_type_id' => $cgt->gateway_type_id, 'gateway_type_id' => $cgt->gateway_type_id,
'payment_type' => PaymentType::ACH, 'payment_type' => PaymentType::ACH,
'transaction_reference' => $response->charges->data[0]->id, 'transaction_reference' => isset($response->latest_charge) ? $response->latest_charge : $response->charges->data[0]->id,
'amount' => $amount, 'amount' => $amount,
]; ];
@ -454,9 +455,9 @@ class ACH
->first(); ->first();
if ($invoice) { if ($invoice) {
$description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
} else { } else {
$description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; $description = ctrans('texts.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
} }
if (substr($source->token, 0, 2) === 'pm') { if (substr($source->token, 0, 2) === 'pm') {

View File

@ -135,7 +135,7 @@ class BrowserPay implements MethodInterface
'payment_method' => $gateway_response->payment_method, 'payment_method' => $gateway_response->payment_method,
'payment_type' => PaymentType::parseCardType(strtolower($payment_method->card->brand)), 'payment_type' => PaymentType::parseCardType(strtolower($payment_method->card->brand)),
'amount' => $this->stripe->convertFromStripeAmount($gateway_response->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), 'amount' => $this->stripe->convertFromStripeAmount($gateway_response->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
'transaction_reference' => optional($payment_intent->charges->data[0])->id, 'transaction_reference' => isset($payment_intent->latest_charge) ? $payment_intent->latest_charge : $payment_intent->charges->data[0]->id,
'gateway_type_id' => GatewayType::APPLE_PAY, 'gateway_type_id' => GatewayType::APPLE_PAY,
]; ];

View File

@ -32,6 +32,7 @@ use Stripe\Exception\CardException;
use Stripe\Exception\InvalidRequestException; use Stripe\Exception\InvalidRequestException;
use Stripe\Exception\RateLimitException; use Stripe\Exception\RateLimitException;
use Stripe\StripeClient; use Stripe\StripeClient;
use App\Utils\Number;
class Charge class Charge
{ {
@ -62,9 +63,9 @@ class Charge
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first(); $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
if ($invoice) { if ($invoice) {
$description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
} else { } else {
$description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; $description = ctrans('texts.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
} }
$this->stripe->init(); $this->stripe->init();
@ -90,6 +91,11 @@ class Charge
$data['payment_method_types'] = ['sepa_debit']; $data['payment_method_types'] = ['sepa_debit'];
} }
/* Should improve token billing with client not present */
if (!auth()->guard('contact')->check()) {
$data['off_session'] = true;
}
$response = $this->stripe->createPaymentIntent($data, $this->stripe->stripe_connect_auth); $response = $this->stripe->createPaymentIntent($data, $this->stripe->stripe_connect_auth);
SystemLogger::dispatch($response, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client, $this->stripe->client->company); SystemLogger::dispatch($response, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client, $this->stripe->client->company);
@ -141,20 +147,27 @@ class Charge
$payment_method_type = PaymentType::SEPA; $payment_method_type = PaymentType::SEPA;
$status = Payment::STATUS_PENDING; $status = Payment::STATUS_PENDING;
} else { } else {
$payment_method_type = $response->charges->data[0]->payment_method_details->card->brand;
if(isset($response->latest_charge)) {
$charge = \Stripe\Charge::retrieve($response->latest_charge, $this->stripe->stripe_connect_auth);
$payment_method_type = $charge->payment_method_details->card->brand;
}
elseif(isset($response->charges->data[0]->payment_method_details->card->brand))
$payment_method_type = $response->charges->data[0]->payment_method_details->card->brand;
else
$payment_method_type = 'visa';
$status = Payment::STATUS_COMPLETED; $status = Payment::STATUS_COMPLETED;
} }
if($response?->status == 'processing'){ if(!in_array($response?->status, ['succeeded', 'processing'])){
//allows us to jump over the next stage - used for SEPA
}elseif($response?->status != 'succeeded'){
$this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400)); $this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400));
} }
$data = [ $data = [
'gateway_type_id' => $cgt->gateway_type_id, 'gateway_type_id' => $cgt->gateway_type_id,
'payment_type' => $this->transformPaymentTypeToConstant($payment_method_type), 'payment_type' => $this->transformPaymentTypeToConstant($payment_method_type),
'transaction_reference' => $response->charges->data[0]->id, 'transaction_reference' => isset($response->latest_charge) ? $response->latest_charge : $response->charges->data[0]->id,
'amount' => $amount, 'amount' => $amount,
]; ];
@ -162,6 +175,7 @@ class Charge
$payment->meta = $cgt->meta; $payment->meta = $cgt->meta;
$payment->save(); $payment->save();
$payment_hash->data = array_merge((array) $payment_hash->data, ['payment_intent' => $response, 'amount_with_fee' => $amount]);
$payment_hash->payment_id = $payment->id; $payment_hash->payment_id = $payment->id;
$payment_hash->save(); $payment_hash->save();

View File

@ -23,6 +23,7 @@ use App\PaymentDrivers\StripePaymentDriver;
use App\PaymentDrivers\Stripe\Jobs\UpdateCustomer; use App\PaymentDrivers\Stripe\Jobs\UpdateCustomer;
use Stripe\PaymentIntent; use Stripe\PaymentIntent;
use Stripe\PaymentMethod; use Stripe\PaymentMethod;
use App\Utils\Number;
class CreditCard class CreditCard
{ {
@ -62,7 +63,7 @@ class CreditCard
// $description = $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')) . " for client {$this->stripe->client->present()->name()}"; // $description = $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')) . " for client {$this->stripe->client->present()->name()}";
$invoice_numbers = collect($data['invoices'])->pluck('invoice_number')->implode(','); $invoice_numbers = collect($data['invoices'])->pluck('invoice_number')->implode(',');
$description = "Invoices: {$invoice_numbers} for {$data['total']['amount_with_fee']} for client {$this->stripe->client->present()->name()}"; $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice_numbers, 'amount' => Number::formatMoney($data['total']['amount_with_fee'], $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
$payment_intent_data = [ $payment_intent_data = [
'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()), 'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()),

View File

@ -26,6 +26,7 @@ use App\PaymentDrivers\StripePaymentDriver;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Database\QueryException;
use Stripe\Customer; use Stripe\Customer;
use Stripe\PaymentMethod; use Stripe\PaymentMethod;
@ -37,6 +38,8 @@ class ImportCustomers
/** @var StripePaymentDriver */ /** @var StripePaymentDriver */
public $stripe; public $stripe;
private bool $completed = true;
public $update_payment_methods; public $update_payment_methods;
public function __construct(StripePaymentDriver $stripe) public function __construct(StripePaymentDriver $stripe)
@ -64,9 +67,14 @@ class ImportCustomers
} }
//handle //handle
if(is_array($customers->data) && end($customers->data) && array_key_exists('id', end($customers->data))) // if(is_array($customers->data) && end($customers->data) && array_key_exists('id', end($customers->data)))
$starting_after = end($customers->data)['id']; // $starting_after = end($customers->data)['id'];
else // else
// break;
$starting_after = isset(end($customers->data)['id']) ? end($customers->data)['id'] : false;
if(!$starting_after)
break; break;
} while ($customers->has_more); } while ($customers->has_more);
@ -132,10 +140,30 @@ class ImportCustomers
$client->name = $customer->name ? $customer->name : $customer->email; $client->name = $customer->name ? $customer->name : $customer->email;
if (! isset($client->number) || empty($client->number)) { if (! isset($client->number) || empty($client->number)) {
$client->number = $this->getNextClientNumber($client);
}
$client->save(); $x = 1;
do {
try {
$client->number = $this->getNextClientNumber($client);
$client->saveQuietly();
$this->completed = false;
} catch (QueryException $e) {
$x++;
if ($x > 10) {
$this->completed = false;
}
}
} while ($this->completed);
}
else{
$client->save();
}
$contact = ClientContactFactory::create($client->company_id, $client->user_id); $contact = ClientContactFactory::create($client->company_id, $client->user_id);
$contact->client_id = $client->id; $contact->client_id = $client->id;

View File

@ -66,11 +66,9 @@ class PaymentIntentWebhook implements ShouldQueue
{ {
$payment = Payment::query() $payment = Payment::query()
->where('company_id', $company->id) ->where('company_id', $company->id)
->where(function ($query) use ($transaction) { ->where('transaction_reference', $transaction['payment_intent'])
$query->where('transaction_reference', $transaction['payment_intent'])
->orWhere('transaction_reference', $transaction['id']);
})
->first(); ->first();
} }
else else
{ {
@ -138,6 +136,9 @@ class PaymentIntentWebhook implements ShouldQueue
$hash = isset($charge['metadata']['payment_hash']) ? $charge['metadata']['payment_hash'] : false; $hash = isset($charge['metadata']['payment_hash']) ? $charge['metadata']['payment_hash'] : false;
if(!$hash)
return;
$payment_hash = PaymentHash::where('hash', $hash)->first(); $payment_hash = PaymentHash::where('hash', $hash)->first();
if(!$payment_hash) if(!$payment_hash)
@ -264,6 +265,39 @@ class PaymentIntentWebhook implements ShouldQueue
} }
} }
// private function updateSepaPayment($payment_hash, $client, $meta)
// {
// $company_gateway = CompanyGateway::find($this->company_gateway_id);
// $payment_method_type = GatewayType::SEPA;
// $driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type);
// $payment_hash->data = array_merge((array) $payment_hash->data, $this->stripe_request);
// $payment_hash->save();
// $driver->setPaymentHash($payment_hash);
// $data = [
// 'payment_method' => $payment_hash->data->object->payment_method,
// 'payment_type' => PaymentType::parseCardType(strtolower($meta['card_details'])) ?: PaymentType::CREDIT_CARD_OTHER,
// 'amount' => $payment_hash->data->amount_with_fee,
// 'transaction_reference' => $meta['transaction_reference'],
// 'gateway_type_id' => GatewayType::CREDIT_CARD,
// ];
// $payment = $driver->createPayment($data, Payment::STATUS_COMPLETED);
// SystemLogger::dispatch(
// ['response' => $this->stripe_request, 'data' => $data],
// SystemLog::CATEGORY_GATEWAY_RESPONSE,
// SystemLog::EVENT_GATEWAY_SUCCESS,
// SystemLog::TYPE_STRIPE,
// $client,
// $client->company,
// );
// }
private function updateCreditCardPayment($payment_hash, $client, $meta) private function updateCreditCardPayment($payment_hash, $client, $meta)
{ {

View File

@ -84,7 +84,7 @@ class SEPA
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $request->all()); $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $request->all());
$this->stripe->payment_hash->save(); $this->stripe->payment_hash->save();
if (property_exists($gateway_response, 'status') && ($gateway_response->status == 'processing' || $gateway_response->status === 'succeeded')) { if (property_exists($gateway_response, 'status') && ($gateway_response->status == 'processing' || $gateway_response->status == 'succeeded')) {
if ($request->store_card) { if ($request->store_card) {
$this->storePaymentMethod($gateway_response); $this->storePaymentMethod($gateway_response);
} }

View File

@ -660,14 +660,22 @@ class StripePaymentDriver extends BaseDriver
], $this->stripe_connect_auth); ], $this->stripe_connect_auth);
if ($charge->captured) { if ($charge->captured) {
$payment = Payment::query()
->where('transaction_reference', $transaction['payment_intent']) $payment = false;
->where('company_id', $request->getCompany()->id)
->where(function ($query) use ($transaction) { if(isset($transaction['payment_intent']))
$query->where('transaction_reference', $transaction['payment_intent']) {
->orWhere('transaction_reference', $transaction['id']); $payment = Payment::query()
}) ->where('transaction_reference', $transaction['payment_intent'])
->first(); ->where('company_id', $request->getCompany()->id)
->first();
}
elseif(isset($transaction['id'])) {
$payment = Payment::query()
->where('transaction_reference', $transaction['id'])
->where('company_id', $request->getCompany()->id)
->first();
}
if ($payment) { if ($payment) {
$payment->status_id = Payment::STATUS_COMPLETED; $payment->status_id = Payment::STATUS_COMPLETED;

View File

@ -56,6 +56,8 @@ class ClientRepository extends BaseRepository
*/ */
public function save(array $data, Client $client) : ?Client public function save(array $data, Client $client) : ?Client
{ {
$contact_data = $data;
unset($data['contacts']);
/* When uploading documents, only the document array is sent, so we must return early*/ /* When uploading documents, only the document array is sent, so we must return early*/
if (array_key_exists('documents', $data) && count($data['documents']) >= 1) { if (array_key_exists('documents', $data) && count($data['documents']) >= 1) {
@ -67,7 +69,7 @@ class ClientRepository extends BaseRepository
$client->fill($data); $client->fill($data);
if (array_key_exists('settings', $data)) { if (array_key_exists('settings', $data)) {
$client->saveSettings($data['settings'], $client); $client->settings = $client->saveSettings($data['settings'], $client);
} }
if (! $client->country_id) { if (! $client->country_id) {
@ -75,19 +77,9 @@ class ClientRepository extends BaseRepository
$client->country_id = $company->settings->country_id; $client->country_id = $company->settings->country_id;
} }
try{ $client->save();
$client->save();
}
catch(\Exception $e) {
nlog("client save failed");
nlog($data);
}
if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) { if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) {
// $client->number = $this->getNextClientNumber($client);
// $client->save();
$x = 1; $x = 1;
@ -111,7 +103,7 @@ class ClientRepository extends BaseRepository
$data['name'] = $client->present()->name(); $data['name'] = $client->present()->name();
} }
$this->contact_repo->save($data, $client); $this->contact_repo->save($contact_data, $client);
return $client; return $client;
} }

View File

@ -31,32 +31,20 @@ use Illuminate\Support\Facades\Cache;
class BankMatchingService implements ShouldQueue class BankMatchingService implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, GeneratesCounter; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private Company $company; public function __construct(protected int $company_id, protected string $db){
$this->company_id = $company_id;
$this->db = $db;
$this->middleware_key = "bank_match_rate:{$this->company_id}";
}
private $invoices; public function handle() :void
public $deleteWhenMissingModels = true;
public function __construct(private int $company_id, private string $db){}
public function handle()
{ {
MultiDB::setDb($this->db); MultiDB::setDb($this->db);
$this->company = Company::find($this->company_id); BankTransaction::where('company_id', $this->company_id)
$this->matchTransactions();
}
private function matchTransactions()
{
BankTransaction::where('company_id', $this->company->id)
->where('status_id', BankTransaction::STATUS_UNMATCHED) ->where('status_id', BankTransaction::STATUS_UNMATCHED)
->cursor() ->cursor()
->each(function ($bt){ ->each(function ($bt){
@ -69,6 +57,6 @@ class BankMatchingService implements ShouldQueue
public function middleware() public function middleware()
{ {
return [new WithoutOverlapping($this->company_id)]; return [new WithoutOverlapping($this->middleware_key)];
} }
} }

View File

@ -33,8 +33,6 @@ class ClientService
try { try {
\DB::connection(config('database.default'))->transaction(function () use($amount) { \DB::connection(config('database.default'))->transaction(function () use($amount) {
nlog("inside transaction - updating balance by {$amount}");
$this->client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first(); $this->client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first();
$this->client->balance += $amount; $this->client->balance += $amount;
$this->client->save(); $this->client->save();

View File

@ -45,7 +45,7 @@ class SendEmail
$this->credit->invitations->each(function ($invitation) { $this->credit->invitations->each(function ($invitation) {
if (! $invitation->contact->trashed() && $invitation->contact->email) { if (! $invitation->contact->trashed() && $invitation->contact->email) {
EmailEntity::dispatch($invitation, $invitation->company, $this->reminder_template); EmailEntity::dispatch($invitation, $invitation->company, $this->reminder_template)->delay(2);
} }
}); });

View File

@ -112,8 +112,7 @@ class AddGatewayFee extends AbstractService
$this->invoice $this->invoice
->client ->client
->service() ->service()
->updateBalance($adjustment) ->updateBalance($adjustment);
->save();
$this->invoice $this->invoice
->ledger() ->ledger()

View File

@ -44,11 +44,12 @@ class HandleRestore extends AbstractService
return $this->invoice; return $this->invoice;
} }
//determine whether we need to un-delete payments OR just modify the payment amount /applied balances. //cannot restore an invoice with a deleted payment
foreach ($this->invoice->payments as $payment) { foreach ($this->invoice->payments as $payment) {
//restore the payment record
$this->invoice->restore(); if(($this->invoice->paid_to_date == 0) && $payment->is_deleted)
return $this->invoice;
} }
//adjust ledger balance //adjust ledger balance
@ -56,8 +57,7 @@ class HandleRestore extends AbstractService
$this->invoice->client $this->invoice->client
->service() ->service()
->updateBalance($this->invoice->balance) ->updateBalanceAndPaidToDate($this->invoice->balance,$this->invoice->paid_to_date)
->updatePaidToDate($this->invoice->paid_to_date)
->save(); ->save();
$this->windBackInvoiceNumber(); $this->windBackInvoiceNumber();
@ -120,11 +120,11 @@ class HandleRestore extends AbstractService
if ($this->adjustment_amount == $this->total_payments) { if ($this->adjustment_amount == $this->total_payments) {
$this->invoice->payments()->update(['payments.deleted_at' => null, 'payments.is_deleted' => false]); $this->invoice->payments()->update(['payments.deleted_at' => null, 'payments.is_deleted' => false]);
} else { }
//adjust payments down by the amount applied to the invoice payment. //adjust payments down by the amount applied to the invoice payment.
$this->invoice->payments->each(function ($payment) { $this->invoice->payments->fresh()->each(function ($payment) {
$payment_adjustment = $payment->paymentables $payment_adjustment = $payment->paymentables
->where('paymentable_type', '=', 'invoices') ->where('paymentable_type', '=', 'invoices')
->where('paymentable_id', $this->invoice->id) ->where('paymentable_id', $this->invoice->id)
@ -141,7 +141,6 @@ class HandleRestore extends AbstractService
$payment->restore(); $payment->restore();
$payment->save(); $payment->save();
}); });
}
return $this; return $this;
} }

View File

@ -300,8 +300,8 @@ class InvoiceService
} elseif ($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount) { } elseif ($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount) {
$this->setStatus(Invoice::STATUS_PARTIAL); $this->setStatus(Invoice::STATUS_PARTIAL);
} }
elseif($this->invoice->balance < 0) { elseif ($this->invoice->balance < 0 || $this->invoice->balance > 0) {
$this->setStatus(Invoice::STATUS_PARTIAL); $this->invoice->status_id = Invoice::STATUS_SENT;
} }
return $this; return $this;
@ -318,7 +318,7 @@ class InvoiceService
} elseif ($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount) { } elseif ($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount) {
$this->invoice->status_id = Invoice::STATUS_PARTIAL; $this->invoice->status_id = Invoice::STATUS_PARTIAL;
} }
elseif ($this->invoice->balance < 0) { elseif ($this->invoice->balance < 0 || $this->invoice->balance > 0) {
$this->invoice->status_id = Invoice::STATUS_SENT; $this->invoice->status_id = Invoice::STATUS_SENT;
} }

View File

@ -53,16 +53,6 @@ class MarkInvoiceDeleted extends AbstractService
->adjustPaidToDateAndBalance() ->adjustPaidToDateAndBalance()
->adjustLedger(); ->adjustLedger();
$transaction = [
'invoice' => $this->invoice->transaction_event(),
'payment' => $this->invoice->payments()->exists() ? $this->invoice->payments()->first()->transaction_event() : [],
'client' => $this->invoice->client->transaction_event(),
'credit' => [],
'metadata' => ['total_payments' => $this->total_payments, 'balance_adjustment' => $this->balance_adjustment, 'adjustment_amount' => $this->adjustment_amount],
];
// TransactionLog::dispatch(TransactionEvent::INVOICE_DELETED, $transaction, $this->invoice->company->db);
return $this->invoice; return $this->invoice;
} }
@ -87,26 +77,17 @@ class MarkInvoiceDeleted extends AbstractService
return $this; return $this;
} }
// @deprecated
private function adjustBalance()
{
// $client = $this->invoice->client->fresh();
// $client->balance += $this->balance_adjustment * -1;
// $client->save();
// $this->invoice->client->service()->updateBalance($this->balance_adjustment * -1)->save(); //reduces the client balance by the invoice amount.
return $this;
}
/* Adjust the payment amounts */ /* Adjust the payment amounts */
private function adjustPayments() private function adjustPayments()
{ {
//if total payments = adjustment amount - that means we need to delete the payments as well. //if total payments = adjustment amount - that means we need to delete the payments as well.
if ($this->adjustment_amount == $this->total_payments) { nlog($this->adjustment_amount);
nlog($this->total_payments);
if ($this->adjustment_amount == $this->total_payments)
$this->invoice->payments()->update(['payments.deleted_at' => now(), 'payments.is_deleted' => true]); $this->invoice->payments()->update(['payments.deleted_at' => now(), 'payments.is_deleted' => true]);
} else {
//adjust payments down by the amount applied to the invoice payment. //adjust payments down by the amount applied to the invoice payment.
@ -125,7 +106,7 @@ class MarkInvoiceDeleted extends AbstractService
$payment->applied -= $payment_adjustment; $payment->applied -= $payment_adjustment;
$payment->save(); $payment->save();
}); });
}
return $this; return $this;
} }

View File

@ -139,9 +139,9 @@ class UpdateReminder extends AbstractService
if ($this->invoice->last_sent_date && if ($this->invoice->last_sent_date &&
$this->settings->enable_reminder_endless && $this->settings->enable_reminder_endless &&
($this->invoice->reminder1_sent || $this->settings->schedule_reminder1 == "") && ($this->invoice->reminder1_sent || $this->settings->schedule_reminder1 == "" || !$this->settings->enable_reminder1) &&
($this->invoice->reminder2_sent || $this->settings->schedule_reminder2 == "") && ($this->invoice->reminder2_sent || $this->settings->schedule_reminder2 == "" || !$this->settings->enable_reminder2) &&
($this->invoice->reminder3_sent || $this->settings->schedule_reminder3 == "")) { ($this->invoice->reminder3_sent || $this->settings->schedule_reminder3 == "" || !$this->settings->enable_reminder3)) {
$reminder_date = $this->addTimeInterval($this->invoice->last_sent_date, (int) $this->settings->endless_reminder_frequency_id); $reminder_date = $this->addTimeInterval($this->invoice->last_sent_date, (int) $this->settings->endless_reminder_frequency_id);
if ($reminder_date) { if ($reminder_date) {

View File

@ -36,7 +36,7 @@ class LedgerService
$this->entity->company_ledger()->save($company_ledger); $this->entity->company_ledger()->save($company_ledger);
ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(300)); ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(rand(30,300)));
return $this; return $this;
} }
@ -52,7 +52,7 @@ class LedgerService
$this->entity->company_ledger()->save($company_ledger); $this->entity->company_ledger()->save($company_ledger);
ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(300)); ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(rand(30,300)));
return $this; return $this;
} }
@ -68,7 +68,7 @@ class LedgerService
$this->entity->company_ledger()->save($company_ledger); $this->entity->company_ledger()->save($company_ledger);
ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(300)); ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(rand(30,300)));
return $this; return $this;
} }

View File

@ -37,7 +37,7 @@ class SendEmail
$contact = $this->payment->client->contacts()->first(); $contact = $this->payment->client->contacts()->first();
if ($contact?->email) if ($contact?->email)
EmailPayment::dispatch($this->payment, $this->payment->company, $contact); EmailPayment::dispatch($this->payment, $this->payment->company, $contact)->delay(now()->addSeconds(3));
} }
} }

View File

@ -785,7 +785,7 @@ class SubscriptionService
*/ */
public function triggerWebhook($context) public function triggerWebhook($context)
{ {
nlog("trigger webook"); nlog("trigger webhook");
if (empty($this->subscription->webhook_configuration['post_purchase_url']) || is_null($this->subscription->webhook_configuration['post_purchase_url']) || strlen($this->subscription->webhook_configuration['post_purchase_url']) < 1) { if (empty($this->subscription->webhook_configuration['post_purchase_url']) || is_null($this->subscription->webhook_configuration['post_purchase_url']) || strlen($this->subscription->webhook_configuration['post_purchase_url']) < 1) {
return ["message" => "Success", "status_code" => 200]; return ["message" => "Success", "status_code" => 200];
@ -879,6 +879,53 @@ class SubscriptionService
} }
/* OPTIONAL PRODUCTS*/
/**
* Get the single charge products for the
* subscription
*
*/
public function optional_products()
{
if(!$this->subscription->optional_product_ids)
return collect();
$keys = $this->transformKeys(explode(",", $this->subscription->optional_product_ids));
if(is_array($keys))
return Product::whereIn('id', $keys)->get();
else
return Product::where('id', $keys)->get();
}
/**
* Get the recurring products for the
* subscription
*
*/
public function optional_recurring_products()
{
if(!$this->subscription->optional_recurring_product_ids)
return collect();
$keys = $this->transformKeys(explode(",", $this->subscription->optional_recurring_product_ids));
if(is_array($keys)){
return Product::whereIn('id', $keys)->get();
}
else{
return Product::where('id', $keys)->get();
}
}
/** /**
* Get available upgrades & downgrades for the plan. * Get available upgrades & downgrades for the plan.
* *
@ -901,6 +948,8 @@ class SubscriptionService
*/ */
public function handleCancellation(RecurringInvoice $recurring_invoice) public function handleCancellation(RecurringInvoice $recurring_invoice)
{ {
$invoice_start_date = false;
$refund_end_date = false;
//only refund if they are in the refund window. //only refund if they are in the refund window.
$outstanding_invoice = Invoice::where('subscription_id', $this->subscription->id) $outstanding_invoice = Invoice::where('subscription_id', $this->subscription->id)
@ -909,8 +958,11 @@ class SubscriptionService
->orderBy('id', 'desc') ->orderBy('id', 'desc')
->first(); ->first();
$invoice_start_date = Carbon::parse($outstanding_invoice->date); if($outstanding_invoice)
$refund_end_date = $invoice_start_date->addSeconds($this->subscription->refund_period); {
$invoice_start_date = Carbon::parse($outstanding_invoice->date);
$refund_end_date = $invoice_start_date->addSeconds($this->subscription->refund_period);
}
/* Stop the recurring invoice and archive */ /* Stop the recurring invoice and archive */
$recurring_invoice->service()->stop()->save(); $recurring_invoice->service()->stop()->save();
@ -918,7 +970,7 @@ class SubscriptionService
$recurring_invoice_repo->archive($recurring_invoice); $recurring_invoice_repo->archive($recurring_invoice);
/* Refund only if we are in the window - and there is nothing outstanding on the invoice */ /* Refund only if we are in the window - and there is nothing outstanding on the invoice */
if($refund_end_date->greaterThan(now()) && (int)$outstanding_invoice->balance == 0) if($refund_end_date && $refund_end_date->greaterThan(now()) && (int)$outstanding_invoice->balance == 0)
{ {
if($outstanding_invoice->payments()->exists()) if($outstanding_invoice->payments()->exists())

View File

@ -39,6 +39,7 @@ class BankTransactionTransformer extends EntityTransformer
'company', 'company',
'account', 'account',
'expense', 'expense',
'payment',
'vendor', 'vendor',
'bank_account', 'bank_account',
]; ];
@ -66,6 +67,7 @@ class BankTransactionTransformer extends EntityTransformer
'base_type' => (string) $bank_transaction->base_type ?: '', 'base_type' => (string) $bank_transaction->base_type ?: '',
'invoice_ids' => (string) $bank_transaction->invoice_ids ?: '', 'invoice_ids' => (string) $bank_transaction->invoice_ids ?: '',
'expense_id'=> (string) $this->encodePrimaryKey($bank_transaction->expense_id) ?: '', 'expense_id'=> (string) $this->encodePrimaryKey($bank_transaction->expense_id) ?: '',
'payment_id'=> (string) $this->encodePrimaryKey($bank_transaction->payment_id) ?: '',
'vendor_id'=> (string) $this->encodePrimaryKey($bank_transaction->vendor_id) ?: '', 'vendor_id'=> (string) $this->encodePrimaryKey($bank_transaction->vendor_id) ?: '',
'bank_transaction_rule_id' => (string) $this->encodePrimaryKey($bank_transaction->bank_transaction_rule_id) ?: '', 'bank_transaction_rule_id' => (string) $this->encodePrimaryKey($bank_transaction->bank_transaction_rule_id) ?: '',
'is_deleted' => (bool) $bank_transaction->is_deleted, 'is_deleted' => (bool) $bank_transaction->is_deleted,
@ -103,4 +105,11 @@ class BankTransactionTransformer extends EntityTransformer
return $this->includeItem($bank_transaction->vendor, $transformer, Vendor::class); return $this->includeItem($bank_transaction->vendor, $transformer, Vendor::class);
} }
public function includePayment(BankTransaction $bank_transaction)
{
$transformer = new PaymentTransformer($this->serializer);
return $this->includeItem($bank_transaction->payment, $transformer, Payment::class);
}
} }

View File

@ -106,9 +106,10 @@ class Helpers
* *
* @param string $value * @param string $value
* @param Client|Company $entity * @param Client|Company $entity
* @param null|Carbon $currentDateTime
* @return null|string * @return null|string
*/ */
public static function processReservedKeywords(?string $value, $entity): ?string public static function processReservedKeywords(?string $value, $entity, $currentDateTime = null): ?string
{ {
if (! $value) { if (! $value) {
return ''; return '';
@ -132,71 +133,75 @@ class Helpers
Carbon::setLocale($entity->locale()); Carbon::setLocale($entity->locale());
if (!$currentDateTime) {
$currentDateTime = Carbon::now();
}
$replacements = [ $replacements = [
'literal' => [ 'literal' => [
':MONTH_BEFORE' => \sprintf( ':MONTH_BEFORE' => \sprintf(
'%s %s %s', '%s %s %s',
Carbon::now()->subMonth(1)->translatedFormat($entity->date_format()), $currentDateTime->copy()->subMonth(1)->translatedFormat($entity->date_format()),
ctrans('texts.to'), ctrans('texts.to'),
Carbon::now()->subDay(1)->translatedFormat($entity->date_format()), $currentDateTime->copy()->subDay(1)->translatedFormat($entity->date_format()),
), ),
':YEAR_BEFORE' => \sprintf( ':YEAR_BEFORE' => \sprintf(
'%s %s %s', '%s %s %s',
Carbon::now()->subYear(1)->translatedFormat($entity->date_format()), $currentDateTime->copy()->subYear(1)->translatedFormat($entity->date_format()),
ctrans('texts.to'), ctrans('texts.to'),
Carbon::now()->subDay(1)->translatedFormat($entity->date_format()), $currentDateTime->copy()->subDay(1)->translatedFormat($entity->date_format()),
), ),
':MONTH_AFTER' => \sprintf( ':MONTH_AFTER' => \sprintf(
'%s %s %s', '%s %s %s',
Carbon::now()->translatedFormat($entity->date_format()), $currentDateTime->translatedFormat($entity->date_format()),
ctrans('texts.to'), ctrans('texts.to'),
Carbon::now()->addMonth(1)->subDay(1)->translatedFormat($entity->date_format()), $currentDateTime->copy()->addMonth(1)->subDay(1)->translatedFormat($entity->date_format()),
), ),
':YEAR_AFTER' => \sprintf( ':YEAR_AFTER' => \sprintf(
'%s %s %s', '%s %s %s',
Carbon::now()->translatedFormat($entity->date_format()), $currentDateTime->translatedFormat($entity->date_format()),
ctrans('texts.to'), ctrans('texts.to'),
Carbon::now()->addYear(1)->subDay(1)->translatedFormat($entity->date_format()), $currentDateTime->copy()->addYear(1)->subDay(1)->translatedFormat($entity->date_format()),
), ),
':MONTHYEAR' => \sprintf( ':MONTHYEAR' => \sprintf(
'%s %s', '%s %s',
Carbon::createFromDate(now()->month)->translatedFormat('F'), Carbon::createFromDate($currentDateTime->month)->translatedFormat('F'),
now()->year, $currentDateTime->year,
), ),
':MONTH' => Carbon::createFromDate(now()->year, now()->month)->translatedFormat('F'), ':MONTH' => Carbon::createFromDate($currentDateTime->year, $currentDateTime->month)->translatedFormat('F'),
':YEAR' => now()->year, ':YEAR' => $currentDateTime->year,
':QUARTER' => 'Q'.now()->quarter, ':QUARTER' => 'Q'.$currentDateTime->quarter,
':WEEK_BEFORE' => \sprintf( ':WEEK_BEFORE' => \sprintf(
'%s %s %s', '%s %s %s',
Carbon::now()->subDays(7)->translatedFormat($entity->date_format()), $currentDateTime->copy()->subDays(7)->translatedFormat($entity->date_format()),
ctrans('texts.to'), ctrans('texts.to'),
Carbon::now()->subDays(1)->translatedFormat($entity->date_format()) $currentDateTime->copy()->subDays(1)->translatedFormat($entity->date_format())
), ),
':WEEK_AHEAD' => \sprintf( ':WEEK_AHEAD' => \sprintf(
'%s %s %s', '%s %s %s',
Carbon::now()->addDays(7)->translatedFormat($entity->date_format()), $currentDateTime->copy()->addDays(7)->translatedFormat($entity->date_format()),
ctrans('texts.to'), ctrans('texts.to'),
Carbon::now()->addDays(13)->translatedFormat($entity->date_format()) $currentDateTime->copy()->addDays(13)->translatedFormat($entity->date_format())
), ),
':WEEK' => \sprintf( ':WEEK' => \sprintf(
'%s %s %s', '%s %s %s',
Carbon::now()->translatedFormat($entity->date_format()), $currentDateTime->translatedFormat($entity->date_format()),
ctrans('texts.to'), ctrans('texts.to'),
Carbon::now()->addDays(6)->translatedFormat($entity->date_format()) $currentDateTime->copy()->addDays(6)->translatedFormat($entity->date_format())
), ),
], ],
'raw' => [ 'raw' => [
':MONTHYEAR' => now()->month, ':MONTHYEAR' => $currentDateTime->month,
':MONTH' => now()->month, ':MONTH' => $currentDateTime->month,
':YEAR' => now()->year, ':YEAR' => $currentDateTime->year,
':QUARTER' => now()->quarter, ':QUARTER' => $currentDateTime->quarter,
], ],
'ranges' => [ 'ranges' => [
'MONTHYEAR' => Carbon::createFromDate(now()->year, now()->month), 'MONTHYEAR' => Carbon::createFromDate($currentDateTime->year, $currentDateTime->month),
], ],
'ranges_raw' => [ 'ranges_raw' => [
'MONTH' => now()->month, 'MONTH' => $currentDateTime->month,
'YEAR' => now()->year, 'YEAR' => $currentDateTime->year,
], ],
]; ];
@ -221,12 +226,12 @@ class Helpers
continue; continue;
} }
$_left = Carbon::createFromDate(now()->year, now()->month)->translatedFormat('F Y'); $_left = Carbon::createFromDate($currentDateTime->year, $currentDateTime->month)->translatedFormat('F Y');
$_right = ''; $_right = '';
// If right side doesn't have any calculations, replace with raw ranges keyword. // If right side doesn't have any calculations, replace with raw ranges keyword.
if (! Str::contains($right, ['-', '+', '/', '*'])) { if (! Str::contains($right, ['-', '+', '/', '*'])) {
$_right = Carbon::createFromDate(now()->year, now()->month)->translatedFormat('F Y'); $_right = Carbon::createFromDate($currentDateTime->year, $currentDateTime->month)->translatedFormat('F Y');
} }
// If right side contains one of math operations, calculate. // If right side contains one of math operations, calculate.
@ -237,7 +242,7 @@ class Helpers
$_value = explode($_operation, $right); // [MONTHYEAR, 4] $_value = explode($_operation, $right); // [MONTHYEAR, 4]
$_right = Carbon::createFromDate(now()->year, now()->month)->addMonths($_value[1])->translatedFormat('F Y'); $_right = Carbon::createFromDate($currentDateTime->year, $currentDateTime->month)->addMonths($_value[1])->translatedFormat('F Y');
} }
$replacement = sprintf('%s to %s', $_left, $_right); $replacement = sprintf('%s to %s', $_left, $_right);
@ -304,7 +309,7 @@ class Helpers
} }
if ($matches->keys()->first() == ':MONTHYEAR') { if ($matches->keys()->first() == ':MONTHYEAR') {
$final_date = now()->addMonths($output - now()->month); $final_date = $currentDateTime->copy()->addMonths($output - $currentDateTime->month);
$output = \sprintf( $output = \sprintf(
'%s %s', '%s %s',

View File

@ -28,6 +28,6 @@ class NinjaPdf
RequestOptions::JSON => ['html' => $html], RequestOptions::JSON => ['html' => $html],
]); ]);
return $response->getBody(); return $response->getBody()->getContents();
} }
} }

View File

@ -226,6 +226,12 @@ class HtmlEngine
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_credit')]; $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['$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()) ?: '&nbsp;', 'label' => ctrans('texts.credit_date')]; $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: '&nbsp;', 'label' => ctrans('texts.credit_date')];
$data['$credit.custom1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'credit1', $this->entity->custom_value1, $this->client) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', '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) ?: '&nbsp;', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
} }
$data['$portal_url'] = ['value' => $this->invitation->getPortalLink(), 'label' =>'']; $data['$portal_url'] = ['value' => $this->invitation->getPortalLink(), 'label' =>''];

View File

@ -42,7 +42,7 @@ class Phantom
* *
* @param $invitation * @param $invitation
*/ */
public function generate($invitation) public function generate($invitation, $return_pdf = false)
{ {
$entity = false; $entity = false;
@ -112,6 +112,9 @@ class Phantom
$instance = Storage::disk(config('filesystems.default'))->put($file_path, $pdf); $instance = Storage::disk(config('filesystems.default'))->put($file_path, $pdf);
if($return_pdf)
return $pdf;
return $file_path; return $file_path;
} }

View File

@ -30,7 +30,7 @@ trait ClientGroupSettingsSaver
* Saves a setting object. * Saves a setting object.
* *
* Works for groups|clients|companies * Works for groups|clients|companies
* @param array $settings The request input settings array * @param array|object $settings The request input settings array
* @param object $entity The entity which the settings belongs to * @param object $entity The entity which the settings belongs to
* @return void * @return void
*/ */
@ -64,19 +64,6 @@ trait ClientGroupSettingsSaver
$entity_settings->{$key} = $value; $entity_settings->{$key} = $value;
} }
$entity->settings = $entity_settings;
try{
$entity->save();
}
catch(\Exception $e){
nlog("client settings failure");
nlog($entity_settings);
nlog($e->getMessage());
}
return $entity_settings; return $entity_settings;
} }

Some files were not shown because too many files have changed in this diff Show More