mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge remote-tracking branch 'upstream/v5-develop' into v5-update-email-templates
This commit is contained in:
commit
dfdb4b1cd9
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,11 +1,21 @@
|
|||||||
# Release notes
|
# Release notes
|
||||||
|
|
||||||
## [Unreleased (daily channel)](https://github.com/invoiceninja/invoiceninja/tree/v5-develop)
|
## [Unreleased (daily channel)](https://github.com/invoiceninja/invoiceninja/tree/v5-develop)
|
||||||
|
|
||||||
|
## [v5.2.0-release](https://github.com/invoiceninja/invoiceninja/releases/tag/v5.2.0-release)
|
||||||
|
## Added:
|
||||||
|
- Timezone Offset: Schedule emails based on timezone and time offsets.
|
||||||
|
- Force client country to system country if none is set.
|
||||||
|
- GMail Oauth via web
|
||||||
|
|
||||||
|
## Fixed:
|
||||||
- Add Cache-control: no-cache to prevent overaggressive caching of assets
|
- Add Cache-control: no-cache to prevent overaggressive caching of assets
|
||||||
- Improved labelling in the settings (client portal)
|
- Improved labelling in the settings (client portal)
|
||||||
- Client portal: Multiple accounts access improvements (#5703)
|
- Client portal: Multiple accounts access improvements (#5703)
|
||||||
- Client portal: "Credits" updates (#5734)
|
- Client portal: "Credits" updates (#5734)
|
||||||
- Client portal: Make sidebar white color, in order to make logo displaying more simple. (#5753)
|
- Client portal: Make sidebar white color, in order to make logo displaying more simple. (#5753)
|
||||||
|
- Inject small delay into emails to allow all resources to be produced (ie PDFs) prior to sending
|
||||||
|
- Fixes for endless reminders not firing
|
||||||
|
|
||||||
## [v5.1.56-release](https://github.com/invoiceninja/invoiceninja/releases/tag/v5.1.56-release)
|
## [v5.1.56-release](https://github.com/invoiceninja/invoiceninja/releases/tag/v5.1.56-release)
|
||||||
## Fixed:
|
## Fixed:
|
||||||
|
88
LICENSE
88
LICENSE
@ -1,47 +1,47 @@
|
|||||||
Copyright (c) 2021 by David Bomba
|
Elastic License 2.0 (ELv2)
|
||||||
Invoice Ninja * https://www.invoiceninja.com
|
Elastic License
|
||||||
"CREATE. SEND. GET PAID"
|
|
||||||
|
|
||||||
All Rights Reserved
|
Acceptance
|
||||||
ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license)
|
By using the software, you agree to all of the terms and conditions below.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the conditions below are met.
|
|
||||||
These conditions require a modest attribution to InvoiceNinja.com (the
|
|
||||||
"Author"), who hopes that its promotional value may help justify the
|
|
||||||
thousands of dollars in otherwise billable time invested in writing
|
|
||||||
this and other freely available, open-source software.
|
|
||||||
|
|
||||||
1. Redistributions of source code, in whole or part and with or without
|
Copyright License
|
||||||
modification (the "Code"), must prominently display this GPG-signed
|
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations and conditions below
|
||||||
text in verifiable form.
|
|
||||||
2. Redistributions of the Code in binary form must be accompanied by
|
|
||||||
this GPG-signed text in any documentation and, each time the resulting
|
|
||||||
executable program or a program dependent thereon is launched, a
|
|
||||||
prominent display (e.g., splash screen or banner text) of the Author's
|
|
||||||
attribution information, which includes:
|
|
||||||
(a) Name ("Hillel Coren"),
|
|
||||||
(b) Professional identification ("Invoice Ninja"), and
|
|
||||||
(c) URL ("https://www.invoiceninja.com").
|
|
||||||
3. Neither the name nor any trademark of the Author may be used to
|
|
||||||
endorse or promote products derived from this software without specific
|
|
||||||
prior written permission.
|
|
||||||
4. Users are entirely responsible, to the exclusion of the Author and
|
|
||||||
any other persons, for compliance with (1) regulations set by owners or
|
|
||||||
administrators of employed equipment, (2) licensing terms of any other
|
|
||||||
software, and (3) local regulations regarding use, including those
|
|
||||||
regarding import, export, and use of encryption software.
|
|
||||||
|
|
||||||
THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND
|
Limitations
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
You may not move, change, disable, or circumvent the license key functionality in the software, and you may not remove or obscure any functionality in the software that is protected by the license key.
|
||||||
EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS;
|
Patents
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
||||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
Notices
|
||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms.
|
||||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
||||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
If you modify the software, you must include in any modified copies of the software prominent notices stating that you have modified the software.
|
||||||
|
|
||||||
|
No Other Rights
|
||||||
|
These terms do not imply any licenses other than those expressly granted in these terms.
|
||||||
|
|
||||||
|
Termination
|
||||||
|
If you use the software in violation of these terms, such use is not licensed, and your licenses will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your licenses will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your licenses to terminate automatically and permanently.
|
||||||
|
|
||||||
|
No Liability
|
||||||
|
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
|
||||||
|
|
||||||
|
Definitions
|
||||||
|
The licensor is the entity offering these terms, and the software is the software the licensor makes available under these terms, including any portion of it.
|
||||||
|
|
||||||
|
you refers to the individual or entity agreeing to these terms.
|
||||||
|
|
||||||
|
your company is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
|
||||||
|
|
||||||
|
your licenses are all the licenses granted to you for the software under these terms.
|
||||||
|
|
||||||
|
use means anything you do with the software requiring one of your licenses.
|
||||||
|
|
||||||
|
trademark means trademarks, service marks, and similar rights.
|
||||||
|
|
||||||
|
For more information regarding the interpretation of this license please see here: https://invoiceninja.github.io/docs/legal/license/
|
@ -1 +1 @@
|
|||||||
5.1.71
|
5.2.4
|
@ -311,17 +311,17 @@ class CheckData extends Command
|
|||||||
Client::withTrashed()->where('is_deleted', 0)->cursor()->each(function ($client) use ($wrong_paid_to_dates, $credit_total_applied) {
|
Client::withTrashed()->where('is_deleted', 0)->cursor()->each(function ($client) use ($wrong_paid_to_dates, $credit_total_applied) {
|
||||||
$total_invoice_payments = 0;
|
$total_invoice_payments = 0;
|
||||||
|
|
||||||
foreach ($client->invoices->where('is_deleted', false)->where('status_id', '>', 1) as $invoice) {
|
foreach ($client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get() as $invoice) {
|
||||||
|
|
||||||
$total_amount = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.amount');
|
$total_amount = $invoice->payments()->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->get()->sum('pivot.amount');
|
||||||
$total_refund = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.refunded');
|
$total_refund = $invoice->payments()->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->get()->sum('pivot.refunded');
|
||||||
|
|
||||||
$total_invoice_payments += ($total_amount - $total_refund);
|
$total_invoice_payments += ($total_amount - $total_refund);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10/02/21
|
// 10/02/21
|
||||||
foreach ($client->payments as $payment) {
|
foreach ($client->payments as $payment) {
|
||||||
$credit_total_applied += $payment->paymentables->where('paymentable_type', App\Models\Credit::class)->sum(DB::raw('amount'));
|
$credit_total_applied += $payment->paymentables()->where('paymentable_type', App\Models\Credit::class)->get()->sum(DB::raw('amount'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($credit_total_applied < 0) {
|
if ($credit_total_applied < 0) {
|
||||||
@ -347,10 +347,11 @@ class CheckData extends Command
|
|||||||
$wrong_paid_to_dates = 0;
|
$wrong_paid_to_dates = 0;
|
||||||
|
|
||||||
Client::cursor()->where('is_deleted', 0)->each(function ($client) use ($wrong_balances) {
|
Client::cursor()->where('is_deleted', 0)->each(function ($client) use ($wrong_balances) {
|
||||||
|
|
||||||
$client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($wrong_balances, $client) {
|
$client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($wrong_balances, $client) {
|
||||||
$total_amount = $invoice->payments->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.amount');
|
$total_amount = $invoice->payments()->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->get()->sum('pivot.amount');
|
||||||
$total_refund = $invoice->payments->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
|
$total_refund = $invoice->payments()->get()->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
|
||||||
$total_credit = $invoice->credits->sum('amount');
|
$total_credit = $invoice->credits()->get()->sum('amount');
|
||||||
|
|
||||||
$total_paid = $total_amount - $total_refund;
|
$total_paid = $total_amount - $total_refund;
|
||||||
$calculated_paid_amount = $invoice->amount - $invoice->balance - $total_credit;
|
$calculated_paid_amount = $invoice->amount - $invoice->balance - $total_credit;
|
||||||
@ -363,6 +364,7 @@ class CheckData extends Command
|
|||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->logMessage("{$wrong_balances} clients with incorrect invoice balances");
|
$this->logMessage("{$wrong_balances} clients with incorrect invoice balances");
|
||||||
@ -408,8 +410,8 @@ class CheckData extends Command
|
|||||||
$wrong_paid_to_dates = 0;
|
$wrong_paid_to_dates = 0;
|
||||||
|
|
||||||
foreach (Client::where('is_deleted', 0)->cursor() as $client) {
|
foreach (Client::where('is_deleted', 0)->cursor() as $client) {
|
||||||
$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
|
$invoice_balance = $client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get()->sum('balance');
|
||||||
$credit_balance = $client->credits->where('is_deleted', false)->sum('balance');
|
$credit_balance = $client->credits()->where('is_deleted', false)->get()->sum('balance');
|
||||||
|
|
||||||
// if($client->balance != $invoice_balance)
|
// if($client->balance != $invoice_balance)
|
||||||
// $invoice_balance -= $credit_balance;//doesn't make sense to remove the credit amount
|
// $invoice_balance -= $credit_balance;//doesn't make sense to remove the credit amount
|
||||||
|
165
app/Console/Commands/CheckDb.php
Normal file
165
app/Console/Commands/CheckDb.php
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App;
|
||||||
|
use App\Factory\ClientContactFactory;
|
||||||
|
use App\Models\Account;
|
||||||
|
use App\Models\Activity;
|
||||||
|
use App\Models\Backup;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\ClientContact;
|
||||||
|
use App\Models\ClientGatewayToken;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\CompanyGateway;
|
||||||
|
use App\Models\CompanyLedger;
|
||||||
|
use App\Models\CompanyToken;
|
||||||
|
use App\Models\CompanyUser;
|
||||||
|
use App\Models\Contact;
|
||||||
|
use App\Models\Credit;
|
||||||
|
use App\Models\CreditInvitation;
|
||||||
|
use App\Models\Design;
|
||||||
|
use App\Models\Document;
|
||||||
|
use App\Models\Expense;
|
||||||
|
use App\Models\ExpenseCategory;
|
||||||
|
use App\Models\Gateway;
|
||||||
|
use App\Models\GroupSetting;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\InvoiceInvitation;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\PaymentHash;
|
||||||
|
use App\Models\Paymentable;
|
||||||
|
use App\Models\Product;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\Quote;
|
||||||
|
use App\Models\QuoteInvitation;
|
||||||
|
use App\Models\RecurringInvoice;
|
||||||
|
use App\Models\RecurringInvoiceInvitation;
|
||||||
|
use App\Models\Subscription;
|
||||||
|
use App\Models\SystemLog;
|
||||||
|
use App\Models\Task;
|
||||||
|
use App\Models\TaskStatus;
|
||||||
|
use App\Models\TaxRate;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Vendor;
|
||||||
|
use App\Models\VendorContact;
|
||||||
|
use App\Models\Webhook;
|
||||||
|
use App\Utils\Ninja;
|
||||||
|
use DB;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Mail;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CheckDb.
|
||||||
|
*/
|
||||||
|
class CheckDb extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'ninja:check-db';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Check MultiDB';
|
||||||
|
|
||||||
|
|
||||||
|
protected $log = '';
|
||||||
|
|
||||||
|
|
||||||
|
private $entities = [
|
||||||
|
Account::class,
|
||||||
|
Activity::class,
|
||||||
|
Backup::class,
|
||||||
|
Client::class,
|
||||||
|
ClientContact::class,
|
||||||
|
ClientGatewayToken::class,
|
||||||
|
Company::class,
|
||||||
|
CompanyGateway::class,
|
||||||
|
CompanyLedger::class,
|
||||||
|
CompanyToken::class,
|
||||||
|
CompanyUser::class,
|
||||||
|
Credit::class,
|
||||||
|
CreditInvitation::class,
|
||||||
|
Design::class,
|
||||||
|
Document::class,
|
||||||
|
Expense::class,
|
||||||
|
ExpenseCategory::class,
|
||||||
|
Gateway::class,
|
||||||
|
GroupSetting::class,
|
||||||
|
Invoice::class,
|
||||||
|
InvoiceInvitation::class,
|
||||||
|
Payment::class,
|
||||||
|
Paymentable::class,
|
||||||
|
PaymentHash::class,
|
||||||
|
Product::class,
|
||||||
|
Project::class,
|
||||||
|
Quote::class,
|
||||||
|
QuoteInvitation::class,
|
||||||
|
RecurringInvoice::class,
|
||||||
|
RecurringInvoiceInvitation::class,
|
||||||
|
Subscription::class,
|
||||||
|
SystemLog::class,
|
||||||
|
Task::class,
|
||||||
|
TaskStatus::class,
|
||||||
|
TaxRate::class,
|
||||||
|
User::class,
|
||||||
|
Vendor::class,
|
||||||
|
VendorContact::class,
|
||||||
|
WebHook::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->LogMessage("Checking - V5_DB1");
|
||||||
|
|
||||||
|
foreach($this->entities as $entity) {
|
||||||
|
|
||||||
|
$count_db_1 = $entity::on('db-ninja-01')->count();
|
||||||
|
$count_db_2 = $entity::on('db-ninja-02a')->count();
|
||||||
|
|
||||||
|
$diff = $count_db_1 - $count_db_2;
|
||||||
|
|
||||||
|
if($diff != 0)
|
||||||
|
$this->logMessage("{$entity} DB1: {$count_db_1} - DB2: {$count_db_2} - diff = {$diff}");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->LogMessage("Checking - V5_DB2");
|
||||||
|
|
||||||
|
foreach($this->entities as $entity) {
|
||||||
|
|
||||||
|
$count_db_1 = $entity::on('db-ninja-02')->count();
|
||||||
|
$count_db_2 = $entity::on('db-ninja-01a')->count();
|
||||||
|
|
||||||
|
$diff = $count_db_1 - $count_db_2;
|
||||||
|
|
||||||
|
if($diff != 0)
|
||||||
|
$this->logMessage("{$entity} DB1: {$count_db_1} - DB2: {$count_db_2} - diff = {$diff}");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function logMessage($str)
|
||||||
|
{
|
||||||
|
$str = date('Y-m-d h:i:s').' '.$str;
|
||||||
|
$this->info($str);
|
||||||
|
$this->log .= $str."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
app/Console/Commands/HostedUsers.php
Normal file
61
app/Console/Commands/HostedUsers.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\ClientContact;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Utils\Ninja;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class HostedUsers extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'ninja:sync-users';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Syncs Invoice Ninja Users';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
|
||||||
|
Company::on('db-ninja-01')->each(function ($company){
|
||||||
|
|
||||||
|
if(Ninja::isHosted())
|
||||||
|
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow([], $company);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Company::on('db-ninja-02')->each(function ($company){
|
||||||
|
|
||||||
|
if(Ninja::isHosted())
|
||||||
|
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow([], $company);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
71
app/Console/Commands/S3Cleanup.php
Normal file
71
app/Console/Commands/S3Cleanup.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class S3Cleanup extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'ninja:s3-cleanup';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Remove orphan folders';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
|
||||||
|
$c1 = Company::on('db-ninja-01')->pluck('company_key');
|
||||||
|
$c2 = Company::on('db-ninja-02')->pluck('company_key');
|
||||||
|
|
||||||
|
$merged = $c1->merge($c2)->toArray();
|
||||||
|
|
||||||
|
$directories = Storage::disk(config('filesystems.default'))->directories();
|
||||||
|
|
||||||
|
$this->LogMessage("Disk Cleanup");
|
||||||
|
|
||||||
|
foreach($directories as $dir)
|
||||||
|
{
|
||||||
|
if(!in_array($dir, $merged))
|
||||||
|
{
|
||||||
|
$this->logMessage("Deleting $dir");
|
||||||
|
Storage::disk(config('filesystems.default'))->deleteDirectory($dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logMessage("exiting");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function logMessage($str)
|
||||||
|
{
|
||||||
|
$str = date('Y-m-d h:i:s').' '.$str;
|
||||||
|
$this->info($str);
|
||||||
|
$this->log .= $str."\n";
|
||||||
|
}
|
||||||
|
}
|
@ -89,7 +89,7 @@ class SendRemindersCron extends Command
|
|||||||
->cursor();
|
->cursor();
|
||||||
|
|
||||||
$invoices->each(function ($invoice) {
|
$invoices->each(function ($invoice) {
|
||||||
WebHookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
|
WebhookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ class SendRemindersCron extends Command
|
|||||||
->cursor();
|
->cursor();
|
||||||
|
|
||||||
$quotes->each(function ($quote) {
|
$quotes->each(function ($quote) {
|
||||||
WebHookHandler::dispatch(Webhook::EVENT_EXPIRED_QUOTE, $quote, $quote->company);
|
WebhookHandler::dispatch(Webhook::EVENT_EXPIRED_QUOTE, $quote, $quote->company);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ use App\Factory\InvoiceInvitationFactory;
|
|||||||
use App\Jobs\Invoice\CreateEntityPdf;
|
use App\Jobs\Invoice\CreateEntityPdf;
|
||||||
use App\Jobs\Mail\NinjaMailerJob;
|
use App\Jobs\Mail\NinjaMailerJob;
|
||||||
use App\Jobs\Mail\NinjaMailerObject;
|
use App\Jobs\Mail\NinjaMailerObject;
|
||||||
use App\Mail\DownloadInvoices;
|
|
||||||
use App\Mail\Migration\MaxCompanies;
|
use App\Mail\Migration\MaxCompanies;
|
||||||
use App\Mail\TemplateEmail;
|
use App\Mail\TemplateEmail;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
@ -92,107 +91,11 @@ class SendTestEmails extends Command
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$nmo = new NinjaMailerObject;
|
$nmo = new NinjaMailerObject;
|
||||||
$nmo->mailable = new DownloadInvoices('https://google.com', $user->account->companies()->first());
|
$nmo->mailable = new MaxCompanies($user->account->companies()->first());
|
||||||
$nmo->company = $user->account->companies()->first();
|
$nmo->company = $user->account->companies()->first();
|
||||||
$nmo->settings = $user->account->companies()->first()->settings;
|
$nmo->settings = $user->account->companies()->first()->settings;
|
||||||
$nmo->to_user = $user;
|
$nmo->to_user = $user;
|
||||||
|
|
||||||
NinjaMailerJob::dispatch($nmo);
|
NinjaMailerJob::dispatchNow($nmo);
|
||||||
|
|
||||||
// $this->sendTemplateEmails('plain');
|
|
||||||
// $this->sendTemplateEmails('light');
|
|
||||||
// $this->sendTemplateEmails('dark');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sendTemplateEmails($template)
|
|
||||||
{
|
|
||||||
$faker = Factory::create();
|
|
||||||
|
|
||||||
$message = [
|
|
||||||
'title' => 'Invoice XJ-3838',
|
|
||||||
'body' => '<div>"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"</div>',
|
|
||||||
'subject' => 'The Test Subject',
|
|
||||||
'footer' => 'Lovely Footer Texts',
|
|
||||||
];
|
|
||||||
|
|
||||||
$user = User::whereEmail('user@example.com')->first();
|
|
||||||
|
|
||||||
if (! $user) {
|
|
||||||
$account = Account::factory()->create();
|
|
||||||
|
|
||||||
$user = User::factory()->create([
|
|
||||||
'account_id' => $account->id,
|
|
||||||
'confirmation_code' => '123',
|
|
||||||
'email' => $faker->safeEmail,
|
|
||||||
'first_name' => 'John',
|
|
||||||
'last_name' => 'Doe',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$company = Company::factory()->create([
|
|
||||||
'account_id' => $account->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$user->companies()->attach($company->id, [
|
|
||||||
'account_id' => $account->id,
|
|
||||||
'is_owner' => 1,
|
|
||||||
'is_admin' => 1,
|
|
||||||
'is_locked' => 0,
|
|
||||||
'permissions' => '',
|
|
||||||
'notifications' => CompanySettings::notificationDefaults(),
|
|
||||||
//'settings' => DefaultSettings::userSettings(),
|
|
||||||
'settings' => null,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
$company = $user->company_users->first()->company;
|
|
||||||
$account = $company->account;
|
|
||||||
}
|
|
||||||
|
|
||||||
$client = Client::all()->first();
|
|
||||||
|
|
||||||
if (! $client) {
|
|
||||||
$client = ClientFactory::create($company->id, $user->id);
|
|
||||||
$client->save();
|
|
||||||
|
|
||||||
ClientContact::factory()->create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'client_id' => $client->id,
|
|
||||||
'company_id' => $company->id,
|
|
||||||
'is_primary' => 1,
|
|
||||||
'send_email' => true,
|
|
||||||
'email' => $faker->safeEmail,
|
|
||||||
]);
|
|
||||||
|
|
||||||
ClientContact::factory()->create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'client_id' => $client->id,
|
|
||||||
'company_id' => $company->id,
|
|
||||||
'send_email' => true,
|
|
||||||
'email' => $faker->safeEmail,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$invoice = InvoiceFactory::create($company->id, $user->id);
|
|
||||||
$invoice->client_id = $client->id;
|
|
||||||
$invoice->setRelation('client', $client);
|
|
||||||
$invoice->save();
|
|
||||||
|
|
||||||
$ii = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id);
|
|
||||||
$ii->invoice_id = $invoice->id;
|
|
||||||
$ii->client_contact_id = $client->primary_contact()->first()->id;
|
|
||||||
$ii->save();
|
|
||||||
|
|
||||||
$invoice->setRelation('invitations', $ii);
|
|
||||||
$invoice->service()->markSent()->save();
|
|
||||||
|
|
||||||
CreateEntityPdf::dispatch($invoice->invitations()->first());
|
|
||||||
|
|
||||||
$cc_emails = [config('ninja.testvars.test_email')];
|
|
||||||
$bcc_emails = [config('ninja.testvars.test_email')];
|
|
||||||
|
|
||||||
|
|
||||||
$email_builder->setFooter($message['footer'])
|
|
||||||
->setSubject($message['subject'])
|
|
||||||
->setBody($message['body']);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
$schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
|
$schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
|
||||||
|
|
||||||
$schedule->job(new ReminderJob)->daily()->withoutOverlapping();
|
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping();
|
||||||
|
|
||||||
$schedule->job(new CompanySizeCheck)->daily()->withoutOverlapping();
|
$schedule->job(new CompanySizeCheck)->daily()->withoutOverlapping();
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled')) {
|
if(config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled') && !config('ninja.is_docker')) {
|
||||||
|
|
||||||
$schedule->command('queue:work')->everyMinute()->withoutOverlapping();
|
$schedule->command('queue:work')->everyMinute()->withoutOverlapping();
|
||||||
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
|
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
|
||||||
|
@ -65,6 +65,8 @@ class CompanySettings extends BaseSettings
|
|||||||
public $auto_convert_quote = true; //@implemented
|
public $auto_convert_quote = true; //@implemented
|
||||||
public $auto_email_invoice = true; //@only used for Recurring Invoices, if set to false, we never send?
|
public $auto_email_invoice = true; //@only used for Recurring Invoices, if set to false, we never send?
|
||||||
|
|
||||||
|
public $entity_send_time = 0;
|
||||||
|
|
||||||
public $inclusive_taxes = false; //@implemented
|
public $inclusive_taxes = false; //@implemented
|
||||||
public $quote_footer = ''; //@implmented
|
public $quote_footer = ''; //@implmented
|
||||||
|
|
||||||
@ -266,6 +268,7 @@ class CompanySettings extends BaseSettings
|
|||||||
public $hide_empty_columns_on_pdf = false;
|
public $hide_empty_columns_on_pdf = false;
|
||||||
|
|
||||||
public static $casts = [
|
public static $casts = [
|
||||||
|
'entity_send_time' => 'int',
|
||||||
'shared_invoice_credit_counter' => 'bool',
|
'shared_invoice_credit_counter' => 'bool',
|
||||||
'reply_to_name' => 'string',
|
'reply_to_name' => 'string',
|
||||||
'hide_empty_columns_on_pdf' => 'bool',
|
'hide_empty_columns_on_pdf' => 'bool',
|
||||||
|
@ -74,14 +74,12 @@ class GmailTransport extends Transport
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->gmail->send();
|
$this->gmail->send();
|
||||||
|
|
||||||
$this->sendPerformed($message);
|
$this->sendPerformed($message);
|
||||||
|
|
||||||
|
|
||||||
return $this->numberOfRecipients($message);
|
return $this->numberOfRecipients($message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://opensource.org/licenses/AAL
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Helpers\Mail;
|
|
||||||
|
|
||||||
use App\Libraries\MultiDB;
|
|
||||||
use App\Mail\SupportMessageSent;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Providers\MailServiceProvider;
|
|
||||||
use Illuminate\Support\Facades\Config;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
use Laravel\Socialite\Facades\Socialite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GmailTransportConfig.
|
|
||||||
*/
|
|
||||||
class GmailTransportConfig
|
|
||||||
{
|
|
||||||
public function test()
|
|
||||||
{
|
|
||||||
/********************* We may need to fetch a new token on behalf of the client ******************************/
|
|
||||||
$query = [
|
|
||||||
'email' => 'david@invoiceninja.com',
|
|
||||||
];
|
|
||||||
|
|
||||||
$user = MultiDB::hasUser($query);
|
|
||||||
// $oauth_user = Socialite::driver('google')->stateless()->userFromToken($user->oauth_user_token);
|
|
||||||
|
|
||||||
// $user->oauth_user_token = $oauth_user->refreshToken;
|
|
||||||
// $user->save();
|
|
||||||
|
|
||||||
Config::set('mail.driver', 'gmail');
|
|
||||||
Config::set('services.gmail.token', $user->oauth_user_token);
|
|
||||||
(new MailServiceProvider(app()))->register();
|
|
||||||
|
|
||||||
Mail::to('david@romulus.com.au')
|
|
||||||
->send(new SupportMessageSent('a cool message'));
|
|
||||||
}
|
|
||||||
}
|
|
@ -213,15 +213,19 @@ class LoginController extends BaseController
|
|||||||
if(!$cu->exists())
|
if(!$cu->exists())
|
||||||
return response()->json(['message' => 'User not linked to any companies'], 403);
|
return response()->json(['message' => 'User not linked to any companies'], 403);
|
||||||
|
|
||||||
$cu->first()->account->companies->each(function ($company) use($cu, $request){
|
/* Ensure the user has a valid token */
|
||||||
|
$user->company_users->each(function ($company_user) use($request){
|
||||||
|
|
||||||
if($company->tokens()->where('is_system', true)->count() == 0)
|
if($company_user->tokens->count() == 0){
|
||||||
{
|
CreateCompanyToken::dispatchNow($company_user->company, $company_user->user, $request->server('HTTP_USER_AGENT'));
|
||||||
CreateCompanyToken::dispatchNow($company, $cu->first()->user, $request->server('HTTP_USER_AGENT'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*On the hosted platform, only owners can login for free/pro accounts*/
|
||||||
|
if(Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
|
||||||
|
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||||
|
|
||||||
return $this->timeConstrainedResponse($cu);
|
return $this->timeConstrainedResponse($cu);
|
||||||
|
|
||||||
|
|
||||||
@ -309,6 +313,9 @@ class LoginController extends BaseController
|
|||||||
if($request->has('current_company') && $request->input('current_company') == 'true')
|
if($request->has('current_company') && $request->input('current_company') == 'true')
|
||||||
$cu->where("company_id", $company_token->company_id);
|
$cu->where("company_id", $company_token->company_id);
|
||||||
|
|
||||||
|
if(Ninja::isHosted() && !$cu->first()->is_owner && !$cu->first()->user->account->isEnterpriseClient())
|
||||||
|
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||||
|
|
||||||
return $this->refreshResponse($cu);
|
return $this->refreshResponse($cu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,6 +377,9 @@ class LoginController extends BaseController
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient())
|
||||||
|
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||||
|
|
||||||
return $this->timeConstrainedResponse($cu);
|
return $this->timeConstrainedResponse($cu);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -398,6 +408,9 @@ class LoginController extends BaseController
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
|
||||||
|
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||||
|
|
||||||
return $this->timeConstrainedResponse($cu);
|
return $this->timeConstrainedResponse($cu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,6 +443,9 @@ class LoginController extends BaseController
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
|
||||||
|
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||||
|
|
||||||
return $this->timeConstrainedResponse($cu);
|
return $this->timeConstrainedResponse($cu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,6 +485,9 @@ class LoginController extends BaseController
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient())
|
||||||
|
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||||
|
|
||||||
return $this->timeConstrainedResponse($cu);
|
return $this->timeConstrainedResponse($cu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,38 +499,54 @@ class LoginController extends BaseController
|
|||||||
|
|
||||||
public function redirectToProvider(string $provider)
|
public function redirectToProvider(string $provider)
|
||||||
{
|
{
|
||||||
//'https://www.googleapis.com/auth/gmail.send','email','profile','openid'
|
|
||||||
$scopes = [];
|
$scopes = [];
|
||||||
|
|
||||||
|
$parameters = [];
|
||||||
|
|
||||||
if($provider == 'google'){
|
if($provider == 'google'){
|
||||||
|
|
||||||
$scopes = ['https://www.googleapis.com/auth/gmail.send','email','profile','openid'];
|
$scopes = ['https://www.googleapis.com/auth/gmail.send','email','profile','openid'];
|
||||||
|
$parameters = ['access_type' => 'offline', "prompt" => "consent select_account", 'redirect_uri' => config('ninja.app_url')."/auth/google"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request()->has('code')) {
|
if (request()->has('code')) {
|
||||||
return $this->handleProviderCallback($provider);
|
return $this->handleProviderCallback($provider);
|
||||||
} else {
|
} else {
|
||||||
return Socialite::driver($provider)->with(['redirect_uri' => config('ninja.app_url')."/auth/google"])->scopes($scopes)->redirect();
|
return Socialite::driver($provider)->with($parameters)->scopes($scopes)->redirect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleProviderCallback(string $provider)
|
public function handleProviderCallback(string $provider)
|
||||||
{
|
{
|
||||||
$socialite_user = Socialite::driver($provider)
|
$socialite_user = Socialite::driver($provider)->user();
|
||||||
->user();
|
|
||||||
|
$oauth_user_token = '';
|
||||||
|
|
||||||
|
if($socialite_user->refreshToken){
|
||||||
|
|
||||||
|
$client = new Google_Client();
|
||||||
|
$client->setClientId(config('ninja.auth.google.client_id'));
|
||||||
|
$client->setClientSecret(config('ninja.auth.google.client_secret'));
|
||||||
|
$client->fetchAccessTokenWithRefreshToken($socialite_user->refreshToken);
|
||||||
|
$oauth_user_token = $client->getAccessToken();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if($user = OAuth::handleAuth($socialite_user, $provider))
|
if($user = OAuth::handleAuth($socialite_user, $provider))
|
||||||
{
|
{
|
||||||
|
|
||||||
nlog('found user and updating their user record');
|
nlog('found user and updating their user record');
|
||||||
|
$name = OAuth::splitName($socialite_user->getName());
|
||||||
|
|
||||||
$update_user = [
|
$update_user = [
|
||||||
'first_name' => $name[0],
|
'first_name' => $name[0],
|
||||||
'last_name' => $name[1],
|
'last_name' => $name[1],
|
||||||
'password' => '',
|
|
||||||
'email' => $socialite_user->getEmail(),
|
'email' => $socialite_user->getEmail(),
|
||||||
'oauth_user_id' => $socialite_user->getId(),
|
'oauth_user_id' => $socialite_user->getId(),
|
||||||
'oauth_provider_id' => $provider,
|
'oauth_provider_id' => $provider,
|
||||||
'oauth_user_token' => $socialite_user->refreshToken,
|
'oauth_user_token' => $oauth_user_token,
|
||||||
|
'oauth_user_refresh_token' => $socialite_user->refreshToken
|
||||||
];
|
];
|
||||||
|
|
||||||
$user->update($update_user);
|
$user->update($update_user);
|
||||||
|
@ -379,6 +379,15 @@ class ClientController extends BaseController
|
|||||||
|
|
||||||
$client->load('contacts', 'primary_contact');
|
$client->load('contacts', 'primary_contact');
|
||||||
|
|
||||||
|
/* Set the client country to the company if none is set */
|
||||||
|
if(!$client->country_id && strlen($client->company->settings->country_id) > 1){
|
||||||
|
|
||||||
|
$client->country_id = $client->company->settings->country_id;
|
||||||
|
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$this->uploadLogo($request->file('company_logo'), $client->company, $client);
|
$this->uploadLogo($request->file('company_logo'), $client->company, $client);
|
||||||
|
|
||||||
event(new ClientWasCreated($client, $client->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
event(new ClientWasCreated($client, $client->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||||
|
@ -55,18 +55,21 @@ class InvitationController extends Controller
|
|||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
|
|
||||||
/* Return early if we have the correct client_hash embedded */
|
/* Return early if we have the correct client_hash embedded */
|
||||||
|
$client_contact = $invitation->contact;
|
||||||
|
|
||||||
|
if(empty($client_contact->email))
|
||||||
|
$client_contact->email = Str::random(15) . "@example.com"; $client_contact->save();
|
||||||
|
|
||||||
if (request()->has('client_hash') && request()->input('client_hash') == $invitation->contact->client->client_hash) {
|
if (request()->has('client_hash') && request()->input('client_hash') == $invitation->contact->client->client_hash) {
|
||||||
auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
|
auth()->guard('contact')->login($client_contact, true);
|
||||||
|
|
||||||
} elseif ((bool) $invitation->contact->client->getSetting('enable_client_portal_password') !== false) {
|
} elseif ((bool) $invitation->contact->client->getSetting('enable_client_portal_password') !== false) {
|
||||||
|
|
||||||
//If no contact password is set - this will cause a 401 error - instead redirect to the client.login route
|
|
||||||
$this->middleware('auth:contact');
|
$this->middleware('auth:contact');
|
||||||
return redirect()->route('client.login');
|
return redirect()->route('client.login');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
|
nlog("else - default - login contact");
|
||||||
|
auth()->guard('contact')->login($client_contact, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,8 +164,9 @@ class InvoiceController extends Controller
|
|||||||
|
|
||||||
//if only 1 pdf, output to buffer for download
|
//if only 1 pdf, output to buffer for download
|
||||||
if ($invoices->count() == 1) {
|
if ($invoices->count() == 1) {
|
||||||
|
$invoice = $invoices->first();
|
||||||
$file = $invoices->first()->pdf_file_path();
|
$invitation = $invoice->invitations->first();
|
||||||
|
$file = $invoice->pdf_file_path($invitation);
|
||||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ class EmailController extends BaseController
|
|||||||
$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(5));
|
->delay(now()->addSeconds(30));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,9 +66,11 @@ class ImportJsonController extends BaseController
|
|||||||
|
|
||||||
$hash = Str::random(32);
|
$hash = Str::random(32);
|
||||||
|
|
||||||
|
nlog($hash);
|
||||||
|
|
||||||
Cache::put( $hash, base64_encode( $contents ), 3600 );
|
Cache::put( $hash, base64_encode( $contents ), 3600 );
|
||||||
|
|
||||||
CompanyImport::dispatch(auth()->user()->getCompany(), auth()->user(), $hash, $request->except('files'));
|
CompanyImport::dispatch(auth()->user()->getCompany(), auth()->user(), $hash, $request->except('files'))->delay(now()->addMinutes(1));
|
||||||
|
|
||||||
return response()->json(['message' => 'Processing'], 200);
|
return response()->json(['message' => 'Processing'], 200);
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ use App\Transformers\RecurringInvoiceTransformer;
|
|||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use App\Utils\Traits\SavesDocuments;
|
use App\Utils\Traits\SavesDocuments;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
@ -205,6 +206,10 @@ class RecurringInvoiceController extends BaseController
|
|||||||
|
|
||||||
event(new RecurringInvoiceWasCreated($recurring_invoice, $recurring_invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
event(new RecurringInvoiceWasCreated($recurring_invoice, $recurring_invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||||
|
|
||||||
|
$offset = $recurring_invoice->client->timezone_offset();
|
||||||
|
$recurring_invoice->next_send_date = Carbon::parse($recurring_invoice->next_send_date)->startOfDay()->addSeconds($offset);
|
||||||
|
$recurring_invoice->save();
|
||||||
|
|
||||||
return $this->itemResponse($recurring_invoice);
|
return $this->itemResponse($recurring_invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Credit;
|
use App\Models\Credit;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -24,6 +25,13 @@ class CreditsTable extends Component
|
|||||||
|
|
||||||
public $per_page = 10;
|
public $per_page = 10;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$query = Credit::query()
|
$query = Credit::query()
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -25,8 +26,13 @@ class DocumentsTable extends Component
|
|||||||
|
|
||||||
public $per_page = 10;
|
public $per_page = 10;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
public function mount($client)
|
public function mount($client)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
@ -26,8 +27,12 @@ class InvoicesTable extends Component
|
|||||||
|
|
||||||
public $status = [];
|
public $status = [];
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->sort_asc = false;
|
$this->sort_asc = false;
|
||||||
|
|
||||||
$this->sort_field = 'date';
|
$this->sort_field = 'date';
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class PayNowDropdown extends Component
|
class PayNowDropdown extends Component
|
||||||
@ -20,8 +21,12 @@ class PayNowDropdown extends Component
|
|||||||
|
|
||||||
public $methods;
|
public $methods;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
public function mount(int $total)
|
public function mount(int $total)
|
||||||
{
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->total = $total;
|
$this->total = $total;
|
||||||
|
|
||||||
$this->methods = auth()->user()->client->service()->getPaymentMethods($total);
|
$this->methods = auth()->user()->client->service()->getPaymentMethods($total);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\ClientGatewayToken;
|
use App\Models\ClientGatewayToken;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -16,10 +17,16 @@ class PaymentMethodsTable extends Component
|
|||||||
use WithSorting;
|
use WithSorting;
|
||||||
|
|
||||||
public $per_page = 10;
|
public $per_page = 10;
|
||||||
|
|
||||||
public $client;
|
public $client;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
public function mount($client)
|
public function mount($client)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -23,11 +24,17 @@ class PaymentsTable extends Component
|
|||||||
use WithPagination;
|
use WithPagination;
|
||||||
|
|
||||||
public $per_page = 10;
|
public $per_page = 10;
|
||||||
|
|
||||||
public $user;
|
public $user;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->user = auth()->user();
|
$this->user = auth()->user();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -23,8 +24,17 @@ class QuotesTable extends Component
|
|||||||
use WithPagination;
|
use WithPagination;
|
||||||
|
|
||||||
public $per_page = 10;
|
public $per_page = 10;
|
||||||
|
|
||||||
public $status = [];
|
public $status = [];
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$query = Quote::query()
|
$query = Quote::query()
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\RecurringInvoice;
|
use App\Models\RecurringInvoice;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@ -22,6 +23,18 @@ class RecurringInvoiceCancellation extends Component
|
|||||||
*/
|
*/
|
||||||
public $invoice;
|
public $invoice;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return render('components.livewire.recurring-invoice-cancellation');
|
||||||
|
}
|
||||||
|
|
||||||
public function processCancellation()
|
public function processCancellation()
|
||||||
{
|
{
|
||||||
if ($this->invoice->subscription) {
|
if ($this->invoice->subscription) {
|
||||||
@ -31,8 +44,5 @@ class RecurringInvoiceCancellation extends Component
|
|||||||
return redirect()->route('client.recurring_invoices.request_cancellation', ['recurring_invoice' => $this->invoice->hashed_id]);
|
return redirect()->route('client.recurring_invoices.request_cancellation', ['recurring_invoice' => $this->invoice->hashed_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return render('components.livewire.recurring-invoice-cancellation');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\ClientContact;
|
use App\Models\ClientContact;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@ -65,7 +66,12 @@ class RequiredClientInfo extends Component
|
|||||||
|
|
||||||
public $show_form = false;
|
public $show_form = false;
|
||||||
|
|
||||||
public function mount() {}
|
public $company;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
}
|
||||||
|
|
||||||
public function handleSubmit(array $data): bool
|
public function handleSubmit(array $data): bool
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\ClientContact;
|
use App\Models\ClientContact;
|
||||||
use App\Models\Subscription;
|
use App\Models\Subscription;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
@ -71,8 +72,12 @@ class SubscriptionPlanSwitch extends Component
|
|||||||
*/
|
*/
|
||||||
public $hash;
|
public $hash;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->total = $this->amount;
|
$this->total = $this->amount;
|
||||||
|
|
||||||
$this->methods = $this->contact->client->service()->getPaymentMethods($this->amount);
|
$this->methods = $this->contact->client->service()->getPaymentMethods($this->amount);
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\RecurringInvoice;
|
use App\Models\RecurringInvoice;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -24,6 +25,13 @@ class SubscriptionRecurringInvoicesTable extends Component
|
|||||||
|
|
||||||
public $per_page = 10;
|
public $per_page = 10;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$query = RecurringInvoice::query()
|
$query = RecurringInvoice::query()
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Task;
|
use App\Models\Task;
|
||||||
use App\Utils\Traits\WithSorting;
|
use App\Utils\Traits\WithSorting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -24,6 +25,13 @@ class TasksTable extends Component
|
|||||||
|
|
||||||
public $per_page = 10;
|
public $per_page = 10;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$query = Task::query()
|
$query = Task::query()
|
||||||
|
@ -46,7 +46,7 @@ class ContactKeyLogin
|
|||||||
if($client_contact = ClientContact::where('email', $contact_email)->where('company_id', $payload['company_id'])->first()){
|
if($client_contact = ClientContact::where('email', $contact_email)->where('company_id', $payload['company_id'])->first()){
|
||||||
|
|
||||||
if(empty($client_contact->email))
|
if(empty($client_contact->email))
|
||||||
$client_contact->email = Str::random(6) . "@example.com"; $client_contact->save();
|
$client_contact->email = Str::random(15) . "@example.com"; $client_contact->save();
|
||||||
|
|
||||||
auth()->guard('contact')->login($client_contact, true);
|
auth()->guard('contact')->login($client_contact, true);
|
||||||
|
|
||||||
|
@ -58,6 +58,9 @@ class PasswordProtection
|
|||||||
$google = new Google();
|
$google = new Google();
|
||||||
$user = $google->getTokenResponse(request()->header('X-API-OAUTH-PASSWORD'));
|
$user = $google->getTokenResponse(request()->header('X-API-OAUTH-PASSWORD'));
|
||||||
|
|
||||||
|
nlog("user");
|
||||||
|
nlog($user);
|
||||||
|
|
||||||
if (is_array($user)) {
|
if (is_array($user)) {
|
||||||
|
|
||||||
$query = [
|
$query = [
|
||||||
@ -65,14 +68,20 @@ class PasswordProtection
|
|||||||
'oauth_provider_id'=> 'google'
|
'oauth_provider_id'=> 'google'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
nlog($query);
|
||||||
|
|
||||||
//If OAuth and user also has a password set - check both
|
//If OAuth and user also has a password set - check both
|
||||||
if ($existing_user = MultiDB::hasUser($query) && auth()->user()->has_password && Hash::check(auth()->user()->password, $request->header('X-API-PASSWORD'))) {
|
if ($existing_user = MultiDB::hasUser($query) && auth()->user()->company()->oauth_password_required && auth()->user()->has_password && Hash::check(auth()->user()->password, $request->header('X-API-PASSWORD'))) {
|
||||||
|
|
||||||
|
nlog("existing user with password");
|
||||||
|
|
||||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->has_password){
|
elseif($existing_user = MultiDB::hasUser($query) && !auth()->user()->company()->oauth_password_required){
|
||||||
|
|
||||||
|
nlog("existing user without password");
|
||||||
|
|
||||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
@ -49,7 +49,7 @@ class StoreCompanyRequest extends Request
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
if(Ninja::isHosted()){
|
if(Ninja::isHosted()){
|
||||||
$rules['subdomain'] = ['nullable', 'alpha_num', new ValidSubdomain($this->all())];
|
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain($this->all())];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
$rules['subdomain'] = 'nullable|alpha_num';
|
$rules['subdomain'] = 'nullable|alpha_num';
|
||||||
|
@ -50,7 +50,7 @@ class UpdateCompanyRequest extends Request
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
if(Ninja::isHosted()){
|
if(Ninja::isHosted()){
|
||||||
$rules['subdomain'] = ['nullable', 'alpha_num', new ValidSubdomain($this->all())];
|
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain($this->all())];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
$rules['subdomain'] = 'nullable|alpha_num';
|
$rules['subdomain'] = 'nullable|alpha_num';
|
||||||
|
@ -14,10 +14,12 @@ namespace App\Http\Requests\User;
|
|||||||
use App\DataMapper\DefaultSettings;
|
use App\DataMapper\DefaultSettings;
|
||||||
use App\Factory\UserFactory;
|
use App\Factory\UserFactory;
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
|
use App\Http\ValidationRules\Ninja\CanAddUserRule;
|
||||||
use App\Http\ValidationRules\User\AttachableUser;
|
use App\Http\ValidationRules\User\AttachableUser;
|
||||||
use App\Http\ValidationRules\ValidUserForCompany;
|
use App\Http\ValidationRules\ValidUserForCompany;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Utils\Ninja;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class StoreUserRequest extends Request
|
class StoreUserRequest extends Request
|
||||||
@ -45,8 +47,7 @@ class StoreUserRequest extends Request
|
|||||||
$rules['email'] = ['email', new AttachableUser()];
|
$rules['email'] = ['email', new AttachableUser()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Ninja::isHosted()) {
|
||||||
if (auth()->user()->company()->account->isFreeHostedClient()) {
|
|
||||||
$rules['hosted_users'] = new CanAddUserRule(auth()->user()->company()->account);
|
$rules['hosted_users'] = new CanAddUserRule(auth()->user()->company()->account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,10 @@ class CreateAccount
|
|||||||
//todo implement SLACK notifications
|
//todo implement SLACK notifications
|
||||||
//$sp035a66->notification(new NewAccountCreated($spaa9f78, $sp035a66))->ninja();
|
//$sp035a66->notification(new NewAccountCreated($spaa9f78, $sp035a66))->ninja();
|
||||||
|
|
||||||
VersionCheck::dispatchNow();
|
if(Ninja::isHosted())
|
||||||
|
\Modules\Admin\Jobs\Account\NinjaUser::dispatch([], $sp035a66);
|
||||||
|
|
||||||
|
VersionCheck::dispatch();
|
||||||
|
|
||||||
LightLogs::create(new AnalyticsAccountCreated())
|
LightLogs::create(new AnalyticsAccountCreated())
|
||||||
->increment()
|
->increment()
|
||||||
@ -118,10 +121,6 @@ class CreateAccount
|
|||||||
if(Ninja::isHosted() && Cache::get('currencies'))
|
if(Ninja::isHosted() && Cache::get('currencies'))
|
||||||
{
|
{
|
||||||
|
|
||||||
//&& $data = unserialize(@file_get_contents('http://www.geoplugin.net/php.gp?ip=' . $this->client_ip))
|
|
||||||
// $currency_code = strtolower($data['geoplugin_currencyCode']);
|
|
||||||
// $country_code = strtolower($data['geoplugin_countryCode']);
|
|
||||||
|
|
||||||
$currency = Cache::get('currencies')->filter(function ($item) use ($currency_code) {
|
$currency = Cache::get('currencies')->filter(function ($item) use ($currency_code) {
|
||||||
return strtolower($item->code) == $currency_code;
|
return strtolower($item->code) == $currency_code;
|
||||||
})->first();
|
})->first();
|
||||||
@ -146,8 +145,6 @@ class CreateAccount
|
|||||||
$settings->language_id = (string)$language->id;
|
$settings->language_id = (string)$language->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
//$timezone = Timezone::where('name', $data['geoplugin_timezone'])->first();
|
|
||||||
|
|
||||||
if($timezone) {
|
if($timezone) {
|
||||||
$settings->timezone_id = (string)$timezone->id;
|
$settings->timezone_id = (string)$timezone->id;
|
||||||
}
|
}
|
||||||
|
@ -480,7 +480,11 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
|
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
|
||||||
|
|
||||||
Storage::makeDirectory(public_path('storage/backups/'), 0775);
|
$path = 'backups';
|
||||||
|
|
||||||
|
if(!Storage::disk(config('filesystems.default'))->exists($path))
|
||||||
|
Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775);
|
||||||
|
|
||||||
$zip_path = public_path('storage/backups/'.$file_name);
|
$zip_path = public_path('storage/backups/'.$file_name);
|
||||||
$zip = new \ZipArchive();
|
$zip = new \ZipArchive();
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ use App\Jobs\Util\UnlinkFile;
|
|||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Mail\DownloadBackup;
|
use App\Mail\DownloadBackup;
|
||||||
use App\Mail\DownloadInvoices;
|
use App\Mail\DownloadInvoices;
|
||||||
|
use App\Mail\Import\CompanyImportFailure;
|
||||||
use App\Models\Activity;
|
use App\Models\Activity;
|
||||||
use App\Models\Backup;
|
use App\Models\Backup;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
@ -88,6 +89,14 @@ class CompanyImport implements ShouldQueue
|
|||||||
|
|
||||||
private $request_array = [];
|
private $request_array = [];
|
||||||
|
|
||||||
|
public $message = '';
|
||||||
|
|
||||||
|
public $pre_flight_checks_pass = true;
|
||||||
|
|
||||||
|
public $force_user_coalesce = false;
|
||||||
|
|
||||||
|
public $company_owner;
|
||||||
|
|
||||||
private $importables = [
|
private $importables = [
|
||||||
// 'company',
|
// 'company',
|
||||||
'users',
|
'users',
|
||||||
@ -136,6 +145,7 @@ class CompanyImport implements ShouldQueue
|
|||||||
public function __construct(Company $company, User $user, string $hash, array $request_array)
|
public function __construct(Company $company, User $user, string $hash, array $request_array)
|
||||||
{
|
{
|
||||||
$this->company = $company;
|
$this->company = $company;
|
||||||
|
$this->user = $user;
|
||||||
$this->hash = $hash;
|
$this->hash = $hash;
|
||||||
$this->request_array = $request_array;
|
$this->request_array = $request_array;
|
||||||
$this->current_app_version = config('ninja.app_version');
|
$this->current_app_version = config('ninja.app_version');
|
||||||
@ -147,6 +157,10 @@ class CompanyImport implements ShouldQueue
|
|||||||
|
|
||||||
$this->company = Company::where('company_key', $this->company->company_key)->firstOrFail();
|
$this->company = Company::where('company_key', $this->company->company_key)->firstOrFail();
|
||||||
$this->account = $this->company->account;
|
$this->account = $this->company->account;
|
||||||
|
$this->company_owner = $this->company->owner();
|
||||||
|
|
||||||
|
nlog("Company ID = {$this->company->id}");
|
||||||
|
nlog("Hash ID = {$this->hash}");
|
||||||
|
|
||||||
$this->backup_file = Cache::get($this->hash);
|
$this->backup_file = Cache::get($this->hash);
|
||||||
|
|
||||||
@ -156,21 +170,112 @@ class CompanyImport implements ShouldQueue
|
|||||||
$this->backup_file = json_decode(base64_decode($this->backup_file));
|
$this->backup_file = json_decode(base64_decode($this->backup_file));
|
||||||
|
|
||||||
// nlog($this->backup_file);
|
// nlog($this->backup_file);
|
||||||
|
$this->checkUserCount();
|
||||||
|
|
||||||
if(array_key_exists('import_settings', $this->request_array) && $this->request_array['import_settings'] == 'true') {
|
if(array_key_exists('import_settings', $this->request_array) && $this->request_array['import_settings'] == 'true') {
|
||||||
|
|
||||||
$this->preFlightChecks()->importSettings();
|
$this->preFlightChecks()->importSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(array_key_exists('import_data', $this->request_array) && $this->request_array['import_data'] == 'true') {
|
if(array_key_exists('import_data', $this->request_array) && $this->request_array['import_data'] == 'true') {
|
||||||
|
|
||||||
|
try{
|
||||||
|
|
||||||
$this->preFlightChecks()
|
$this->preFlightChecks()
|
||||||
->purgeCompanyData()
|
->purgeCompanyData()
|
||||||
->importData();
|
->importData();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
catch(\Exception $e){
|
||||||
|
|
||||||
|
info($e->getMessage());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On the hosted platform we cannot allow the
|
||||||
|
* import to start if there are users > plan number
|
||||||
|
* due to entity user_id dependencies
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function checkUserCount()
|
||||||
|
{
|
||||||
|
|
||||||
|
if(Ninja::isSelfHost())
|
||||||
|
$this->pre_flight_checks_pass = true;
|
||||||
|
|
||||||
|
$backup_users = $this->backup_file->users;
|
||||||
|
|
||||||
|
$company_users = $this->company->users;
|
||||||
|
|
||||||
|
nlog("This is a free account");
|
||||||
|
nlog("Backup user count = ".count($backup_users));
|
||||||
|
|
||||||
|
if(count($backup_users) > 1){
|
||||||
|
// $this->message = 'Only one user can be in the import for a Free Account';
|
||||||
|
// $this->pre_flight_checks_pass = false;
|
||||||
|
//$this->force_user_coalesce = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlog("backup users email = " . $backup_users[0]->email);
|
||||||
|
|
||||||
|
if(count($backup_users) == 1 && $this->company_owner->email != $backup_users[0]->email) {
|
||||||
|
// $this->message = 'Account emails do not match. Account owner email must match backup user email';
|
||||||
|
// $this->pre_flight_checks_pass = false;
|
||||||
|
// $this->force_user_coalesce = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$backup_users_emails = array_column($backup_users, 'email');
|
||||||
|
|
||||||
|
$company_users_emails = $company_users->pluck('email')->toArray();
|
||||||
|
|
||||||
|
$existing_user_count = count(array_intersect($backup_users_emails, $company_users_emails));
|
||||||
|
|
||||||
|
nlog("existing user count = {$existing_user_count}");
|
||||||
|
|
||||||
|
if($existing_user_count > 1){
|
||||||
|
|
||||||
|
if($this->account->plan == 'pro'){
|
||||||
|
// $this->message = 'Pro plan is limited to one user, you have multiple users in the backup file';
|
||||||
|
// $this->pre_flight_checks_pass = false;
|
||||||
|
// $this->force_user_coalesce = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->account->plan == 'enterprise'){
|
||||||
|
|
||||||
|
$total_import_users = count($backup_users_emails);
|
||||||
|
|
||||||
|
$account_plan_num_user = $this->account->num_users;
|
||||||
|
|
||||||
|
if($total_import_users > $account_plan_num_user){
|
||||||
|
$this->message = "Total user count ({$total_import_users}) greater than your plan allows ({$account_plan_num_user})";
|
||||||
|
$this->pre_flight_checks_pass = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->company->account->isFreeHostedClient() && count($this->backup_file->clients) > config('ninja.quotas.free.clients')){
|
||||||
|
|
||||||
|
nlog("client quota busted");
|
||||||
|
|
||||||
|
$client_count = count($this->backup_file->clients);
|
||||||
|
|
||||||
|
$client_limit = config('ninja.quotas.free.clients');
|
||||||
|
|
||||||
|
$this->message = "You are attempting to import ({$client_count}) clients, your current plan allows a total of ({$client_limit})";
|
||||||
|
|
||||||
|
$this->pre_flight_checks_pass = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
//check if this is a complete company import OR if it is selective
|
//check if this is a complete company import OR if it is selective
|
||||||
/*
|
/*
|
||||||
@ -186,6 +291,18 @@ class CompanyImport implements ShouldQueue
|
|||||||
//perform some magic here
|
//perform some magic here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($this->pre_flight_checks_pass === false)
|
||||||
|
{
|
||||||
|
$nmo = new NinjaMailerObject;
|
||||||
|
$nmo->mailable = new CompanyImportFailure($this->company, $this->message);
|
||||||
|
$nmo->company = $this->company;
|
||||||
|
$nmo->settings = $this->company->settings;
|
||||||
|
$nmo->to_user = $this->company->owner();
|
||||||
|
NinjaMailerJob::dispatchNow($nmo);
|
||||||
|
|
||||||
|
nlog($this->message);
|
||||||
|
throw new \Exception($this->message);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -239,10 +356,14 @@ class CompanyImport implements ShouldQueue
|
|||||||
|
|
||||||
$method = "import_{$import}";
|
$method = "import_{$import}";
|
||||||
|
|
||||||
|
nlog($method);
|
||||||
|
|
||||||
$this->{$method}();
|
$this->{$method}();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlog("finished importing company data");
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -281,6 +402,8 @@ class CompanyImport implements ShouldQueue
|
|||||||
$obj_array,
|
$obj_array,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$new_obj->company_id = $this->company->id;
|
||||||
|
$new_obj->user_id = $user_id;
|
||||||
$new_obj->save(['timestamps' => false]);
|
$new_obj->save(['timestamps' => false]);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1095,11 +1218,22 @@ class CompanyImport implements ShouldQueue
|
|||||||
return implode(",", $tmp_arr);
|
return implode(",", $tmp_arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Transform all IDs from old to new
|
||||||
|
*
|
||||||
|
* In the case of users - we need to check if the system
|
||||||
|
* is attempting to migrate resources above their quota,
|
||||||
|
*
|
||||||
|
* ie. > 50 clients or more than 1 user
|
||||||
|
*/
|
||||||
private function transformId(string $resource, ?string $old): ?int
|
private function transformId(string $resource, ?string $old): ?int
|
||||||
{
|
{
|
||||||
if(empty($old))
|
if(empty($old))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
if ($resource == 'users' && $this->force_user_coalesce){
|
||||||
|
return $this->company_owner->id;
|
||||||
|
}
|
||||||
|
|
||||||
if (! array_key_exists($resource, $this->ids)) {
|
if (! array_key_exists($resource, $this->ids)) {
|
||||||
// nlog($this->ids);
|
// nlog($this->ids);
|
||||||
throw new \Exception("Resource {$resource} not available.");
|
throw new \Exception("Resource {$resource} not available.");
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace App\Jobs\Company;
|
namespace App\Jobs\Company;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\TaskStatus;
|
use App\Models\TaskStatus;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
@ -44,6 +45,9 @@ class CreateCompanyTaskStatuses
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$task_statuses = [
|
$task_statuses = [
|
||||||
['name' => ctrans('texts.backlog'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 1],
|
['name' => ctrans('texts.backlog'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 1],
|
||||||
['name' => ctrans('texts.ready_to_do'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 2],
|
['name' => ctrans('texts.ready_to_do'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 2],
|
||||||
|
@ -102,12 +102,11 @@ class CreateEntityPdf implements ShouldQueue
|
|||||||
/* Set the locale*/
|
/* Set the locale*/
|
||||||
App::setLocale($this->contact->preferredLocale());
|
App::setLocale($this->contact->preferredLocale());
|
||||||
|
|
||||||
// nlog($this->entity->client->getMergedSettings());
|
|
||||||
|
|
||||||
/* Set customized translations _NOW_ */
|
/* Set customized translations _NOW_ */
|
||||||
$t->replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
|
$t->replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
|
||||||
|
|
||||||
$this->entity->service()->deletePdf();
|
/*This line of code hurts... it deletes ALL $entity PDFs... this causes a race condition when trying to send an email*/
|
||||||
|
// $this->entity->service()->deletePdf();
|
||||||
|
|
||||||
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);
|
||||||
@ -116,16 +115,16 @@ class CreateEntityPdf implements ShouldQueue
|
|||||||
$entity_design_id = '';
|
$entity_design_id = '';
|
||||||
|
|
||||||
if ($this->entity instanceof Invoice) {
|
if ($this->entity instanceof Invoice) {
|
||||||
$path = $this->entity->client->invoice_filepath();
|
$path = $this->entity->client->invoice_filepath($this->invitation);
|
||||||
$entity_design_id = 'invoice_design_id';
|
$entity_design_id = 'invoice_design_id';
|
||||||
} elseif ($this->entity instanceof Quote) {
|
} elseif ($this->entity instanceof Quote) {
|
||||||
$path = $this->entity->client->quote_filepath();
|
$path = $this->entity->client->quote_filepath($this->invitation);
|
||||||
$entity_design_id = 'quote_design_id';
|
$entity_design_id = 'quote_design_id';
|
||||||
} elseif ($this->entity instanceof Credit) {
|
} elseif ($this->entity instanceof Credit) {
|
||||||
$path = $this->entity->client->credit_filepath();
|
$path = $this->entity->client->credit_filepath($this->invitation);
|
||||||
$entity_design_id = 'credit_design_id';
|
$entity_design_id = 'credit_design_id';
|
||||||
} elseif ($this->entity instanceof RecurringInvoice) {
|
} elseif ($this->entity instanceof RecurringInvoice) {
|
||||||
$path = $this->entity->client->recurring_invoice_filepath();
|
$path = $this->entity->client->recurring_invoice_filepath($this->invitation);
|
||||||
$entity_design_id = 'invoice_design_id';
|
$entity_design_id = 'invoice_design_id';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +194,11 @@ class CreateEntityPdf implements ShouldQueue
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
|
|
||||||
|
if(!Storage::disk($this->disk)->exists($path))
|
||||||
|
Storage::disk($this->disk)->makeDirectory($path, 0775);
|
||||||
|
|
||||||
|
nlog($file_path);
|
||||||
|
|
||||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -109,9 +109,8 @@ class EmailEntity implements ShouldQueue
|
|||||||
|
|
||||||
App::setLocale($this->invitation->contact->preferredLocale());
|
App::setLocale($this->invitation->contact->preferredLocale());
|
||||||
|
|
||||||
|
|
||||||
$nmo = new NinjaMailerObject;
|
$nmo = new NinjaMailerObject;
|
||||||
$nmo->mailable = new TemplateEmail($this->email_entity_builder,$this->invitation->contact, $this->invitation);
|
$nmo->mailable = new TemplateEmail($this->email_entity_builder, $this->invitation->contact, $this->invitation);
|
||||||
$nmo->company = $this->company;
|
$nmo->company = $this->company;
|
||||||
$nmo->settings = $this->settings;
|
$nmo->settings = $this->settings;
|
||||||
$nmo->to_user = $this->invitation->contact;
|
$nmo->to_user = $this->invitation->contact;
|
||||||
|
@ -78,13 +78,16 @@ class ZipInvoices implements ShouldQueue
|
|||||||
// create a new zipstream object
|
// create a new zipstream object
|
||||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip';
|
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip';
|
||||||
|
|
||||||
$path = $this->invoices->first()->client->invoice_filepath();
|
$invoice = $this->invoices->first();
|
||||||
|
$invitation = $invoice->invitations->first();
|
||||||
|
|
||||||
|
$path = $invoice->client->invoice_filepath($invitation);
|
||||||
|
|
||||||
$zip = new ZipStream($file_name, $options);
|
$zip = new ZipStream($file_name, $options);
|
||||||
|
|
||||||
foreach ($this->invoices as $invoice) {
|
foreach ($this->invoices as $invoice) {
|
||||||
//$zip->addFileFromPath(basename($invoice->pdf_file_path()), TempFile::path($invoice->pdf_file_path()));
|
//$zip->addFileFromPath(basename($invoice->pdf_file_path()), TempFile::path($invoice->pdf_file_path()));
|
||||||
$zip->addFileFromPath(basename($invoice->pdf_file_path()), $invoice->pdf_file_path());
|
$zip->addFileFromPath(basename($invoice->pdf_file_path($invitation)), $invoice->pdf_file_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
$zip->finish();
|
$zip->finish();
|
||||||
|
@ -14,7 +14,7 @@ namespace App\Jobs\Ninja;
|
|||||||
use App\DataMapper\InvoiceItem;
|
use App\DataMapper\InvoiceItem;
|
||||||
use App\Events\Invoice\InvoiceWasEmailed;
|
use App\Events\Invoice\InvoiceWasEmailed;
|
||||||
use App\Jobs\Entity\EmailEntity;
|
use App\Jobs\Entity\EmailEntity;
|
||||||
use App\Jobs\Util\WebHookHandler;
|
use App\Jobs\Util\WebhookHandler;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
@ -84,7 +84,7 @@ class SendReminders implements ShouldQueue
|
|||||||
|
|
||||||
if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3', 'endless_reminder'])) {
|
if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3', 'endless_reminder'])) {
|
||||||
$this->sendReminder($invoice, $reminder_template);
|
$this->sendReminder($invoice, $reminder_template);
|
||||||
WebHookHandler::dispatch(Webhook::EVENT_REMIND_INVOICE, $invoice, $invoice->company);
|
WebhookHandler::dispatch(Webhook::EVENT_REMIND_INVOICE, $invoice, $invoice->company);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -128,9 +128,9 @@ class SendReminders implements ShouldQueue
|
|||||||
$set_reminder3 = false;
|
$set_reminder3 = false;
|
||||||
|
|
||||||
if ((int)$settings->schedule_reminder1 > 0) {
|
if ((int)$settings->schedule_reminder1 > 0) {
|
||||||
$next_reminder_date = $this->calculateScheduledDate($invoice, (int)$settings->schedule_reminder1, (int)$settings->num_days_reminder1);
|
$next_reminder_date = $this->calculateScheduledDate($invoice, $settings->schedule_reminder1, (int)$settings->num_days_reminder1);
|
||||||
|
|
||||||
if ($next_reminder_date->gt(Carbon::parse($invoice->last_sent_date)));
|
if ($next_reminder_date && $next_reminder_date->gt(Carbon::parse($invoice->last_sent_date)));
|
||||||
$dates->push($next_reminder_date);
|
$dates->push($next_reminder_date);
|
||||||
|
|
||||||
if (!$invoice->reminder1_sent) {
|
if (!$invoice->reminder1_sent) {
|
||||||
@ -139,20 +139,20 @@ class SendReminders implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((int)$settings->num_days_reminder2 > 0) {
|
if ((int)$settings->num_days_reminder2 > 0) {
|
||||||
$next_reminder_date = $this->calculateScheduledDate($invoice, (int)$settings->schedule_reminder2, (int)$settings->num_days_reminder2);
|
$next_reminder_date = $this->calculateScheduledDate($invoice, $settings->schedule_reminder2, (int)$settings->num_days_reminder2);
|
||||||
|
|
||||||
if ($next_reminder_date->gt(Carbon::parse($invoice->last_sent_date)));
|
if ($next_reminder_date && $next_reminder_date->gt(Carbon::parse($invoice->last_sent_date)));
|
||||||
$dates->push($next_reminder_date);
|
$dates->push($next_reminder_date);
|
||||||
|
|
||||||
if (!$invoice->reminder2_sent) {
|
if (!$invoice->reminder2_sent) {
|
||||||
$set_reminder3 = true;
|
$set_reminder2 = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((int)$settings->num_days_reminder3 > 0) {
|
if ((int)$settings->num_days_reminder3 > 0) {
|
||||||
$next_reminder_date = $this->calculateScheduledDate($invoice, (int)$settings->schedule_reminder3, (int)$settings->num_days_reminder3);
|
$next_reminder_date = $this->calculateScheduledDate($invoice, $settings->schedule_reminder3, (int)$settings->num_days_reminder3);
|
||||||
|
|
||||||
if ($next_reminder_date->gt(Carbon::parse($invoice->last_sent_date)));
|
if ($next_reminder_date && $next_reminder_date->gt(Carbon::parse($invoice->last_sent_date)));
|
||||||
$dates->push($next_reminder_date);
|
$dates->push($next_reminder_date);
|
||||||
|
|
||||||
if (!$invoice->reminder3_sent) {
|
if (!$invoice->reminder3_sent) {
|
||||||
@ -178,15 +178,17 @@ class SendReminders implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
private function calculateScheduledDate($invoice, $schedule_reminder, $num_days_reminder) :?Carbon
|
private function calculateScheduledDate($invoice, $schedule_reminder, $num_days_reminder) :?Carbon
|
||||||
{
|
{
|
||||||
|
$offset = $invoice->client->timezone_offset();
|
||||||
|
|
||||||
switch ($schedule_reminder) {
|
switch ($schedule_reminder) {
|
||||||
case 'after_invoice_date':
|
case 'after_invoice_date':
|
||||||
return Carbon::parse($invoice->date)->addDays($num_days_reminder)->startOfDay();
|
return Carbon::parse($invoice->date)->addDays($num_days_reminder)->startOfDay()->addSeconds($offset);
|
||||||
break;
|
break;
|
||||||
case 'before_due_date':
|
case 'before_due_date':
|
||||||
return Carbon::parse($invoice->due_date)->subDays($num_days_reminder)->startOfDay();
|
return Carbon::parse($invoice->due_date)->subDays($num_days_reminder)->startOfDay()->addSeconds($offset);
|
||||||
break;
|
break;
|
||||||
case 'after_due_date':
|
case 'after_due_date':
|
||||||
return Carbon::parse($invoice->due_date)->addDays($num_days_reminder)->startOfDay();
|
return Carbon::parse($invoice->due_date)->addDays($num_days_reminder)->startOfDay()->addSeconds($offset);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
@ -212,6 +214,7 @@ class SendReminders implements ShouldQueue
|
|||||||
nlog("firing email");
|
nlog("firing email");
|
||||||
|
|
||||||
EmailEntity::dispatchNow($invitation, $invitation->company, $template);
|
EmailEntity::dispatchNow($invitation, $invitation->company, $template);
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -226,8 +229,9 @@ class SendReminders implements ShouldQueue
|
|||||||
if (in_array($template, ['reminder1', 'reminder2', 'reminder3'])) {
|
if (in_array($template, ['reminder1', 'reminder2', 'reminder3'])) {
|
||||||
$invoice->{$template."_sent"} = now();
|
$invoice->{$template."_sent"} = now();
|
||||||
}
|
}
|
||||||
|
$invoice->service()->touchReminder($template)->save();
|
||||||
|
|
||||||
$invoice->save();
|
// $invoice->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,9 +70,9 @@ class SendRecurring implements ShouldQueue
|
|||||||
|
|
||||||
nlog("updating recurring invoice dates");
|
nlog("updating recurring invoice dates");
|
||||||
/* Set next date here to prevent a recurring loop forming */
|
/* Set next date here to prevent a recurring loop forming */
|
||||||
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate()->format('Y-m-d');
|
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
|
||||||
$this->recurring_invoice->remaining_cycles = $this->recurring_invoice->remainingCycles();
|
$this->recurring_invoice->remaining_cycles = $this->recurring_invoice->remainingCycles();
|
||||||
$this->recurring_invoice->last_sent_date = date('Y-m-d');
|
$this->recurring_invoice->last_sent_date = now();
|
||||||
|
|
||||||
/* Set completed if we don't have any more cycles remaining*/
|
/* Set completed if we don't have any more cycles remaining*/
|
||||||
if ($this->recurring_invoice->remaining_cycles == 0) {
|
if ($this->recurring_invoice->remaining_cycles == 0) {
|
||||||
|
@ -212,7 +212,7 @@ class Import implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if(Ninja::isHosted() && array_key_exists('ninja_tokens', $data))
|
// if(Ninja::isHosted() && array_key_exists('ninja_tokens', $data))
|
||||||
// $this->processNinjaTokens($data['ninja_tokens']);
|
$this->processNinjaTokens($data['ninja_tokens']);
|
||||||
|
|
||||||
$this->setInitialCompanyLedgerBalances();
|
$this->setInitialCompanyLedgerBalances();
|
||||||
|
|
||||||
@ -929,7 +929,7 @@ class Import implements ShouldQueue
|
|||||||
|
|
||||||
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
|
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
|
||||||
|
|
||||||
if(array_key_exists('invoice_id', $resource) && $this->tryTransformingId('invoices', $resource['invoice_id']))
|
if(array_key_exists('invoice_id', $resource) && isset($resource['invoice_id']) && $this->tryTransformingId('invoices', $resource['invoice_id']))
|
||||||
$modified['invoice_id'] = $this->transformId('invoices', $resource['invoice_id']);
|
$modified['invoice_id'] = $this->transformId('invoices', $resource['invoice_id']);
|
||||||
|
|
||||||
$modified['user_id'] = $this->processUserId($resource);
|
$modified['user_id'] = $this->processUserId($resource);
|
||||||
@ -1659,8 +1659,10 @@ class Import implements ShouldQueue
|
|||||||
|
|
||||||
private function processNinjaTokens(array $data)
|
private function processNinjaTokens(array $data)
|
||||||
{
|
{
|
||||||
|
nlog("attempting to process Ninja Tokens");
|
||||||
|
|
||||||
if(Ninja::isHosted())
|
if(Ninja::isHosted())
|
||||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatch($data, $this->company);
|
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow($data, $this->company);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,11 @@ class ReminderJob implements ShouldQueue
|
|||||||
|
|
||||||
private function processReminders()
|
private function processReminders()
|
||||||
{
|
{
|
||||||
Invoice::where('next_send_date', Carbon::today()->format('Y-m-d'))->with('invitations')->cursor()->each(function ($invoice) {
|
Invoice::whereDate('next_send_date', '<=', now())
|
||||||
|
->where('is_deleted', 0)
|
||||||
|
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
|
->where('balance', '>', 0)
|
||||||
|
->with('invitations')->cursor()->each(function ($invoice) {
|
||||||
|
|
||||||
if ($invoice->isPayable()) {
|
if ($invoice->isPayable()) {
|
||||||
$reminder_template = $invoice->calculateTemplate('invoice');
|
$reminder_template = $invoice->calculateTemplate('invoice');
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
*
|
*
|
||||||
* @license https://opensource.org/licenses/AAL
|
* @license https://opensource.org/licenses/AAL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace App\Jobs\Util;
|
namespace App\Jobs\Util;
|
||||||
|
|
||||||
use App\Jobs\Util\SystemLogger;
|
use App\Jobs\Util\SystemLogger;
|
||||||
|
@ -34,11 +34,11 @@ class OAuth
|
|||||||
* @param Socialite $user
|
* @param Socialite $user
|
||||||
* @return bool|\App\Models\User|\App\Libraries\App\Models\User|null
|
* @return bool|\App\Models\User|\App\Libraries\App\Models\User|null
|
||||||
*/
|
*/
|
||||||
public static function handleAuth(Socialite $user)
|
public static function handleAuth($socialite_user, $provider)
|
||||||
{
|
{
|
||||||
/** 1. Ensure user arrives on the correct provider **/
|
/** 1. Ensure user arrives on the correct provider **/
|
||||||
$query = [
|
$query = [
|
||||||
'oauth_user_id' =>$user->getId(),
|
'oauth_user_id' =>$socialite_user->getId(),
|
||||||
'oauth_provider_id'=>$provider,
|
'oauth_provider_id'=>$provider,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -47,6 +47,9 @@ class CreditEmailEngine extends BaseEmailEngine
|
|||||||
$t = app('translator');
|
$t = app('translator');
|
||||||
$t->replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
$t->replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
||||||
|
|
||||||
|
if($this->reminder_template == 'endless_reminder')
|
||||||
|
$this->reminder_template = 'reminder_endless';
|
||||||
|
|
||||||
if (is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
|
if (is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
|
||||||
$body_template = $this->template_data['body'];
|
$body_template = $this->template_data['body'];
|
||||||
} else {
|
} else {
|
||||||
@ -98,9 +101,9 @@ class CreditEmailEngine extends BaseEmailEngine
|
|||||||
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(null, 'url', true)]);
|
$this->setAttachments([$this->credit->pdf_file_path($this->invitation, 'url', true)]);
|
||||||
else
|
else
|
||||||
$this->setAttachments([$this->credit->pdf_file_path()]);
|
$this->setAttachments([$this->credit->pdf_file_path($this->invitation)]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,9 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
|||||||
$t = app('translator');
|
$t = app('translator');
|
||||||
$t->replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
$t->replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
||||||
|
|
||||||
|
if($this->reminder_template == 'endless_reminder')
|
||||||
|
$this->reminder_template = 'reminder_endless';
|
||||||
|
|
||||||
if (is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
|
if (is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
|
||||||
$body_template = $this->template_data['body'];
|
$body_template = $this->template_data['body'];
|
||||||
} elseif (strlen($this->client->getSetting('email_template_'.$this->reminder_template)) > 0) {
|
} elseif (strlen($this->client->getSetting('email_template_'.$this->reminder_template)) > 0) {
|
||||||
@ -109,9 +112,9 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
|||||||
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())
|
if(Ninja::isHosted())
|
||||||
$this->setAttachments([$this->invoice->pdf_file_path(null, 'url', true)]);
|
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation, 'url', true)]);
|
||||||
else
|
else
|
||||||
$this->setAttachments([$this->invoice->pdf_file_path()]);
|
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation)]);
|
||||||
|
|
||||||
// $this->setAttachments(['path' => $this->invoice->pdf_file_path(), 'name' => basename($this->invoice->pdf_file_path())]);
|
// $this->setAttachments(['path' => $this->invoice->pdf_file_path(), 'name' => basename($this->invoice->pdf_file_path())]);
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ class PaymentEmailEngine extends BaseEmailEngine
|
|||||||
|
|
||||||
$this->payment->invoices->each(function ($invoice){
|
$this->payment->invoices->each(function ($invoice){
|
||||||
|
|
||||||
$this->setAttachments([$invoice->pdf_file_path()]);
|
$this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first())]);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -48,6 +48,9 @@ class QuoteEmailEngine extends BaseEmailEngine
|
|||||||
$t = app('translator');
|
$t = app('translator');
|
||||||
$t->replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
$t->replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
||||||
|
|
||||||
|
if($this->reminder_template == 'endless_reminder')
|
||||||
|
$this->reminder_template = 'reminder_endless';
|
||||||
|
|
||||||
if (is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
|
if (is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
|
||||||
$body_template = $this->template_data['body'];
|
$body_template = $this->template_data['body'];
|
||||||
} else {
|
} else {
|
||||||
@ -100,9 +103,9 @@ class QuoteEmailEngine extends BaseEmailEngine
|
|||||||
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(null, 'url', true)]);
|
$this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]);
|
||||||
else
|
else
|
||||||
$this->setAttachments([$this->quote->pdf_file_path()]);
|
$this->setAttachments([$this->quote->pdf_file_path($this->invitation)]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
65
app/Mail/Import/CompanyImportFailure.php
Normal file
65
app/Mail/Import/CompanyImportFailure.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Mail\Import;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class CompanyImportFailure extends Mailable
|
||||||
|
{
|
||||||
|
// use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public $settings;
|
||||||
|
|
||||||
|
public $logo;
|
||||||
|
|
||||||
|
public $title;
|
||||||
|
|
||||||
|
public $message;
|
||||||
|
|
||||||
|
public $whitelabel;
|
||||||
|
|
||||||
|
public $user_message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($company, $user_message)
|
||||||
|
{
|
||||||
|
$this->company = $company;
|
||||||
|
$this->user_message = $user_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the message.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function build()
|
||||||
|
{
|
||||||
|
$this->settings = $this->company->settings;
|
||||||
|
$this->logo = $this->company->present()->logo();
|
||||||
|
$this->title = ctrans('texts.company_import_failure_subject', ['company' => $this->company->present()->name()]);
|
||||||
|
$this->whitelabel = $this->company->account->isPaid();
|
||||||
|
|
||||||
|
nlog($this->user_message);
|
||||||
|
|
||||||
|
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||||
|
->subject(ctrans('texts.company_import_failure_subject', ['company' => $this->company->present()->name()]))
|
||||||
|
->view('email.import.import_failure', ['user_message' => $this->user_message, 'title' => $this->title]);
|
||||||
|
}
|
||||||
|
}
|
@ -41,9 +41,6 @@ class MigrationCompleted extends Mailable
|
|||||||
$result = $this->from(config('mail.from.address'), config('mail.from.name'))
|
$result = $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||||
->view('email.import.completed', $data);
|
->view('email.import.completed', $data);
|
||||||
|
|
||||||
// if($this->company->invoices->count() >=1)
|
|
||||||
// $result->attach($this->company->invoices->first()->pdf_file_path());
|
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,9 +105,6 @@ class Account extends BaseModel
|
|||||||
return $this->hasOne(Company::class, 'id', 'default_company_id');
|
return $this->hasOne(Company::class, 'id', 'default_company_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return BelongsTo
|
|
||||||
*/
|
|
||||||
public function payment()
|
public function payment()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Payment::class)->withTrashed();
|
return $this->belongsTo(Payment::class)->withTrashed();
|
||||||
@ -207,7 +204,7 @@ class Account extends BaseModel
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->plan == 'free';
|
return $this->plan == 'free' || is_null($this->plan);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEnterpriseClient()
|
public function isEnterpriseClient()
|
||||||
@ -323,4 +320,5 @@ class Account extends BaseModel
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ use App\DataMapper\ClientSettings;
|
|||||||
use App\DataMapper\CompanySettings;
|
use App\DataMapper\CompanySettings;
|
||||||
use App\Models\Presenters\ClientPresenter;
|
use App\Models\Presenters\ClientPresenter;
|
||||||
use App\Services\Client\ClientService;
|
use App\Services\Client\ClientService;
|
||||||
|
use App\Utils\Traits\AppSetup;
|
||||||
use App\Utils\Traits\GeneratesCounter;
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
use App\Utils\Traits\MakesDates;
|
use App\Utils\Traits\MakesDates;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
@ -32,6 +33,7 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
use Filterable;
|
use Filterable;
|
||||||
use GeneratesCounter;
|
use GeneratesCounter;
|
||||||
|
use AppSetup;
|
||||||
|
|
||||||
protected $presenter = ClientPresenter::class;
|
protected $presenter = ClientPresenter::class;
|
||||||
|
|
||||||
@ -230,13 +232,16 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
|
|
||||||
public function language()
|
public function language()
|
||||||
{
|
{
|
||||||
//return Language::find($this->getSetting('language_id'));
|
|
||||||
|
|
||||||
$languages = Cache::get('languages');
|
$languages = Cache::get('languages');
|
||||||
|
|
||||||
|
if(!$languages)
|
||||||
|
$this->buildCache(true);
|
||||||
|
|
||||||
return $languages->filter(function ($item) {
|
return $languages->filter(function ($item) {
|
||||||
return $item->id == $this->getSetting('language_id');
|
return $item->id == $this->getSetting('language_id');
|
||||||
})->first();
|
})->first();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function locale()
|
public function locale()
|
||||||
@ -257,6 +262,9 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
{
|
{
|
||||||
$currencies = Cache::get('currencies');
|
$currencies = Cache::get('currencies');
|
||||||
|
|
||||||
|
if(!$currencies)
|
||||||
|
$this->buildCache(true);
|
||||||
|
|
||||||
return $currencies->filter(function ($item) {
|
return $currencies->filter(function ($item) {
|
||||||
return $item->id == $this->getSetting('currency_id');
|
return $item->id == $this->getSetting('currency_id');
|
||||||
})->first();
|
})->first();
|
||||||
@ -622,29 +630,36 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
{
|
{
|
||||||
$languages = Cache::get('languages');
|
$languages = Cache::get('languages');
|
||||||
|
|
||||||
|
if(!$languages)
|
||||||
|
$this->buildCache(true);
|
||||||
|
|
||||||
return $languages->filter(function ($item) {
|
return $languages->filter(function ($item) {
|
||||||
return $item->id == $this->getSetting('language_id');
|
return $item->id == $this->getSetting('language_id');
|
||||||
})->first()->locale;
|
})->first()->locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function invoice_filepath()
|
public function invoice_filepath($invitation)
|
||||||
{
|
{
|
||||||
return $this->company->company_key.'/'.$this->client_hash.'/invoices/';
|
$contact_key = $invitation->contact->contact_key;
|
||||||
|
return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/invoices/';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function quote_filepath()
|
public function quote_filepath($invitation)
|
||||||
{
|
{
|
||||||
return $this->company->company_key.'/'.$this->client_hash.'/quotes/';
|
$contact_key = $invitation->contact->contact_key;
|
||||||
|
return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/quotes/';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function credit_filepath()
|
public function credit_filepath($invitation)
|
||||||
{
|
{
|
||||||
return $this->company->company_key.'/'.$this->client_hash.'/credits/';
|
$contact_key = $invitation->contact->contact_key;
|
||||||
|
return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/credits/';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function recurring_invoice_filepath()
|
public function recurring_invoice_filepath($invitation)
|
||||||
{
|
{
|
||||||
return $this->company->company_key.'/'.$this->client_hash.'/recurring_invoices/';
|
$contact_key = $invitation->contact->contact_key;
|
||||||
|
return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/recurring_invoices/';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function company_filepath()
|
public function company_filepath()
|
||||||
@ -684,4 +699,21 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Payment::class);
|
return $this->hasMany(Payment::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function timezone_offset()
|
||||||
|
{
|
||||||
|
$offset = 0;
|
||||||
|
|
||||||
|
$entity_send_time = $this->getSetting('entity_send_time');
|
||||||
|
|
||||||
|
if($entity_send_time == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
$timezone = $this->company->timezone();
|
||||||
|
|
||||||
|
$offset -= $timezone->utc_offset;
|
||||||
|
$offset += ($entity_send_time * 3600);
|
||||||
|
|
||||||
|
return $offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,7 @@ class Credit extends BaseModel
|
|||||||
if(!$invitation)
|
if(!$invitation)
|
||||||
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
||||||
|
|
||||||
$file_path = $this->client->credit_filepath().$this->numberFormatter().'.pdf';
|
$file_path = $this->client->credit_filepath($invitation).$this->numberFormatter().'.pdf';
|
||||||
|
|
||||||
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
||||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||||
|
@ -126,9 +126,9 @@ class CreditInvitation extends BaseModel
|
|||||||
|
|
||||||
public function pdf_file_path()
|
public function pdf_file_path()
|
||||||
{
|
{
|
||||||
$storage_path = Storage::url($this->credit->client->quote_filepath().$this->credit->numberFormatter().'.pdf');
|
$storage_path = Storage::url($this->credit->client->quote_filepath($this).$this->credit->numberFormatter().'.pdf');
|
||||||
|
|
||||||
if (! Storage::exists($this->credit->client->credit_filepath().$this->credit->numberFormatter().'.pdf')) {
|
if (! Storage::exists($this->credit->client->credit_filepath($this).$this->credit->numberFormatter().'.pdf')) {
|
||||||
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
event(new CreditWasUpdated($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||||
CreateEntityPdf::dispatchNow($this);
|
CreateEntityPdf::dispatchNow($this);
|
||||||
}
|
}
|
||||||
|
@ -409,13 +409,13 @@ class Invoice extends BaseModel
|
|||||||
if(!$invitation)
|
if(!$invitation)
|
||||||
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
||||||
|
|
||||||
$file_path = $this->client->invoice_filepath().$this->numberFormatter().'.pdf';
|
$file_path = $this->client->invoice_filepath($invitation).$this->numberFormatter().'.pdf';
|
||||||
|
|
||||||
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
||||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||||
}
|
}
|
||||||
elseif(Ninja::isHosted() && $portal){
|
elseif(Ninja::isHosted() && $portal){
|
||||||
$file_path = CreateEntityPdf::dispatchNow($invitation,config('filesystems.default'));
|
$file_path = CreateEntityPdf::dispatchNow($invitation, config('filesystems.default'));
|
||||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ class InvoiceInvitation extends BaseModel
|
|||||||
{
|
{
|
||||||
$storage_path = Storage::url($this->invoice->client->invoice_filepath().$this->invoice->numberFormatter().'.pdf');
|
$storage_path = Storage::url($this->invoice->client->invoice_filepath().$this->invoice->numberFormatter().'.pdf');
|
||||||
|
|
||||||
if (! Storage::exists($this->invoice->client->invoice_filepath().$this->invoice->numberFormatter().'.pdf')) {
|
if (! Storage::exists($this->invoice->client->invoice_filepath($this).$this->invoice->numberFormatter().'.pdf')) {
|
||||||
event(new InvoiceWasUpdated($this->invoice, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
event(new InvoiceWasUpdated($this->invoice, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||||
CreateEntityPdf::dispatchNow($this);
|
CreateEntityPdf::dispatchNow($this);
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ class Quote extends BaseModel
|
|||||||
if(!$invitation)
|
if(!$invitation)
|
||||||
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
||||||
|
|
||||||
$file_path = $this->client->quote_filepath().$this->numberFormatter().'.pdf';
|
$file_path = $this->client->quote_filepath($invitation).$this->numberFormatter().'.pdf';
|
||||||
|
|
||||||
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
||||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||||
|
@ -130,9 +130,9 @@ class QuoteInvitation extends BaseModel
|
|||||||
|
|
||||||
public function pdf_file_path()
|
public function pdf_file_path()
|
||||||
{
|
{
|
||||||
$storage_path = Storage::url($this->quote->client->quote_filepath().$this->quote->numberFormatter().'.pdf');
|
$storage_path = Storage::url($this->quote->client->quote_filepath($this).$this->quote->numberFormatter().'.pdf');
|
||||||
|
|
||||||
if (! Storage::exists($this->quote->client->quote_filepath().$this->quote->numberFormatter().'.pdf')) {
|
if (! Storage::exists($this->quote->client->quote_filepath($this).$this->quote->numberFormatter().'.pdf')) {
|
||||||
event(new QuoteWasUpdated($this->quote, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
event(new QuoteWasUpdated($this->quote, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||||
CreateEntityPdf::dispatchNow($this);
|
CreateEntityPdf::dispatchNow($this);
|
||||||
}
|
}
|
||||||
|
@ -220,34 +220,35 @@ class RecurringInvoice extends BaseModel
|
|||||||
{
|
{
|
||||||
if (!$this->next_send_date) {
|
if (!$this->next_send_date) {
|
||||||
return null;
|
return null;
|
||||||
// $this->next_send_date = now()->format('Y-m-d');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$offset = $this->client->timezone_offset();
|
||||||
|
|
||||||
switch ($this->frequency_id) {
|
switch ($this->frequency_id) {
|
||||||
case self::FREQUENCY_DAILY:
|
case self::FREQUENCY_DAILY:
|
||||||
return Carbon::parse($this->next_send_date)->addDay();
|
return Carbon::parse($this->next_send_date)->startOfDay()->addDay()->addSeconds($offset);
|
||||||
case self::FREQUENCY_WEEKLY:
|
case self::FREQUENCY_WEEKLY:
|
||||||
return Carbon::parse($this->next_send_date)->addWeek();
|
return Carbon::parse($this->next_send_date)->startOfDay()->addWeek()->addSeconds($offset);
|
||||||
case self::FREQUENCY_TWO_WEEKS:
|
case self::FREQUENCY_TWO_WEEKS:
|
||||||
return Carbon::parse($this->next_send_date)->addWeeks(2);
|
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(2)->addSeconds($offset);
|
||||||
case self::FREQUENCY_FOUR_WEEKS:
|
case self::FREQUENCY_FOUR_WEEKS:
|
||||||
return Carbon::parse($this->next_send_date)->addWeeks(4);
|
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(4)->addSeconds($offset);
|
||||||
case self::FREQUENCY_MONTHLY:
|
case self::FREQUENCY_MONTHLY:
|
||||||
return Carbon::parse($this->next_send_date)->addMonthNoOverflow();
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthNoOverflow()->addSeconds($offset);
|
||||||
case self::FREQUENCY_TWO_MONTHS:
|
case self::FREQUENCY_TWO_MONTHS:
|
||||||
return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(2);
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset);
|
||||||
case self::FREQUENCY_THREE_MONTHS:
|
case self::FREQUENCY_THREE_MONTHS:
|
||||||
return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(3);
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset);
|
||||||
case self::FREQUENCY_FOUR_MONTHS:
|
case self::FREQUENCY_FOUR_MONTHS:
|
||||||
return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(4);
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset);
|
||||||
case self::FREQUENCY_SIX_MONTHS:
|
case self::FREQUENCY_SIX_MONTHS:
|
||||||
return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(6);
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(6)->addSeconds($offset);
|
||||||
case self::FREQUENCY_ANNUALLY:
|
case self::FREQUENCY_ANNUALLY:
|
||||||
return Carbon::parse($this->next_send_date)->addYear();
|
return Carbon::parse($this->next_send_date)->startOfDay()->addYear()->addSeconds($offset);
|
||||||
case self::FREQUENCY_TWO_YEARS:
|
case self::FREQUENCY_TWO_YEARS:
|
||||||
return Carbon::parse($this->next_send_date)->addYears(2);
|
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(2)->addSeconds($offset);
|
||||||
case self::FREQUENCY_THREE_YEARS:
|
case self::FREQUENCY_THREE_YEARS:
|
||||||
return Carbon::parse($this->next_send_date)->addYears(3);
|
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(3)->addSeconds($offset);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -255,31 +256,33 @@ class RecurringInvoice extends BaseModel
|
|||||||
|
|
||||||
public function nextDateByFrequency($date)
|
public function nextDateByFrequency($date)
|
||||||
{
|
{
|
||||||
|
$offset = $this->client->timezone_offset();
|
||||||
|
|
||||||
switch ($this->frequency_id) {
|
switch ($this->frequency_id) {
|
||||||
case self::FREQUENCY_DAILY:
|
case self::FREQUENCY_DAILY:
|
||||||
return Carbon::parse($date)->addDay();
|
return Carbon::parse($date)->startOfDay()->addDay()->addSeconds($offset);
|
||||||
case self::FREQUENCY_WEEKLY:
|
case self::FREQUENCY_WEEKLY:
|
||||||
return Carbon::parse($date)->addWeek();
|
return Carbon::parse($date)->startOfDay()->addWeek()->addSeconds($offset);
|
||||||
case self::FREQUENCY_TWO_WEEKS:
|
case self::FREQUENCY_TWO_WEEKS:
|
||||||
return Carbon::parse($date)->addWeeks(2);
|
return Carbon::parse($date)->startOfDay()->addWeeks(2)->addSeconds($offset);
|
||||||
case self::FREQUENCY_FOUR_WEEKS:
|
case self::FREQUENCY_FOUR_WEEKS:
|
||||||
return Carbon::parse($date)->addWeeks(4);
|
return Carbon::parse($date)->startOfDay()->addWeeks(4)->addSeconds($offset);
|
||||||
case self::FREQUENCY_MONTHLY:
|
case self::FREQUENCY_MONTHLY:
|
||||||
return Carbon::parse($date)->addMonthNoOverflow();
|
return Carbon::parse($date)->startOfDay()->addMonthNoOverflow()->addSeconds($offset);
|
||||||
case self::FREQUENCY_TWO_MONTHS:
|
case self::FREQUENCY_TWO_MONTHS:
|
||||||
return Carbon::parse($date)->addMonthsNoOverflow(2);
|
return Carbon::parse($date)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset);
|
||||||
case self::FREQUENCY_THREE_MONTHS:
|
case self::FREQUENCY_THREE_MONTHS:
|
||||||
return Carbon::parse($date)->addMonthsNoOverflow(3);
|
return Carbon::parse($date)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset);
|
||||||
case self::FREQUENCY_FOUR_MONTHS:
|
case self::FREQUENCY_FOUR_MONTHS:
|
||||||
return Carbon::parse($date)->addMonthsNoOverflow(4);
|
return Carbon::parse($date)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset);
|
||||||
case self::FREQUENCY_SIX_MONTHS:
|
case self::FREQUENCY_SIX_MONTHS:
|
||||||
return Carbon::parse($date)->addMonthsNoOverflow(6);
|
return Carbon::parse($date)->addMonthsNoOverflow(6)->addSeconds($offset);
|
||||||
case self::FREQUENCY_ANNUALLY:
|
case self::FREQUENCY_ANNUALLY:
|
||||||
return Carbon::parse($date)->addYear();
|
return Carbon::parse($date)->startOfDay()->addYear()->addSeconds($offset);
|
||||||
case self::FREQUENCY_TWO_YEARS:
|
case self::FREQUENCY_TWO_YEARS:
|
||||||
return Carbon::parse($date)->addYears(2);
|
return Carbon::parse($date)->startOfDay()->addYears(2)->addSeconds($offset);
|
||||||
case self::FREQUENCY_THREE_YEARS:
|
case self::FREQUENCY_THREE_YEARS:
|
||||||
return Carbon::parse($date)->addYears(3);
|
return Carbon::parse($date)->startOfDay()->addYears(3)->addSeconds($offset);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,6 @@ class InvoiceObserver
|
|||||||
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company);
|
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if($invoice->isDirty('date') || $invoice->isDirty('due_date'))
|
|
||||||
// $invoice->service()->setReminder()->save();
|
|
||||||
|
|
||||||
// UnlinkFile::dispatchNow(config('filesystems.default'), $invoice->client->invoice_filepath() . $invoice->numberFormatter().'.pdf');
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +98,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
|||||||
|
|
||||||
$message = [
|
$message = [
|
||||||
'server_response' => $response->getMessage(),
|
'server_response' => $response->getMessage(),
|
||||||
'data' => $this->checkout->payment_hash->data,
|
'data' => $this->payment_hash->data,
|
||||||
];
|
];
|
||||||
|
|
||||||
SystemLogger::dispatch(
|
SystemLogger::dispatch(
|
||||||
@ -187,7 +187,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
|||||||
'cancelUrl' => $this->client->company->domain() . '/client/invoices',
|
'cancelUrl' => $this->client->company->domain() . '/client/invoices',
|
||||||
'description' => implode(',', collect($this->payment_hash->data->invoices)
|
'description' => implode(',', collect($this->payment_hash->data->invoices)
|
||||||
->map(function ($invoice) {
|
->map(function ($invoice) {
|
||||||
return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->number);
|
return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->invoice_number);
|
||||||
})->toArray()),
|
})->toArray()),
|
||||||
'transactionId' => $this->payment_hash->hash . '-' . time(),
|
'transactionId' => $this->payment_hash->hash . '-' . time(),
|
||||||
'ButtonSource' => 'InvoiceNinja_SP',
|
'ButtonSource' => 'InvoiceNinja_SP',
|
||||||
|
@ -56,7 +56,7 @@ class ImportCustomers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Now call the update payment methods handler*/
|
/* Now call the update payment methods handler*/
|
||||||
$this->stripe->updateAllPaymentMethods();
|
// $this->stripe->updateAllPaymentMethods();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ class UpdatePaymentMethods
|
|||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
private function updateMethods(Customer $customer, Client $client)
|
public function updateMethods(Customer $customer, Client $client)
|
||||||
{
|
{
|
||||||
$card_methods = PaymentMethod::all([
|
$card_methods = PaymentMethod::all([
|
||||||
'customer' => $customer->id,
|
'customer' => $customer->id,
|
||||||
@ -145,7 +145,7 @@ class UpdatePaymentMethods
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildPaymentMethodMeta(PaymentMethod $method, GatewayType $type_id)
|
private function buildPaymentMethodMeta(PaymentMethod $method, $type_id)
|
||||||
{
|
{
|
||||||
|
|
||||||
switch ($type_id) {
|
switch ($type_id) {
|
||||||
|
@ -508,10 +508,10 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
* the respective tokens in the system.
|
* the respective tokens in the system.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
// public function updateAllPaymentMethods()
|
public function updateAllPaymentMethods()
|
||||||
// {
|
{
|
||||||
// return (new UpdatePaymentMethods($this))->run();
|
return (new UpdatePaymentMethods($this))->run();
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports stripe customers and their payment methods
|
* Imports stripe customers and their payment methods
|
||||||
|
@ -140,7 +140,11 @@ class CreditService
|
|||||||
|
|
||||||
public function deletePdf()
|
public function deletePdf()
|
||||||
{
|
{
|
||||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->credit->client->credit_filepath() . $this->credit->numberFormatter().'.pdf');
|
$this->credit->invitations->each(function ($invitation){
|
||||||
|
|
||||||
|
UnlinkFile::dispatchNow(config('filesystems.default'), $this->credit->client->credit_filepath($invitation) . $this->credit->numberFormatter().'.pdf');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ class GetCreditPdf extends AbstractService
|
|||||||
$this->contact = $this->credit->client->primary_contact()->first();
|
$this->contact = $this->credit->client->primary_contact()->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
$path = $this->credit->client->credit_filepath();
|
$path = $this->credit->client->credit_filepath($this->invitation);
|
||||||
|
|
||||||
$file_path = $path.$this->credit->numberFormatter().'.pdf';
|
$file_path = $path.$this->credit->numberFormatter().'.pdf';
|
||||||
|
|
||||||
|
@ -60,14 +60,15 @@ class GenerateDeliveryNote
|
|||||||
? $this->invoice->design_id
|
? $this->invoice->design_id
|
||||||
: $this->decodePrimaryKey($this->invoice->client->getSetting('invoice_design_id'));
|
: $this->decodePrimaryKey($this->invoice->client->getSetting('invoice_design_id'));
|
||||||
|
|
||||||
$file_path = sprintf('%s%s_delivery_note.pdf', $this->invoice->client->invoice_filepath(), $this->invoice->number);
|
$invitation = $this->invoice->invitations->first();
|
||||||
|
$file_path = sprintf('%s%s_delivery_note.pdf', $this->invoice->client->invoice_filepath($invitation), $this->invoice->number);
|
||||||
|
|
||||||
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->invoice->invitations->first());
|
return (new Phantom)->generate($this->invoice->invitations->first());
|
||||||
}
|
}
|
||||||
|
|
||||||
$design = Design::find($design_id);
|
$design = Design::find($design_id);
|
||||||
$html = new HtmlEngine($this->invoice->invitations->first());
|
$html = new HtmlEngine($invitation);
|
||||||
|
|
||||||
if ($design->is_custom) {
|
if ($design->is_custom) {
|
||||||
$options = ['custom_partials' => json_decode(json_encode($design->design), true)];
|
$options = ['custom_partials' => json_decode(json_encode($design->design), true)];
|
||||||
@ -105,6 +106,9 @@ class GenerateDeliveryNote
|
|||||||
info($maker->getCompiledHTML());
|
info($maker->getCompiledHTML());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!Storage::disk($this->disk)->exists($this->invoice->client->invoice_filepath($invitation)))
|
||||||
|
Storage::disk($this->disk)->makeDirectory($this->invoice->client->invoice_filepath($invitation), 0775);
|
||||||
|
|
||||||
Storage::disk($this->disk)->put($file_path, $pdf);
|
Storage::disk($this->disk)->put($file_path, $pdf);
|
||||||
|
|
||||||
return Storage::disk($this->disk)->path($file_path);
|
return Storage::disk($this->disk)->path($file_path);
|
||||||
|
@ -35,7 +35,7 @@ class GetInvoicePdf extends AbstractService
|
|||||||
|
|
||||||
$invitation = $this->invoice->invitations->where('client_contact_id', $this->contact->id)->first();
|
$invitation = $this->invoice->invitations->where('client_contact_id', $this->contact->id)->first();
|
||||||
|
|
||||||
$path = $this->invoice->client->invoice_filepath();
|
$path = $this->invoice->client->invoice_filepath($invitation);
|
||||||
|
|
||||||
$file_path = $path.$this->invoice->numberFormatter().'.pdf';
|
$file_path = $path.$this->invoice->numberFormatter().'.pdf';
|
||||||
|
|
||||||
|
@ -307,13 +307,16 @@ class InvoiceService
|
|||||||
|
|
||||||
public function deletePdf()
|
public function deletePdf()
|
||||||
{
|
{
|
||||||
//UnlinkFile::dispatchNow(config('filesystems.default'), $this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
$this->invoice->invitations->each(function ($invitation){
|
||||||
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
|
||||||
|
Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf');
|
||||||
|
|
||||||
if(Ninja::isHosted()) {
|
if(Ninja::isHosted()) {
|
||||||
Storage::disk('public')->delete($this->invoice->client->invoice_filepath() . $this->invoice->numberFormatter().'.pdf');
|
Storage::disk('public')->delete($this->invoice->client->invoice_filepath($invitation) . $this->invoice->numberFormatter().'.pdf');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,8 +354,17 @@ class InvoiceService
|
|||||||
* PDF when it is updated etc.
|
* PDF when it is updated etc.
|
||||||
* @return InvoiceService
|
* @return InvoiceService
|
||||||
*/
|
*/
|
||||||
public function touchPdf()
|
public function touchPdf($force = false)
|
||||||
{
|
{
|
||||||
|
if($force){
|
||||||
|
|
||||||
|
$this->invoice->invitations->each(function ($invitation) {
|
||||||
|
CreateEntityPdf::dispatchNow($invitation);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
$this->invoice->invitations->each(function ($invitation) {
|
$this->invoice->invitations->each(function ($invitation) {
|
||||||
CreateEntityPdf::dispatch($invitation);
|
CreateEntityPdf::dispatch($invitation);
|
||||||
});
|
});
|
||||||
@ -376,9 +388,12 @@ class InvoiceService
|
|||||||
$this->invoice->reminder3_sent = now()->format('Y-m-d');
|
$this->invoice->reminder3_sent = now()->format('Y-m-d');
|
||||||
$this->invoice->reminder_last_sent = now()->format('Y-m-d');
|
$this->invoice->reminder_last_sent = now()->format('Y-m-d');
|
||||||
break;
|
break;
|
||||||
|
case 'endless_reminder':
|
||||||
|
$this->invoice->reminder_last_sent = now()->format('Y-m-d');
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// code...
|
$this->invoice->reminder1_sent = now()->format('Y-m-d');
|
||||||
|
$this->invoice->reminder_last_sent = now()->format('Y-m-d');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,90 +41,95 @@ class UpdateReminder extends AbstractService
|
|||||||
return $this->invoice; //exit early
|
return $this->invoice; //exit early
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$offset = $this->invoice->client->timezone_offset();
|
||||||
|
|
||||||
$date_collection = collect();
|
$date_collection = collect();
|
||||||
|
|
||||||
if (is_null($this->invoice->reminder1_sent) &&
|
if (is_null($this->invoice->reminder1_sent) &&
|
||||||
$this->settings->schedule_reminder1 == 'after_invoice_date' &&
|
$this->settings->schedule_reminder1 == 'after_invoice_date' &&
|
||||||
$this->settings->num_days_reminder1 > 0) {
|
$this->settings->num_days_reminder1 > 0) {
|
||||||
$reminder_date = Carbon::parse($this->invoice->date)->addDays($this->settings->num_days_reminder1);
|
$reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder1)->addSeconds($offset);
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($this->invoice->reminder1_sent) &&
|
if (is_null($this->invoice->reminder1_sent) &&
|
||||||
$this->settings->schedule_reminder1 == 'before_due_date' &&
|
$this->settings->schedule_reminder1 == 'before_due_date' &&
|
||||||
$this->settings->num_days_reminder1 > 0) {
|
$this->settings->num_days_reminder1 > 0) {
|
||||||
$reminder_date = Carbon::parse($this->invoice->due_date)->subDays($this->settings->num_days_reminder1);
|
$reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder1)->addSeconds($offset);
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($this->invoice->reminder1_sent) &&
|
if (is_null($this->invoice->reminder1_sent) &&
|
||||||
$this->settings->schedule_reminder1 == 'after_due_date' &&
|
$this->settings->schedule_reminder1 == 'after_due_date' &&
|
||||||
$this->settings->num_days_reminder1 > 0) {
|
$this->settings->num_days_reminder1 > 0) {
|
||||||
$reminder_date = Carbon::parse($this->invoice->due_date)->addDays($this->settings->num_days_reminder1);
|
$reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder1)->addSeconds($offset);
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($this->invoice->reminder2_sent) &&
|
if (is_null($this->invoice->reminder2_sent) &&
|
||||||
$this->settings->schedule_reminder2 == 'after_invoice_date' &&
|
$this->settings->schedule_reminder2 == 'after_invoice_date' &&
|
||||||
$this->settings->num_days_reminder2 > 0) {
|
$this->settings->num_days_reminder2 > 0) {
|
||||||
$reminder_date = Carbon::parse($this->invoice->date)->addDays($this->settings->num_days_reminder2);
|
$reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder2)->addSeconds($offset);
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($this->invoice->reminder2_sent) &&
|
if (is_null($this->invoice->reminder2_sent) &&
|
||||||
$this->settings->schedule_reminder2 == 'before_due_date' &&
|
$this->settings->schedule_reminder2 == 'before_due_date' &&
|
||||||
$this->settings->num_days_reminder2 > 0) {
|
$this->settings->num_days_reminder2 > 0) {
|
||||||
$reminder_date = Carbon::parse($this->invoice->due_date)->subDays($this->settings->num_days_reminder2);
|
$reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder2)->addSeconds($offset);
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($this->invoice->reminder2_sent) &&
|
if (is_null($this->invoice->reminder2_sent) &&
|
||||||
$this->settings->schedule_reminder2 == 'after_due_date' &&
|
$this->settings->schedule_reminder2 == 'after_due_date' &&
|
||||||
$this->settings->num_days_reminder2 > 0) {
|
$this->settings->num_days_reminder2 > 0) {
|
||||||
$reminder_date = Carbon::parse($this->invoice->due_date)->addDays($this->settings->num_days_reminder2);
|
$reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder2)->addSeconds($offset);
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($this->invoice->reminder3_sent) &&
|
if (is_null($this->invoice->reminder3_sent) &&
|
||||||
$this->settings->schedule_reminder3 == 'after_invoice_date' &&
|
$this->settings->schedule_reminder3 == 'after_invoice_date' &&
|
||||||
$this->settings->num_days_reminder3 > 0) {
|
$this->settings->num_days_reminder3 > 0) {
|
||||||
$reminder_date = Carbon::parse($this->invoice->date)->addDays($this->settings->num_days_reminder3);
|
$reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder3)->addSeconds($offset);
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($this->invoice->reminder3_sent) &&
|
if (is_null($this->invoice->reminder3_sent) &&
|
||||||
$this->settings->schedule_reminder3 == 'before_due_date' &&
|
$this->settings->schedule_reminder3 == 'before_due_date' &&
|
||||||
$this->settings->num_days_reminder3 > 0) {
|
$this->settings->num_days_reminder3 > 0) {
|
||||||
$reminder_date = Carbon::parse($this->invoice->due_date)->subDays($this->settings->num_days_reminder3);
|
$reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder3)->addSeconds($offset);
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($this->invoice->reminder3_sent) &&
|
if (is_null($this->invoice->reminder3_sent) &&
|
||||||
$this->settings->schedule_reminder3 == 'after_due_date' &&
|
$this->settings->schedule_reminder3 == 'after_due_date' &&
|
||||||
$this->settings->num_days_reminder3 > 0) {
|
$this->settings->num_days_reminder3 > 0) {
|
||||||
$reminder_date = Carbon::parse($this->invoice->due_date)->addDays($this->settings->num_days_reminder3);
|
$reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder3)->addSeconds($offset);
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date)));
|
||||||
$date_collection->push($reminder_date->format('Y-m-d'));
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($date_collection->count() >=1 && $date_collection->sort()->first()->gte(now()))
|
||||||
$this->invoice->next_send_date = $date_collection->sort()->first();
|
$this->invoice->next_send_date = $date_collection->sort()->first();
|
||||||
|
else
|
||||||
|
$this->invoice->next_send_date = null;
|
||||||
|
|
||||||
return $this->invoice;
|
return $this->invoice;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class GetQuotePdf extends AbstractService
|
|||||||
|
|
||||||
$invitation = $this->quote->invitations->where('client_contact_id', $this->contact->id)->first();
|
$invitation = $this->quote->invitations->where('client_contact_id', $this->contact->id)->first();
|
||||||
|
|
||||||
$path = $this->quote->client->quote_filepath();
|
$path = $this->quote->client->quote_filepath($invitation);
|
||||||
|
|
||||||
$file_path = $path.$this->quote->numberFormatter().'.pdf';
|
$file_path = $path.$this->quote->numberFormatter().'.pdf';
|
||||||
|
|
||||||
|
@ -178,7 +178,11 @@ class QuoteService
|
|||||||
|
|
||||||
public function deletePdf()
|
public function deletePdf()
|
||||||
{
|
{
|
||||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->quote->client->quote_filepath() . $this->quote->numberFormatter().'.pdf');
|
$this->quote->invitations->each(function ($invitation){
|
||||||
|
|
||||||
|
UnlinkFile::dispatchNow(config('filesystems.default'), $this->quote->client->quote_filepath($invitation) . $this->quote->numberFormatter().'.pdf');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ class GetInvoicePdf extends AbstractService
|
|||||||
|
|
||||||
$invitation = $this->entity->invitations->where('client_contact_id', $this->contact->id)->first();
|
$invitation = $this->entity->invitations->where('client_contact_id', $this->contact->id)->first();
|
||||||
|
|
||||||
$path = $this->entity->client->recurring_invoice_filepath();
|
$path = $this->entity->client->recurring_invoice_filepath($invitation);
|
||||||
|
|
||||||
$file_path = $path.$this->entity->hashed_id.'.pdf';
|
$file_path = $path.$this->entity->hashed_id.'.pdf';
|
||||||
|
|
||||||
|
@ -87,7 +87,13 @@ class RecurringService
|
|||||||
|
|
||||||
public function deletePdf()
|
public function deletePdf()
|
||||||
{
|
{
|
||||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath() . $this->recurring_entity->numberFormatter().'.pdf');
|
|
||||||
|
$this->recurring_entity->invitations->each(function ($invitation){
|
||||||
|
|
||||||
|
UnlinkFile::dispatchNow(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath($invitation) . $this->recurring_entity->numberFormatter().'.pdf');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -630,7 +630,7 @@ class SubscriptionService
|
|||||||
*/
|
*/
|
||||||
public function triggerWebhook($context)
|
public function triggerWebhook($context)
|
||||||
{
|
{
|
||||||
if (empty($this->subscription->webhook_configuration['post_purchase_url']) || empty($this->subscription->webhook_configuration['post_purchase_rest_method'])) {
|
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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ use App\Models\Account;
|
|||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\CompanyUser;
|
use App\Models\CompanyUser;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,7 +51,7 @@ class CompanyUserTransformer extends EntityTransformer
|
|||||||
'archived_at' => (int) $company_user->deleted_at,
|
'archived_at' => (int) $company_user->deleted_at,
|
||||||
'created_at' => (int) $company_user->created_at,
|
'created_at' => (int) $company_user->created_at,
|
||||||
'permissions_updated_at' => (int) $company_user->permissions_updated_at,
|
'permissions_updated_at' => (int) $company_user->permissions_updated_at,
|
||||||
//'number_years_active' => (int) $company_user->number_years_active,
|
'ninja_portal_url' => (string) $company_user->ninja_portal_url,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +181,7 @@ class HtmlEngine
|
|||||||
$data['$amount_due'] = ['value' => &$data['$total']['value'], 'label' => ctrans('texts.amount_due')];
|
$data['$amount_due'] = ['value' => &$data['$total']['value'], 'label' => ctrans('texts.amount_due')];
|
||||||
$data['$quote.total'] = &$data['$total'];
|
$data['$quote.total'] = &$data['$total'];
|
||||||
$data['$invoice.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.invoice_total')];
|
$data['$invoice.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.invoice_total')];
|
||||||
|
$data['$invoice_total_raw'] = ['value' => $this->entity_calc->getTotal(), 'label' => ctrans('texts.invoice_total')];
|
||||||
$data['$invoice.amount'] = &$data['$total'];
|
$data['$invoice.amount'] = &$data['$total'];
|
||||||
$data['$quote.amount'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.quote_total')];
|
$data['$quote.amount'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.quote_total')];
|
||||||
$data['$credit.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_total')];
|
$data['$credit.total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_total')];
|
||||||
|
@ -62,19 +62,19 @@ class Phantom
|
|||||||
$entity_obj = $invitation->{$entity};
|
$entity_obj = $invitation->{$entity};
|
||||||
|
|
||||||
if ($entity == 'invoice') {
|
if ($entity == 'invoice') {
|
||||||
$path = $entity_obj->client->invoice_filepath();
|
$path = $entity_obj->client->invoice_filepath($invitation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entity == 'quote') {
|
if ($entity == 'quote') {
|
||||||
$path = $entity_obj->client->quote_filepath();
|
$path = $entity_obj->client->quote_filepath($invitation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entity == 'credit') {
|
if ($entity == 'credit') {
|
||||||
$path = $entity_obj->client->credit_filepath();
|
$path = $entity_obj->client->credit_filepath($invitation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entity == 'recurring_invoice') {
|
if ($entity == 'recurring_invoice') {
|
||||||
$path = $entity_obj->client->recurring_invoice_filepath();
|
$path = $entity_obj->client->recurring_invoice_filepath($invitation);
|
||||||
}
|
}
|
||||||
|
|
||||||
$file_path = $path.$entity_obj->numberFormatter().'.pdf';
|
$file_path = $path.$entity_obj->numberFormatter().'.pdf';
|
||||||
@ -90,6 +90,9 @@ class Phantom
|
|||||||
|
|
||||||
$this->checkMime($pdf, $invitation, $entity);
|
$this->checkMime($pdf, $invitation, $entity);
|
||||||
|
|
||||||
|
if(!Storage::disk(config('filesystems.default'))->exists($path))
|
||||||
|
Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775);
|
||||||
|
|
||||||
$instance = Storage::disk(config('filesystems.default'))->put($file_path, $pdf);
|
$instance = Storage::disk(config('filesystems.default'))->put($file_path, $pdf);
|
||||||
|
|
||||||
return $file_path;
|
return $file_path;
|
||||||
@ -118,8 +121,6 @@ class Phantom
|
|||||||
|
|
||||||
$finfo = new \finfo(FILEINFO_MIME);
|
$finfo = new \finfo(FILEINFO_MIME);
|
||||||
|
|
||||||
nlog($pdf);
|
|
||||||
|
|
||||||
if($finfo->buffer($pdf) != 'application/pdf; charset=binary')
|
if($finfo->buffer($pdf) != 'application/pdf; charset=binary')
|
||||||
{
|
{
|
||||||
SystemLogger::dispatch(
|
SystemLogger::dispatch(
|
||||||
|
@ -51,20 +51,20 @@ trait MakesReminders
|
|||||||
if ($this->inReminderWindow(
|
if ($this->inReminderWindow(
|
||||||
$client->getSetting('schedule_reminder1'),
|
$client->getSetting('schedule_reminder1'),
|
||||||
$client->getSetting('num_days_reminder1')
|
$client->getSetting('num_days_reminder1')
|
||||||
)) {
|
) && !$this->reminder1_sent) {
|
||||||
return 'reminder1';
|
return 'reminder1';
|
||||||
} elseif ($this->inReminderWindow(
|
} elseif ($this->inReminderWindow(
|
||||||
$client->getSetting('schedule_reminder2'),
|
$client->getSetting('schedule_reminder2'),
|
||||||
$client->getSetting('num_days_reminder2')
|
$client->getSetting('num_days_reminder2')
|
||||||
)) {
|
) && !$this->reminder2_sent) {
|
||||||
return 'reminder2';
|
return 'reminder2';
|
||||||
} elseif ($this->inReminderWindow(
|
} elseif ($this->inReminderWindow(
|
||||||
$client->getSetting('schedule_reminder3'),
|
$client->getSetting('schedule_reminder3'),
|
||||||
$client->getSetting('num_days_reminder3')
|
$client->getSetting('num_days_reminder3')
|
||||||
)) {
|
) && !$this->reminder3_sent) {
|
||||||
return 'reminder3';
|
return 'reminder3';
|
||||||
} elseif ($this->checkEndlessReminder(
|
} elseif ($this->checkEndlessReminder(
|
||||||
$this->last_sent_date,
|
$this->reminder_last_sent,
|
||||||
$client->getSetting('endless_reminder_frequency_id')
|
$client->getSetting('endless_reminder_frequency_id')
|
||||||
)) {
|
)) {
|
||||||
return 'endless_reminder';
|
return 'endless_reminder';
|
||||||
@ -77,6 +77,10 @@ trait MakesReminders
|
|||||||
|
|
||||||
private function checkEndlessReminder($last_sent_date, $endless_reminder_frequency_id) :bool
|
private function checkEndlessReminder($last_sent_date, $endless_reminder_frequency_id) :bool
|
||||||
{
|
{
|
||||||
|
nlog("endless date match = ".$this->addTimeInterval($last_sent_date, $endless_reminder_frequency_id));
|
||||||
|
nlog("Endless reminder bool = ");
|
||||||
|
nlog(Carbon::now()->startOfDay()->eq($this->addTimeInterval($last_sent_date, $endless_reminder_frequency_id)));
|
||||||
|
|
||||||
if (Carbon::now()->startOfDay()->eq($this->addTimeInterval($last_sent_date, $endless_reminder_frequency_id))) {
|
if (Carbon::now()->startOfDay()->eq($this->addTimeInterval($last_sent_date, $endless_reminder_frequency_id))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -86,10 +90,13 @@ trait MakesReminders
|
|||||||
|
|
||||||
private function addTimeInterval($date, $endless_reminder_frequency_id) :?Carbon
|
private function addTimeInterval($date, $endless_reminder_frequency_id) :?Carbon
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!$date)
|
if (!$date)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
switch ($endless_reminder_frequency_id) {
|
switch ($endless_reminder_frequency_id) {
|
||||||
|
case RecurringInvoice::FREQUENCY_DAILY:
|
||||||
|
return Carbon::parse($date)->addDay()->startOfDay();
|
||||||
case RecurringInvoice::FREQUENCY_WEEKLY:
|
case RecurringInvoice::FREQUENCY_WEEKLY:
|
||||||
return Carbon::parse($date)->addWeek()->startOfDay();
|
return Carbon::parse($date)->addWeek()->startOfDay();
|
||||||
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
|
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
|
||||||
|
@ -99,6 +99,24 @@ return [
|
|||||||
// ),
|
// ),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'db-ninja-01a' => [
|
||||||
|
'driver' => 'mysql',
|
||||||
|
'host' => env('DB_HOST1', env('DB_HOST', '127.0.0.1')),
|
||||||
|
'database' => env('DB_DATABASE2', env('DB_DATABASE', 'forge')),
|
||||||
|
'username' => env('DB_USERNAME2', env('DB_USERNAME', 'forge')),
|
||||||
|
'password' => env('DB_PASSWORD2', env('DB_PASSWORD', '')),
|
||||||
|
'port' => env('DB_PORT1', env('DB_PORT', '3306')),
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_unicode_ci',
|
||||||
|
'prefix' => '',
|
||||||
|
'prefix_indexes' => true,
|
||||||
|
'strict' => env('DB_STRICT', false),
|
||||||
|
'engine' => 'InnoDB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8',
|
||||||
|
// 'options' => array(
|
||||||
|
// PDO::ATTR_EMULATE_PREPARES => true
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
|
||||||
'db-ninja-02' => [
|
'db-ninja-02' => [
|
||||||
'driver' => 'mysql',
|
'driver' => 'mysql',
|
||||||
'host' => env('DB_HOST2', env('DB_HOST', '127.0.0.1')),
|
'host' => env('DB_HOST2', env('DB_HOST', '127.0.0.1')),
|
||||||
@ -116,6 +134,24 @@ return [
|
|||||||
// PDO::ATTR_EMULATE_PREPARES => true
|
// PDO::ATTR_EMULATE_PREPARES => true
|
||||||
// ),
|
// ),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'db-ninja-02a' => [
|
||||||
|
'driver' => 'mysql',
|
||||||
|
'host' => env('DB_HOST2', env('DB_HOST', '127.0.0.1')),
|
||||||
|
'database' => env('DB_DATABASE1', env('DB_DATABASE', 'forge')),
|
||||||
|
'username' => env('DB_USERNAME1', env('DB_USERNAME', 'forge')),
|
||||||
|
'password' => env('DB_PASSWORD1', env('DB_PASSWORD', '')),
|
||||||
|
'port' => env('DB_PORT2', env('DB_PORT', '3306')),
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_unicode_ci',
|
||||||
|
'prefix' => '',
|
||||||
|
'prefix_indexes' => true,
|
||||||
|
'strict' => env('DB_STRICT', false),
|
||||||
|
'engine' => 'InnoDB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8',
|
||||||
|
// 'options' => array(
|
||||||
|
// PDO::ATTR_EMULATE_PREPARES => true
|
||||||
|
// ),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -14,8 +14,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => '5.1.71',
|
'app_version' => '5.2.4',
|
||||||
'app_tag' => '5.1.71-release',
|
'app_tag' => '5.2.4-release',
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', ''),
|
'api_secret' => env('API_SECRET', ''),
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddNinjaPortalColumnToAccountsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('company_user', function (Blueprint $table) {
|
||||||
|
$table->text('ninja_portal_url')->default('');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
2
public/flutter_service_worker.js
vendored
2
public/flutter_service_worker.js
vendored
@ -5,7 +5,7 @@ const CACHE_NAME = 'flutter-app-cache';
|
|||||||
const RESOURCES = {
|
const RESOURCES = {
|
||||||
"version.json": "9fe5b22a16f39b766c8fdc35a24b3efa",
|
"version.json": "9fe5b22a16f39b766c8fdc35a24b3efa",
|
||||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||||
"main.dart.js": "e63018de16a321755f090480820081ff",
|
"main.dart.js": "33f9288e9a8ba68d21b46b8afda06fbb",
|
||||||
"/": "23224b5e03519aaa87594403d54412cf",
|
"/": "23224b5e03519aaa87594403d54412cf",
|
||||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||||
"assets/AssetManifest.json": "659dcf9d1baf3aed3ab1b9c42112bf8f",
|
"assets/AssetManifest.json": "659dcf9d1baf3aed3ab1b9c42112bf8f",
|
||||||
|
166703
public/main.dart.js
vendored
166703
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
170743
public/main.foss.dart.js
vendored
170743
public/main.foss.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -4264,6 +4264,8 @@ $LANG = array(
|
|||||||
'client_email_company_contact_label' => 'If you have any questions please contact us, we\'re here to help!',
|
'client_email_company_contact_label' => 'If you have any questions please contact us, we\'re here to help!',
|
||||||
'quote_was_approved_label' => 'Quote was approved',
|
'quote_was_approved_label' => 'Quote was approved',
|
||||||
'quote_was_approved' => 'We would like to inform you that quote was approved.',
|
'quote_was_approved' => 'We would like to inform you that quote was approved.',
|
||||||
|
'company_import_failure_subject' => 'Error importing :company',
|
||||||
|
'company_import_failure_body' => 'There was an error importing the company data, the error message was:',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $LANG;
|
return $LANG;
|
||||||
|
22
resources/views/email/import/import_failure.blade.php
Normal file
22
resources/views/email/import/import_failure.blade.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@component('email.template.master', ['design' => 'light', 'settings' => $settings])
|
||||||
|
|
||||||
|
@slot('header')
|
||||||
|
@include('email.components.header', ['logo' => $logo])
|
||||||
|
@endslot
|
||||||
|
|
||||||
|
<h2>{{ $title }}</h2>
|
||||||
|
|
||||||
|
<p>{{ctrans('texts.company_import_failure_body')}}</p>
|
||||||
|
|
||||||
|
@if($user_message)
|
||||||
|
<p>{{ $user_message }}</p>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(isset($whitelabel) && !$whitelabel)
|
||||||
|
@slot('footer')
|
||||||
|
@component('email.components.footer', ['url' => 'https://invoiceninja.com', 'url_text' => '© InvoiceNinja'])
|
||||||
|
For any info, please visit InvoiceNinja.
|
||||||
|
@endcomponent
|
||||||
|
@endslot
|
||||||
|
@endif
|
||||||
|
@endcomponent
|
@ -4,12 +4,11 @@
|
|||||||
<p>{{ ctrans('texts.migration_failed') }} {{ $company->present()->name() }}</p>
|
<p>{{ ctrans('texts.migration_failed') }} {{ $company->present()->name() }}</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@if(\App\Utils\Ninja::isHosted())
|
@if(\App\Utils\Ninja::isSelfHost())
|
||||||
{!! $exception->getMessage() !!}
|
{!! $exception->getMessage() !!}
|
||||||
{!! $content !!}
|
{!! $content !!}
|
||||||
@else
|
@else
|
||||||
{!! $exception->getMessage() !!}
|
<p>Please contact us at contact@invoiceninja.com for more information on this error.</p>
|
||||||
{!! $content !!}
|
|
||||||
@endif
|
@endif
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,6 +13,6 @@
|
|||||||
|
|
||||||
@section('body')
|
@section('body')
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
@livewire('credits-table')
|
@livewire('credits-table', ['company' => $company])
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
@ -14,5 +14,5 @@
|
|||||||
@csrf
|
@csrf
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@livewire('documents-table', ['client' => $client])
|
@livewire('documents-table', ['client' => $client, 'company' => $company])
|
||||||
@endsection
|
@endsection
|
||||||
|
@ -20,6 +20,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col mt-4">
|
<div class="flex flex-col mt-4">
|
||||||
@livewire('invoices-table')
|
@livewire('invoices-table', ['company' => $company])
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user