diff --git a/app/Http/Controllers/PreviewController.php b/app/Http/Controllers/PreviewController.php
index 8861ca321aad..ba81318fcf5a 100644
--- a/app/Http/Controllers/PreviewController.php
+++ b/app/Http/Controllers/PreviewController.php
@@ -218,7 +218,7 @@ class PreviewController extends BaseController
/* Catch all in case migration doesn't pass back a valid design */
if(!$design)
- $design = Design::find(2);
+ $design = \App\Models\Design::find(2);
if ($design->is_custom) {
$options = [
diff --git a/app/Http/Requests/Payments/PaymentWebhookRequest.php b/app/Http/Requests/Payments/PaymentWebhookRequest.php
index ea11d0af2056..50bc276c14ae 100644
--- a/app/Http/Requests/Payments/PaymentWebhookRequest.php
+++ b/app/Http/Requests/Payments/PaymentWebhookRequest.php
@@ -74,9 +74,9 @@ class PaymentWebhookRequest extends Request
{
// For testing purposes we'll slow down the webhook processing by 2 seconds
// to make sure webhook request doesn't came before our processing.
- if (app()->environment() !== 'production') {
+ //if (app()->environment() !== 'production') {
sleep(2);
- }
+ //}
// Some gateways, like Checkout, we can dynamically pass payment hash,
// which we will resolve here and get payment information from it.
diff --git a/app/Jobs/Entity/EmailEntity.php b/app/Jobs/Entity/EmailEntity.php
index 32829c030ad9..f8e4ae5affd4 100644
--- a/app/Jobs/Entity/EmailEntity.php
+++ b/app/Jobs/Entity/EmailEntity.php
@@ -119,7 +119,7 @@ class EmailEntity implements ShouldQueue
$nmo->reminder_template = $this->reminder_template;
$nmo->entity = $this->entity;
- NinjaMailerJob::dispatch($nmo);
+ NinjaMailerJob::dispatchNow($nmo);
/* Mark entity sent */
$this->entity->service()->markSent()->save();
diff --git a/app/Jobs/RecurringInvoice/SendRecurring.php b/app/Jobs/RecurringInvoice/SendRecurring.php
index 70e249f58015..96ed926e734d 100644
--- a/app/Jobs/RecurringInvoice/SendRecurring.php
+++ b/app/Jobs/RecurringInvoice/SendRecurring.php
@@ -86,14 +86,14 @@ class SendRecurring implements ShouldQueue
$this->recurring_invoice->save();
//Admin notification for recurring invoice sent.
- if ($invoice->invitations->count() >= 1) {
+ if ($invoice->invitations->count() >= 1 ) {
$invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', 'email_template_invoice');
}
nlog("Invoice {$invoice->number} created");
$invoice->invitations->each(function ($invitation) use ($invoice) {
- if ($invitation->contact && strlen($invitation->contact->email) >=1) {
+ if ($invitation->contact && strlen($invitation->contact->email) >=1 && $invoice->client->getSetting('auto_email_invoice')) {
try{
EmailEntity::dispatch($invitation, $invoice->company);
diff --git a/app/Jobs/Util/Import.php b/app/Jobs/Util/Import.php
index e256105de35e..7b01cc890c18 100644
--- a/app/Jobs/Util/Import.php
+++ b/app/Jobs/Util/Import.php
@@ -35,11 +35,14 @@ use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
use App\Http\ValidationRules\ValidUserForCompany;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Company\CreateCompanyToken;
+use App\Jobs\Mail\NinjaMailerJob;
+use App\Jobs\Mail\NinjaMailerObject;
use App\Jobs\Ninja\CheckCompanyData;
use App\Jobs\Ninja\CompanySizeCheck;
use App\Jobs\Util\VersionCheck;
use App\Libraries\MultiDB;
use App\Mail\MigrationCompleted;
+use App\Mail\Migration\StripeConnectMigration;
use App\Models\Activity;
use App\Models\Client;
use App\Models\ClientContact;
@@ -87,6 +90,7 @@ use Illuminate\Http\UploadedFile;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
@@ -244,6 +248,10 @@ class Import implements ShouldQueue
// $this->fixData();
try{
+ App::forgetInstance('translator');
+ $t = app('translator');
+ $t->replace(Ninja::transformTranslations($this->company->settings));
+
Mail::to($this->user->email, $this->user->name())
->send(new MigrationCompleted($this->company, implode("
",$check_data)));
}
@@ -1381,9 +1389,21 @@ class Import implements ShouldQueue
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
}
+ /* On Hosted platform we need to advise Stripe users to connect with Stripe Connect */
if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
+
+ $nmo = new NinjaMailerObject;
+ $nmo->mailable = new StripeConnectMigration($this->company);
+ $nmo->company = $this->company;
+ $nmo->settings = $this->company->settings;
+ $nmo->to_user = $this->user;
+ NinjaMailerJob::dispatch($nmo);
+
$modified['gateway_key'] = 'd14dd26a47cecc30fdd65700bfb67b34';
- $modified['fees_and_limits'] = [];
+
+ //why do we set this to a blank array?
+ //$modified['fees_and_limits'] = [];
+
}
$company_gateway = CompanyGateway::create($modified);
diff --git a/app/Mail/Migration/StripeConnectMigration.php b/app/Mail/Migration/StripeConnectMigration.php
new file mode 100644
index 000000000000..93fda725afac
--- /dev/null
+++ b/app/Mail/Migration/StripeConnectMigration.php
@@ -0,0 +1,55 @@
+company = $company;
+ }
+
+ /**
+ * Build the message.
+ *
+ * @return $this
+ */
+ public function build()
+ {
+ $this->settings = $this->company->settings;
+ $this->logo = $this->company->present()->logo();
+ $this->whitelabel = $this->company->account->isPaid();
+
+ return $this->from(config('mail.from.address'), config('mail.from.name'))
+ ->subject(ctrans('texts.stripe_connect_migration_title'))
+ ->view('email.migration.stripe_connect');
+ }
+}
diff --git a/app/Models/Client.php b/app/Models/Client.php
index 5bbfcc8af071..894d928a036d 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -15,7 +15,11 @@ use App\DataMapper\ClientSettings;
use App\DataMapper\CompanySettings;
use App\DataMapper\FeesAndLimits;
use App\Models\CompanyGateway;
+use App\Models\Expense;
use App\Models\Presenters\ClientPresenter;
+use App\Models\Project;
+use App\Models\Quote;
+use App\Models\Task;
use App\Services\Client\ClientService;
use App\Utils\Traits\AppSetup;
use App\Utils\Traits\GeneratesCounter;
@@ -153,6 +157,16 @@ class Client extends BaseModel implements HasLocalePreference
return $this->hasMany(ClientGatewayToken::class);
}
+ public function expenses()
+ {
+ return $this->hasMany(Expense::class)->withTrashed();
+ }
+
+ public function projects()
+ {
+ return $this->hasMany(Project::class)->withTrashed();
+ }
+
/**
* Retrieves the specific payment token per
* gateway - per payment method.
@@ -217,6 +231,16 @@ class Client extends BaseModel implements HasLocalePreference
return $this->hasMany(Invoice::class)->withTrashed();
}
+ public function quotes()
+ {
+ return $this->hasMany(Quote::class)->withTrashed();
+ }
+
+ public function tasks()
+ {
+ return $this->hasMany(Task::class)->withTrashed();
+ }
+
public function recurring_invoices()
{
return $this->hasMany(RecurringInvoice::class)->withTrashed();
@@ -774,7 +798,7 @@ class Client extends BaseModel implements HasLocalePreference
public function payments()
{
- return $this->hasMany(Payment::class);
+ return $this->hasMany(Payment::class)->withTrashed();
}
public function timezone_offset()
diff --git a/app/Repositories/ActivityRepository.php b/app/Repositories/ActivityRepository.php
index ce31e331ce99..ec0beef03b97 100644
--- a/app/Repositories/ActivityRepository.php
+++ b/app/Repositories/ActivityRepository.php
@@ -125,7 +125,7 @@ class ActivityRepository extends BaseRepository
$design = Design::find($entity_design_id);
- if(!$entity->invitations()->exists()){
+ if(!$entity->invitations()->exists() || !$design){
nlog("No invitations for entity {$entity->id} - {$entity->number}");
return;
}
diff --git a/app/Services/Client/ClientService.php b/app/Services/Client/ClientService.php
index 2355639f70d2..840a3b7a9754 100644
--- a/app/Services/Client/ClientService.php
+++ b/app/Services/Client/ClientService.php
@@ -12,6 +12,7 @@
namespace App\Services\Client;
use App\Models\Client;
+use App\Services\Client\Merge;
use App\Services\Client\PaymentMethod;
use App\Utils\Number;
use Illuminate\Database\Eloquent\Collection;
@@ -77,6 +78,13 @@ class ClientService
return (new PaymentMethod($this->client, $amount))->run();
}
+ public function merge(Client $mergable_client)
+ {
+ $this->client = (new Merge($this->client, $mergable_client))->run();
+
+ return $this;
+ }
+
public function save() :Client
{
$this->client->save();
diff --git a/app/Services/Client/Merge.php b/app/Services/Client/Merge.php
new file mode 100644
index 000000000000..e254000b2384
--- /dev/null
+++ b/app/Services/Client/Merge.php
@@ -0,0 +1,102 @@
+client = $client;
+ $this->mergable_client = $mergable_client;
+ }
+
+ public function run()
+ {
+
+ $this->client->balance += $this->mergable_client->balance;
+ $this->client->paid_to_date += $this->mergable_client->paid_to_date;
+ $this->client->save();
+
+ $this->updateLedger($this->mergable_client->balance);
+
+ $this->mergable_client->activities()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->contacts()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->gateway_tokens()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->credits()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->expenses()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->invoices()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->payments()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->projects()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->quotes()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->recurring_invoices()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->tasks()->update(['client_id' => $this->client->id]);
+ $this->mergable_client->documents()->update(['documentable_id' => $this->client->id]);
+
+ /* Loop through contacts an only merge distinct contacts by email */
+ $this->mergable_client->contacts->each(function ($contact){
+
+ $exist = $this->client->contacts->contains(function ($client_contact) use($contact){
+ return $client_contact->email == $contact->email;
+ });
+
+ if($exist)
+ {
+ $contact->delete();
+ $contact->save();
+ }
+
+ });
+
+ $this->mergable_client->forceDelete();
+
+ return $this->client;
+ }
+
+ private function updateLedger($adjustment)
+ {
+ $balance = 0;
+
+ $company_ledger = CompanyLedger::whereClientId($this->client->id)
+ ->orderBy('id', 'DESC')
+ ->first();
+
+ if ($company_ledger) {
+ $balance = $company_ledger->balance;
+ }
+
+ $company_ledger = CompanyLedgerFactory::create($this->client->company_id, $this->client->user_id);
+ $company_ledger->client_id = $this->client->id;
+ $company_ledger->adjustment = $adjustment;
+ $company_ledger->notes = "Balance update after merging " . $this->mergable_client->present()->name();
+ $company_ledger->balance = $balance + $adjustment;
+ $company_ledger->activity_id = Activity::UPDATE_CLIENT;
+ $company_ledger->save();
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/Services/User/UserService.php b/app/Services/User/UserService.php
index 5b44632420c1..bcecad59ac48 100644
--- a/app/Services/User/UserService.php
+++ b/app/Services/User/UserService.php
@@ -39,7 +39,7 @@ class UserService
$nmo->to_user = $this->user;
$nmo->settings = $company->settings;
- NinjaMailerJob::dispatch($nmo);
+ NinjaMailerJob::dispatch($nmo, true);
Ninja::registerNinjaUser($this->user);
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index 40038652ebad..eed12cf3f464 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -4287,6 +4287,8 @@ $LANG = array(
'company_deleted' => 'Company deleted',
'company_deleted_body' => 'Company [ :company ] was deleted by :user',
'back_to' => 'Back to :url',
+ 'stripe_connect_migration_title' => 'Connect your Stripe Account',
+ 'stripe_connect_migration_desc' => 'Invoice Ninja v5 uses Stripe Connect to link your Stripe account to Invoice Ninja. This provides an additional layer of security for your account. Now that you data has migrated, you will need to Authorize Stripe to accept payments in v5.
To do this, navigate to Settings > Online Payments > Configure Gateways. Click on Stripe Connect and then under Settings click Setup Gateway. This will take you to Stripe to authorize Invoice Ninja and on your return your account will be successfully linked!',
);
return $LANG;
diff --git a/resources/views/email/migration/max_companies.blade.php b/resources/views/email/migration/max_companies.blade.php
index 6dd1b45a93f3..11bb1484f184 100644
--- a/resources/views/email/migration/max_companies.blade.php
+++ b/resources/views/email/migration/max_companies.blade.php
@@ -1,6 +1,7 @@
@component('email.template.admin', ['logo' => $logo, 'settings' => $settings])
{{ ctrans('texts.max_companies_desc') }}
{{ ctrans('texts.stripe_connect_migration_desc') }}
+