diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php
index 4d81705ea3f6..10036a77bda7 100644
--- a/app/Http/Controllers/Auth/ForgotPasswordController.php
+++ b/app/Http/Controllers/Auth/ForgotPasswordController.php
@@ -104,9 +104,9 @@ class ForgotPasswordController extends Controller
*/
public function sendResetLinkEmail(Request $request)
{
- //MultiDB::userFindAndSetDb($request->input('email'));
+ MultiDB::userFindAndSetDb($request->input('email'));
- $user = MultiDB::hasUser(['email' => $request->input('email')]);
+ // $user = MultiDB::hasUser(['email' => $request->input('email')]);
$this->validateEmail($request);
diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php
new file mode 100644
index 000000000000..e2d99efa8e93
--- /dev/null
+++ b/app/Http/Controllers/ExportController.php
@@ -0,0 +1,64 @@
+user()->getCompany(), auth()->user());
+
+ return response()->json(['message' => 'Processing'], 200);
+
+ }
+}
diff --git a/app/Http/Requests/Export/StoreExportRequest.php b/app/Http/Requests/Export/StoreExportRequest.php
new file mode 100644
index 000000000000..96d322a53df6
--- /dev/null
+++ b/app/Http/Requests/Export/StoreExportRequest.php
@@ -0,0 +1,39 @@
+user()->isAdmin();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [];
+ }
+}
diff --git a/app/Jobs/Company/CompanyExport.php b/app/Jobs/Company/CompanyExport.php
new file mode 100644
index 000000000000..f9206969fd8b
--- /dev/null
+++ b/app/Jobs/Company/CompanyExport.php
@@ -0,0 +1,471 @@
+company = $company;
+ $this->user = $user;
+ $this->export_format = $export_format;
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return CompanyToken|null
+ */
+ public function handle() : void
+ {
+
+ MultiDB::setDb($this->company->db);
+
+ set_time_limit(0);
+
+ $this->export_data['app_version'] = config('ninja.app_version');
+
+ $this->export_data['activities'] = $this->company->all_activities->map(function ($activity){
+
+ $activity = $this->transformArrayOfKeys($activity, [
+ 'user_id',
+ 'company_id',
+ 'client_id',
+ 'client_contact_id',
+ 'account_id',
+ 'project_id',
+ 'vendor_id',
+ 'payment_id',
+ 'invoice_id',
+ 'credit_id',
+ 'invitation_id',
+ 'task_id',
+ 'expense_id',
+ 'token_id',
+ 'quote_id',
+ 'subscription_id',
+ 'recurring_invoice_id'
+ ]);
+
+ return $activity;
+
+ })->makeHidden(['id'])->toArray();
+
+ $this->export_data['backups'] = $this->company->all_activities()->with('backup')->cursor()->map(function ($activity){
+
+ $backup = $activity->backup;
+
+ if(!$backup)
+ return;
+
+ $backup->activity_id = $this->encodePrimaryKey($backup->activity_id);
+
+ return $backup;
+
+ })->toArray();
+
+ $this->export_data['client_contacts'] = $this->company->client_contacts->map(function ($client_contact){
+
+ $client_contact = $this->transformArrayOfKeys($client_contact, ['id', 'company_id', 'user_id',' client_id']);
+
+ return $client_contact;
+
+ })->toArray();
+
+
+ $this->export_data['client_gateway_tokens'] = $this->company->client_gateway_tokens->map(function ($client_gateway_token){
+
+ $client_gateway_token = $this->transformArrayOfKeys($client_gateway_token, ['id', 'company_id', 'client_id']);
+
+ return $client_gateway_token;
+
+ })->toArray();
+
+
+ $this->export_data['clients'] = $this->company->clients->map(function ($client){
+
+ $client = $this->transformArrayOfKeys($client, ['id', 'company_id', 'user_id',' assigned_user_id', 'group_settings_id']);
+
+ return $client;
+
+ })->toArray();
+
+ $temp_co = $this->company;
+ $temp_co->id = $this->encodePrimaryKey($temp_co->id);
+ $temp_co->account_id = $this->encodePrimaryKey($temp_co->account_id);
+
+ $this->export_data['company'] = $temp_co->toArray();
+
+ $this->export_data['company_gateways'] = $this->company->company_gateways->map(function ($company_gateway){
+
+ $company_gateway = $this->transformArrayOfKeys($company_gateway, ['company_id', 'user_id']);
+
+ return $company_gateway;
+
+ })->toArray();
+
+ $this->export_data['company_tokens'] = $this->company->tokens->map(function ($token){
+
+ $token = $this->transformArrayOfKeys($token, ['company_id', 'account_id', 'user_id']);
+
+ return $token;
+
+ })->toArray();
+
+ $this->export_data['company_ledger'] = $this->company->ledger->map(function ($ledger){
+
+ $ledger = $this->transformArrayOfKeys($ledger, ['activity_id', 'client_id', 'company_id', 'account_id', 'user_id','company_ledgerable_id']);
+
+ return $ledger;
+
+ })->toArray();
+
+ $this->export_data['company_users'] = $this->company->company_users->map(function ($company_user){
+
+ $company_user = $this->transformArrayOfKeys($company_user, ['company_id', 'account_id', 'user_id']);
+
+ return $company_user;
+
+ })->toArray();
+
+ $this->export_data['credits'] = $this->company->credits->map(function ($credit){
+
+ $credit = $this->transformBasicEntities($credit);
+ $credit = $this->transformArrayOfKeys($credit, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id','invoice_id']);
+
+ return $credit;
+
+ })->toArray();
+
+
+ $this->export_data['credit_invitations'] = CreditInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($credit){
+
+ $credit = $this->transformArrayOfKeys($credit, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
+
+ return $credit;
+
+ })->toArray();
+
+ $this->export_data['designs'] = $this->company->user_designs->makeHidden(['id'])->toArray();
+
+ $this->export_data['documents'] = $this->company->documents->map(function ($document){
+
+ $document = $this->transformArrayOfKeys($document, ['user_id', 'assigned_user_id', 'company_id', 'project_id', 'vendor_id']);
+
+ return $document;
+
+ })->toArray();
+
+ $this->export_data['expense_categories'] = $this->company->expenses->map(function ($expense_category){
+
+ $expense_category = $this->transformArrayOfKeys($expense_category, ['user_id', 'company_id']);
+
+ return $expense_category;
+
+ })->toArray();
+
+
+ $this->export_data['expenses'] = $this->company->expenses->map(function ($expense){
+
+ $expense = $this->transformBasicEntities($expense);
+ $expense = $this->transformArrayOfKeys($expense, ['vendor_id', 'invoice_id', 'client_id', 'category_id', 'recurring_expense_id','project_id']);
+
+ return $expense;
+
+ })->toArray();
+
+ $this->export_data['group_settings'] = $this->company->group_settings->map(function ($gs){
+
+ $gs = $this->transformArrayOfKeys($gs, ['user_id', 'company_id']);
+
+ return $gs;
+
+ })->toArray();
+
+
+ $this->export_data['invoices'] = $this->company->invoices->map(function ($invoice){
+
+ $invoice = $this->transformBasicEntities($invoice);
+ $invoice = $this->transformArrayOfKeys($invoice, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
+
+ return $invoice;
+
+ })->toArray();
+
+
+ $this->export_data['invoice_invitations'] = InvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($invoice){
+
+ $invoice = $this->transformArrayOfKeys($invoice, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
+
+ return $invoice;
+
+ })->toArray();
+
+ $this->export_data['payment_terms'] = $this->company->user_payment_terms->map(function ($term){
+
+ $term = $this->transformArrayOfKeys($term, ['user_id', 'company_id']);
+
+ return $term;
+
+ })->makeHidden(['id'])->toArray();
+
+ $this->export_data['paymentables'] = $this->company->payments()->with('paymentables')->cursor()->map(function ($paymentable){
+
+ $paymentable = $this->transformArrayOfKeys($paymentable, ['payment_id','paymentable_id']);
+
+ return $paymentable;
+
+ })->toArray();
+
+ $this->export_data['payments'] = $this->company->payments->map(function ($payment){
+
+ $payment = $this->transformBasicEntities($payment);
+ $payment = $this->transformArrayOfKeys($payment, ['client_id','project_id', 'vendor_id', 'client_contact_id', 'invitation_id', 'company_gateway_id']);
+
+ return $project;
+
+ })->toArray();
+
+
+ $this->export_data['projects'] = $this->company->projects->map(function ($project){
+
+ $project = $this->transformBasicEntities($project);
+ $project = $this->transformArrayOfKeys($project, ['client_id']);
+
+ return $project;
+
+ })->toArray();
+
+ $this->export_data['quotes'] = $this->company->quotes->map(function ($quote){
+
+ $quote = $this->transformBasicEntities($quote);
+ $quote = $this->transformArrayOfKeys($quote, ['invoice_id','recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
+
+ return $quote;
+
+ })->toArray();
+
+
+ $this->export_data['quote_invitations'] = QuoteInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($quote){
+
+ $quote = $this->transformArrayOfKeys($quote, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
+
+ return $quote;
+
+ })->toArray();
+
+
+ $this->export_data['recurring_invoices'] = $this->company->recurring_invoices->map(function ($ri){
+
+ $ri = $this->transformBasicEntities($ri);
+ $ri = $this->transformArrayOfKeys($ri, ['client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
+ return $ri;
+
+ })->toArray();
+
+
+ $this->export_data['recurring_invoice_invitations'] = RecurringInvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($ri){
+
+ $ri = $this->transformArrayOfKeys($ri, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
+
+ return $ri;
+
+ })->toArray();
+
+ $this->export_data['subscriptions'] = $this->company->subscriptions->map(function ($subscription){
+
+ $subscription = $this->transformBasicEntities($subscription);
+ $subscription->group_id = $this->encodePrimaryKey($subscription->group_id);
+
+ return $subscription;
+
+ })->toArray();
+
+
+ $this->export_data['system_logs'] = $this->company->system_logs->map(function ($log){
+
+ $log->client_id = $this->encodePrimaryKey($log->client_id);
+ $log->company_id = $this->encodePrimaryKey($log->company_id);
+
+ return $log;
+
+ })->makeHidden(['id'])->toArray();
+
+ $this->export_data['tasks'] = $this->company->tasks->map(function ($task){
+
+ $task = $this->transformBasicEntities($task);
+ $task = $this->transformArrayOfKeys($task, ['client_id', 'invoice_id', 'project_id', 'status_id']);
+
+ return $task;
+
+ })->toArray();
+
+ $this->export_data['task_statuses'] = $this->company->task_statuses->map(function ($status){
+
+ $status->id = $this->encodePrimaryKey($status->id);
+ $status->user_id = $this->encodePrimaryKey($status->user_id);
+ $status->company_id = $this->encodePrimaryKey($status->company_id);
+
+ return $status;
+
+ })->toArray();
+
+ $this->export_data['tax_rates'] = $this->company->tax_rates->map(function ($rate){
+
+ $rate->company_id = $this->encodePrimaryKey($rate->company_id);
+ $rate->user_id = $this->encodePrimaryKey($rate->user_id);
+
+ return $rate;
+
+ })->makeHidden(['id'])->toArray();
+
+ $this->export_data['users'] = $this->company->users->map(function ($user){
+
+ $user->account_id = $this->encodePrimaryKey($user->account_id);
+ $user->id = $this->encodePrimaryKey($user->id);
+
+ return $user;
+
+ })->makeHidden(['ip'])->toArray();
+
+ $this->export_data['vendors'] = $this->company->vendors->map(function ($vendor){
+
+ return $this->transformBasicEntities($vendor);
+
+ })->toArray();
+
+
+ $this->export_data['vendor_contacts'] = VendorContact::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($vendor){
+
+ $vendor = $this->transformBasicEntities($vendor);
+ $vendor->vendor_id = $this->encodePrimaryKey($vendor->vendor_id);
+
+ return $vendor;
+
+ })->toArray();
+
+ $this->export_data['webhooks'] = $this->company->webhooks->map(function ($hook){
+
+ $hook->user_id = $this->encodePrimaryKey($hook->user_id);
+ $hook->company_id = $this->encodePrimaryKey($hook->company_id);
+
+ return $hook;
+
+ })->makeHidden(['id'])->toArray();
+
+ //write to tmp and email to owner();
+
+ $this->zipAndSend();
+ }
+
+ private function transformBasicEntities($model)
+ {
+
+ return $this->transformArrayOfKeys($model, ['id', 'user_id', 'assigned_user_id', 'company_id']);
+
+ }
+
+ private function transformArrayOfKeys($model, $keys)
+ {
+
+ foreach($keys as $key){
+ $model->{$key} = $this->encodePrimaryKey($model->{$key});
+ }
+
+ return $model;
+
+ }
+
+ private function zipAndSend()
+ {
+ nlog("zipping");
+
+ $tempStream = fopen('php://memory', 'w+');
+
+ $options = new Archive();
+ $options->setOutputStream($tempStream);
+
+ $file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
+
+ $zip = new ZipStream($file_name, $options);
+
+ $fp = tmpfile();
+ fwrite($fp, json_encode($this->export_data));
+ rewind($fp);
+ $zip->addFileFromStream('backup.json', $fp);
+
+ $zip->finish();
+
+ $path = 'backups/';
+
+ nlog($path.$file_name);
+
+ Storage::disk(config('filesystems.default'))->put($path.$file_name, $tempStream);
+ // fclose($fp);
+
+ nlog(Storage::disk(config('filesystems.default'))->url($path.$file_name));
+
+ fclose($tempStream);
+
+ $nmo = new NinjaMailerObject;
+ $nmo->mailable = new DownloadBackup(Storage::disk(config('filesystems.default'))->url($path.$file_name), $this->company);
+ $nmo->to_user = $this->user;
+ $nmo->settings = $this->company->settings;
+ $nmo->company = $this->company;
+
+ NinjaMailerJob::dispatch($nmo);
+
+ UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1));
+ }
+
+}
diff --git a/app/Jobs/Mail/NinjaMailerJob.php b/app/Jobs/Mail/NinjaMailerJob.php
index 5796d038dce1..6e8777750635 100644
--- a/app/Jobs/Mail/NinjaMailerJob.php
+++ b/app/Jobs/Mail/NinjaMailerJob.php
@@ -54,7 +54,9 @@ class NinjaMailerJob implements ShouldQueue
public $nmo;
- public function __construct(NinjaMailerObject $nmo)
+ public $override;
+
+ public function __construct(NinjaMailerObject $nmo, bool $override = false)
{
$this->nmo = $nmo;
@@ -64,7 +66,7 @@ class NinjaMailerJob implements ShouldQueue
public function handle()
{
/*If we are migrating data we don't want to fire any emails*/
- if ($this->nmo->company->is_disabled)
+ if ($this->nmo->company->is_disabled && !$this->override)
return true;
/*Set the correct database*/
@@ -83,6 +85,10 @@ class NinjaMailerJob implements ShouldQueue
$this->nmo->mailable->replyTo($this->nmo->settings->reply_to_email, $reply_to_name);
}
+ else {
+ $this->nmo->mailable->replyTo($this->nmo->company->owner()->email, $this->nmo->company->owner()->present()->name());
+ }
+
if (strlen($this->nmo->settings->bcc_email) > 1) {
nlog('bcc list available');
diff --git a/app/Jobs/User/UserEmailChanged.php b/app/Jobs/User/UserEmailChanged.php
index de433a4c826f..a87a83b11f52 100644
--- a/app/Jobs/User/UserEmailChanged.php
+++ b/app/Jobs/User/UserEmailChanged.php
@@ -55,9 +55,6 @@ class UserEmailChanged implements ShouldQueue
public function handle()
{
nlog("notifying user of email change");
-
- if ($this->company->is_disabled)
- return true;
//Set DB
MultiDB::setDb($this->company->db);
@@ -78,7 +75,7 @@ class UserEmailChanged implements ShouldQueue
$nmo->company = $this->company;
$nmo->to_user = $this->old_user;
- NinjaMailerJob::dispatch($nmo);
+ NinjaMailerJob::dispatch($nmo, true);
// $nmo->to_user = $this->new_user;
// NinjaMailerJob::dispatch($nmo);
diff --git a/app/Jobs/Util/Import.php b/app/Jobs/Util/Import.php
index 7fc8ec6ea423..e3a2a267fcd3 100644
--- a/app/Jobs/Util/Import.php
+++ b/app/Jobs/Util/Import.php
@@ -209,6 +209,9 @@ class Import implements ShouldQueue
$this->{$method}($data[$import]);
}
+ if(Ninja::isHosted())
+ $this->processNinjaTokens($data['ninja_tokens']);
+
$this->setInitialCompanyLedgerBalances();
// $this->fixClientBalances();
@@ -1636,6 +1639,10 @@ class Import implements ShouldQueue
return $response->getBody();
}
+ private function processNinjaTokens(array $data)
+ {
+
+ }
/* In V4 we use negative invoices (credits) and add then into the client balance. In V5, these sit off ledger and are applied later.
This next section will check for credit balances and reduce the client balance so that the V5 balances are correct
diff --git a/app/Libraries/MultiDB.php b/app/Libraries/MultiDB.php
index 92132a118be2..27673891e09f 100644
--- a/app/Libraries/MultiDB.php
+++ b/app/Libraries/MultiDB.php
@@ -129,13 +129,12 @@ class MultiDB
}
foreach (self::$dbs as $db) {
+
self::setDB($db);
- $user = User::where($data)->withTrashed()->first();
-
- if ($user) {
+ if ($user = User::where($data)->withTrashed()->first())
return $user;
- }
+
}
self::setDefaultDatabase();
diff --git a/app/Mail/DownloadBackup.php b/app/Mail/DownloadBackup.php
new file mode 100644
index 000000000000..5b354a5db265
--- /dev/null
+++ b/app/Mail/DownloadBackup.php
@@ -0,0 +1,41 @@
+file_path = $file_path;
+
+ $this->company = $company;
+ }
+
+ /**
+ * Build the message.
+ */
+ public function build()
+ {
+ return $this->from(config('mail.from.address'), config('mail.from.name'))
+ ->subject(ctrans('texts.download_backup_subject'))
+ ->markdown(
+ 'email.admin.download_files',
+ [
+ 'url' => $this->file_path,
+ 'logo' => $this->company->present()->logo,
+ 'whitelabel' => $this->company->account->isPaid() ? true : false,
+ ]
+ );
+ }
+}
diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php
index f9e3196c2fe7..1568c290e96a 100644
--- a/app/Mail/TemplateEmail.php
+++ b/app/Mail/TemplateEmail.php
@@ -107,7 +107,7 @@ class TemplateEmail extends Mailable
});
//conditionally attach files
- if ($settings->pdf_email_attachment !== false && ! empty($this->build_email->getAttachments())) {
+ // if ($settings->pdf_email_attachment !== false && ! empty($this->build_email->getAttachments())) {
//hosted | plan check here
foreach ($this->build_email->getAttachments() as $file) {
@@ -118,7 +118,7 @@ class TemplateEmail extends Mailable
$this->attach($file['path'], ['as' => $file['name'], 'mime' => $file['mime']]);
}
- }
+ // }
return $this;
}
diff --git a/app/Models/Company.php b/app/Models/Company.php
index ab808ac3510e..47ea068fea24 100644
--- a/app/Models/Company.php
+++ b/app/Models/Company.php
@@ -150,6 +150,11 @@ class Company extends BaseModel
return $this->belongsTo(Account::class);
}
+ public function client_contacts()
+ {
+ return $this->hasMany(ClientContact::class)->withTrashed();
+ }
+
public function users()
{
return $this->hasManyThrough(User::class, CompanyUser::class, 'company_id', 'id', 'id', 'user_id');
@@ -203,6 +208,12 @@ class Company extends BaseModel
return $this->hasMany(Vendor::class)->withTrashed();
}
+ public function all_activities()
+ {
+ return $this->hasMany(Activity::class);
+ }
+
+
public function activities()
{
return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(300);
@@ -301,11 +312,21 @@ class Company extends BaseModel
return $this->hasMany(Design::class)->whereCompanyId($this->id)->orWhere('company_id', null);
}
+ public function user_designs()
+ {
+ return $this->hasMany(Design::class);
+ }
+
public function payment_terms()
{
return $this->hasMany(PaymentTerm::class)->whereCompanyId($this->id)->orWhere('company_id', null);
}
+ public function user_payment_terms()
+ {
+ return $this->hasMany(PaymentTerm::class);
+ }
+
/**
* @return BelongsTo
*/
diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php
index d3da0af8b89f..7acfb3258875 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -415,6 +415,8 @@ class Invoice extends BaseModel
CreateEntityPdf::dispatchNow($invitation);
}
+nlog($storage_path);
+
return $storage_path;
}
diff --git a/app/PaymentDrivers/Stripe/SOFORT.php b/app/PaymentDrivers/Stripe/SOFORT.php
index af96cdf1c693..77d6e63a57d8 100644
--- a/app/PaymentDrivers/Stripe/SOFORT.php
+++ b/app/PaymentDrivers/Stripe/SOFORT.php
@@ -102,8 +102,6 @@ class SOFORT
{
$server_response = $this->stripe->payment_hash->data;
- PaymentFailureMailer::dispatch($this->stripe->client, $server_response->redirect_status, $this->stripe->client->company, $server_response->amount);
-
PaymentFailureMailer::dispatch(
$this->stripe->client,
$server_response,
diff --git a/app/Utils/SystemHealth.php b/app/Utils/SystemHealth.php
index 552d580df458..3d1d72317977 100644
--- a/app/Utils/SystemHealth.php
+++ b/app/Utils/SystemHealth.php
@@ -82,9 +82,20 @@ class SystemHealth
'mail_mailer' => (string)self::checkMailMailer(),
'flutter_renderer' => (string)config('ninja.flutter_canvas_kit'),
'jobs_pending' => (int) Queue::size(),
+ 'pdf_engine' => (string) self::getPdfEngine(),
];
}
+ public static function getPdfEngine()
+ {
+ if(config('ninja.invoiceninja_hosted_pdf_generation'))
+ return 'Invoice Ninja Hosted PDF Generator';
+ elseif(config('ninja.phantomjs_pdf_generation'))
+ return 'Phantom JS Web Generator';
+ else
+ return 'SnapPDF PDF Generator';
+ }
+
public static function checkMailMailer()
{
return config('mail.default');
diff --git a/resources/lang/ca/texts.php b/resources/lang/ca/texts.php
index 5302d2431d11..efa11815049c 100644
--- a/resources/lang/ca/texts.php
+++ b/resources/lang/ca/texts.php
@@ -2861,6 +2861,7 @@ $LANG = [
'my_invoices' => 'My Invoices',
'mobile_refresh_warning' => 'If you\'re using the mobile app you may need to do a full refresh.',
'enable_proposals_for_background' => 'To upload a background image :link to enable the proposals module.',
+
];
diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php
index 2a46a25013ba..7f1174d56bf1 100644
--- a/resources/lang/en/texts.php
+++ b/resources/lang/en/texts.php
@@ -4248,6 +4248,7 @@ $LANG = array(
'activity_104' => ':user restored recurring invoice :recurring_invoice',
'new_login_detected' => 'New login detected for your account.',
'new_login_description' => 'You recently logged in to your Invoice Ninja account from a new location or device:
IP: :ip
Time: :time
Email: :email',
+ 'download_backup_subject' => 'Your company backup is ready for download',
);
return $LANG;
diff --git a/routes/api.php b/routes/api.php
index ed9029c41ff2..b46bd12f0d2e 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -75,6 +75,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::put('expenses/{expense}/upload', 'ExpenseController@upload');
Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk');
+ Route::post('export', 'ExportController@index')->name('export.index');
+
Route::resource('expense_categories', 'ExpenseCategoryController'); // name = (expense_categories. index / create / show / update / destroy / edit
Route::post('expense_categories/bulk', 'ExpenseCategoryController@bulk')->name('expense_categories.bulk');
diff --git a/tests/Feature/Export/ExportCompanyTest.php b/tests/Feature/Export/ExportCompanyTest.php
new file mode 100644
index 000000000000..bfdad43e3845
--- /dev/null
+++ b/tests/Feature/Export/ExportCompanyTest.php
@@ -0,0 +1,49 @@
+withoutMiddleware(
+ ThrottleRequests::class
+ );
+
+ // $this->faker = \Faker\Factory::create();
+
+ $this->makeTestData();
+
+ $this->withoutExceptionHandling();
+ }
+
+ public function testCompanyExport()
+ {
+ CompanyExport::dispatchNow($this->company, $this->company->users->first());
+ }
+}