diff --git a/app/Console/Commands/CreateSingleAccount.php b/app/Console/Commands/CreateSingleAccount.php
new file mode 100644
index 000000000000..ee489b53d7b7
--- /dev/null
+++ b/app/Console/Commands/CreateSingleAccount.php
@@ -0,0 +1,565 @@
+invoice_repo = $invoice_repo;
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $this->info(date('r').' Create Single Sample Account...');
+ $this->count = 1;
+ $this->gateway = $this->argument('gateway');
+
+ $this->info('Warming up cache');
+
+ $this->warmCache();
+
+ $this->createSmallAccount();
+
+ }
+
+ private function createSmallAccount()
+ {
+ $this->info('Creating Small Account and Company');
+
+ $account = Account::factory()->create();
+ $company = Company::factory()->create([
+ 'account_id' => $account->id,
+ 'slack_webhook_url' => config('ninja.notification.slack'),
+ ]);
+
+ $account->default_company_id = $company->id;
+ $account->save();
+
+ $user = User::whereEmail('small@example.com')->first();
+
+ if (! $user) {
+ $user = User::factory()->create([
+ 'account_id' => $account->id,
+ 'email' => 'small@example.com',
+ 'confirmation_code' => $this->createDbHash(config('database.default')),
+ ]);
+ }
+
+ $company_token = new CompanyToken;
+ $company_token->user_id = $user->id;
+ $company_token->company_id = $company->id;
+ $company_token->account_id = $account->id;
+ $company_token->name = 'test token';
+ $company_token->token = Str::random(64);
+ $company_token->is_system = true;
+
+ $company_token->save();
+
+ $user->companies()->attach($company->id, [
+ 'account_id' => $account->id,
+ 'is_owner' => 1,
+ 'is_admin' => 1,
+ 'is_locked' => 0,
+ 'notifications' => CompanySettings::notificationDefaults(),
+ 'settings' => null,
+ ]);
+
+ Product::factory()->count(1)->create([
+ 'user_id' => $user->id,
+ 'company_id' => $company->id,
+ ]);
+
+ $this->info('Creating '.$this->count.' clients');
+
+ for ($x = 0; $x < $this->count; $x++) {
+ $z = $x + 1;
+ $this->info('Creating client # '.$z);
+
+ $this->createClient($company, $user);
+ }
+
+ for ($x = 0; $x < $this->count; $x++) {
+ $client = $company->clients->random();
+
+ $this->info('creating invoice for client #'.$client->id);
+ $this->createInvoice($client);
+ $this->info('creating invoice for client #'.$client->id);
+ $this->createInvoice($client);
+ $this->info('creating invoice for client #'.$client->id);
+ $this->createInvoice($client);
+ $this->info('creating invoice for client #'.$client->id);
+ $this->createInvoice($client);
+ $this->info('creating invoice for client #'.$client->id);
+ $this->createInvoice($client);
+
+ $client = $company->clients->random();
+
+ $this->info('creating credit for client #'.$client->id);
+ $this->createCredit($client);
+
+ $client = $company->clients->random();
+
+ $this->info('creating quote for client #'.$client->id);
+ $this->createQuote($client);
+
+ $client = $company->clients->random();
+
+ $this->info('creating expense for client #'.$client->id);
+ $this->createExpense($client);
+
+ $client = $company->clients->random();
+
+ $this->info('creating vendor for client #'.$client->id);
+ $this->createVendor($client);
+
+ $client = $company->clients->random();
+
+ $this->info('creating task for client #'.$client->id);
+ $this->createTask($client);
+
+ $client = $company->clients->random();
+
+ $this->info('creating project for client #'.$client->id);
+ $this->createProject($client);
+ }
+
+ $this->createGateways($company, $user);
+ }
+
+ private function createClient($company, $user)
+ {
+
+ // dispatch(function () use ($company, $user) {
+
+ // });
+ $client = Client::factory()->create([
+ 'user_id' => $user->id,
+ 'company_id' => $company->id,
+ ]);
+
+ ClientContact::factory()->create([
+ 'user_id' => $user->id,
+ 'client_id' => $client->id,
+ 'company_id' => $company->id,
+ 'is_primary' => 1,
+ 'email' => 'user@example.com'
+ ]);
+
+ ClientContact::factory()->count(rand(1, 2))->create([
+ 'user_id' => $user->id,
+ 'client_id' => $client->id,
+ 'company_id' => $company->id,
+ ]);
+
+ $client->id_number = $this->getNextClientNumber($client);
+
+ $settings = $client->settings;
+ $settings->currency_id = (string) rand(1, 79);
+ $client->settings = $settings;
+
+ $country = Country::all()->random();
+
+ $client->country_id = $country->id;
+ $client->save();
+ }
+
+ private function createExpense($client)
+ {
+ Expense::factory()->count(rand(1, 2))->create([
+ 'user_id' => $client->user->id,
+ 'client_id' => $client->id,
+ 'company_id' => $client->company->id,
+ ]);
+ }
+
+ private function createVendor($client)
+ {
+ $vendor = Vendor::factory()->create([
+ 'user_id' => $client->user->id,
+ 'company_id' => $client->company->id,
+ ]);
+
+ VendorContact::factory()->create([
+ 'user_id' => $client->user->id,
+ 'vendor_id' => $vendor->id,
+ 'company_id' => $client->company->id,
+ 'is_primary' => 1,
+ ]);
+
+ VendorContact::factory()->count(rand(1, 2))->create([
+ 'user_id' => $client->user->id,
+ 'vendor_id' => $vendor->id,
+ 'company_id' => $client->company->id,
+ 'is_primary' => 0,
+ ]);
+ }
+
+ private function createTask($client)
+ {
+ $vendor = Task::factory()->create([
+ 'user_id' => $client->user->id,
+ 'company_id' => $client->company->id,
+ ]);
+ }
+
+ private function createProject($client)
+ {
+ $vendor = Project::factory()->create([
+ 'user_id' => $client->user->id,
+ 'company_id' => $client->company->id,
+ ]);
+ }
+
+ private function createInvoice($client)
+ {
+
+ $faker = \Faker\Factory::create();
+
+ $invoice = InvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
+ $invoice->client_id = $client->id;
+ $dateable = Carbon::now()->subDays(rand(0, 90));
+ $invoice->date = $dateable;
+
+ $invoice->line_items = $this->buildLineItems(rand(1, 10));
+ $invoice->uses_inclusive_taxes = false;
+
+ if (rand(0, 1)) {
+ $invoice->tax_name1 = 'GST';
+ $invoice->tax_rate1 = 10.00;
+ }
+
+ if (rand(0, 1)) {
+ $invoice->tax_name2 = 'VAT';
+ $invoice->tax_rate2 = 17.50;
+ }
+
+ if (rand(0, 1)) {
+ $invoice->tax_name3 = 'CA Sales Tax';
+ $invoice->tax_rate3 = 5;
+ }
+
+ $invoice->custom_value1 = $faker->date;
+ $invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
+
+ $invoice->save();
+
+ $invoice_calc = new InvoiceSum($invoice);
+ $invoice_calc->build();
+
+ $invoice = $invoice_calc->getInvoice();
+
+ $invoice->save();
+ $invoice->service()->createInvitations()->markSent();
+
+ $this->invoice_repo->markSent($invoice);
+
+ // if (rand(0, 1)) {
+ // $invoice = $invoice->service()->markPaid()->save();
+ // }
+ //@todo this slow things down, but gives us PDFs of the invoices for inspection whilst debugging.
+ event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
+ }
+
+ private function createCredit($client)
+ {
+ // for($x=0; $x<$this->count; $x++){
+
+ // dispatch(new CreateTestCreditJob($client));
+
+ // }
+ $faker = \Faker\Factory::create();
+
+ $credit = Credit::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
+
+ $dateable = Carbon::now()->subDays(rand(0, 90));
+ $credit->date = $dateable;
+
+ $credit->line_items = $this->buildLineItems(rand(1, 10));
+ $credit->uses_inclusive_taxes = false;
+
+ if (rand(0, 1)) {
+ $credit->tax_name1 = 'GST';
+ $credit->tax_rate1 = 10.00;
+ }
+
+ if (rand(0, 1)) {
+ $credit->tax_name2 = 'VAT';
+ $credit->tax_rate2 = 17.50;
+ }
+
+ if (rand(0, 1)) {
+ $credit->tax_name3 = 'CA Sales Tax';
+ $credit->tax_rate3 = 5;
+ }
+
+ $credit->save();
+
+ $invoice_calc = new InvoiceSum($credit);
+ $invoice_calc->build();
+
+ $credit = $invoice_calc->getCredit();
+
+ $credit->save();
+ $credit->service()->markSent()->save();
+ $credit->service()->createInvitations();
+ }
+
+ private function createQuote($client)
+ {
+
+ $faker = \Faker\Factory::create();
+
+ $quote = Quote::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
+ $quote->date = $faker->date();
+ $quote->client_id = $client->id;
+
+ $quote->setRelation('client', $client);
+
+ $quote->line_items = $this->buildLineItems(rand(1, 10));
+ $quote->uses_inclusive_taxes = false;
+
+ if (rand(0, 1)) {
+ $quote->tax_name1 = 'GST';
+ $quote->tax_rate1 = 10.00;
+ }
+
+ if (rand(0, 1)) {
+ $quote->tax_name2 = 'VAT';
+ $quote->tax_rate2 = 17.50;
+ }
+
+ if (rand(0, 1)) {
+ $quote->tax_name3 = 'CA Sales Tax';
+ $quote->tax_rate3 = 5;
+ }
+
+ $quote->save();
+
+ $quote_calc = new InvoiceSum($quote);
+ $quote_calc->build();
+
+ $quote = $quote_calc->getQuote();
+
+ $quote->save();
+
+ $quote->service()->markSent()->save();
+ $quote->service()->createInvitations();
+ }
+
+ private function buildLineItems($count = 1)
+ {
+ $line_items = [];
+
+ for ($x = 0; $x < $count; $x++) {
+ $item = InvoiceItemFactory::create();
+ $item->quantity = 1;
+ //$item->cost = 10;
+
+ if (rand(0, 1)) {
+ $item->tax_name1 = 'GST';
+ $item->tax_rate1 = 10.00;
+ }
+
+ if (rand(0, 1)) {
+ $item->tax_name1 = 'VAT';
+ $item->tax_rate1 = 17.50;
+ }
+
+ if (rand(0, 1)) {
+ $item->tax_name1 = 'Sales Tax';
+ $item->tax_rate1 = 5;
+ }
+
+ $product = Product::all()->random();
+
+ $item->cost = (float) $product->cost;
+ $item->product_key = $product->product_key;
+ $item->notes = $product->notes;
+ $item->custom_value1 = $product->custom_value1;
+ $item->custom_value2 = $product->custom_value2;
+ $item->custom_value3 = $product->custom_value3;
+ $item->custom_value4 = $product->custom_value4;
+
+ $line_items[] = $item;
+ }
+
+ return $line_items;
+ }
+
+ private function warmCache()
+ {
+ /* Warm up the cache !*/
+ $cached_tables = config('ninja.cached_tables');
+
+ foreach ($cached_tables as $name => $class) {
+ if (! Cache::has($name)) {
+ // check that the table exists in case the migration is pending
+ if (! Schema::hasTable((new $class())->getTable())) {
+ continue;
+ }
+ if ($name == 'payment_terms') {
+ $orderBy = 'num_days';
+ } elseif ($name == 'fonts') {
+ $orderBy = 'sort_order';
+ } elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
+ $orderBy = 'name';
+ } else {
+ $orderBy = 'id';
+ }
+ $tableData = $class::orderBy($orderBy)->get();
+ if ($tableData->count()) {
+ Cache::forever($name, $tableData);
+ }
+ }
+ }
+ }
+
+ private function createGateways($company, $user)
+ {
+
+ if (config('ninja.testvars.stripe') && ($this->gateway == 'all' || $this->gateway == 'stripe')) {
+ $cg = new CompanyGateway;
+ $cg->company_id = $company->id;
+ $cg->user_id = $user->id;
+ $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23';
+ $cg->require_cvv = true;
+ $cg->show_billing_address = true;
+ $cg->show_shipping_address = true;
+ $cg->update_details = true;
+ $cg->config = encrypt(config('ninja.testvars.stripe'));
+ $cg->save();
+
+ // $cg = new CompanyGateway;
+ // $cg->company_id = $company->id;
+ // $cg->user_id = $user->id;
+ // $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23';
+ // $cg->require_cvv = true;
+ // $cg->show_billing_address = true;
+ // $cg->show_shipping_address = true;
+ // $cg->update_details = true;
+ // $cg->config = encrypt(config('ninja.testvars.stripe'));
+ // $cg->save();
+ }
+
+ if (config('ninja.testvars.paypal') && ($this->gateway == 'all' || $this->gateway == 'paypal')) {
+ $cg = new CompanyGateway;
+ $cg->company_id = $company->id;
+ $cg->user_id = $user->id;
+ $cg->gateway_key = '38f2c48af60c7dd69e04248cbb24c36e';
+ $cg->require_cvv = true;
+ $cg->show_billing_address = true;
+ $cg->show_shipping_address = true;
+ $cg->update_details = true;
+ $cg->config = encrypt(config('ninja.testvars.paypal'));
+ $cg->save();
+ }
+
+ if (config('ninja.testvars.checkout') && ($this->gateway == 'all' || $this->gateway == 'checkout')) {
+ $cg = new CompanyGateway;
+ $cg->company_id = $company->id;
+ $cg->user_id = $user->id;
+ $cg->gateway_key = '3758e7f7c6f4cecf0f4f348b9a00f456';
+ $cg->require_cvv = true;
+ $cg->show_billing_address = true;
+ $cg->show_shipping_address = true;
+ $cg->update_details = true;
+ $cg->config = encrypt(config('ninja.testvars.checkout'));
+ $cg->save();
+ }
+
+ if (config('ninja.testvars.authorize') && ($this->gateway == 'all' || $this->gateway == 'authorizenet')) {
+ $cg = new CompanyGateway;
+ $cg->company_id = $company->id;
+ $cg->user_id = $user->id;
+ $cg->gateway_key = '3b6621f970ab18887c4f6dca78d3f8bb';
+ $cg->require_cvv = true;
+ $cg->show_billing_address = true;
+ $cg->show_shipping_address = true;
+ $cg->update_details = true;
+ $cg->config = encrypt(config('ninja.testvars.authorize'));
+ $cg->save();
+ }
+ }
+}
diff --git a/app/DataMapper/Analytics/EmailFailure.php b/app/DataMapper/Analytics/EmailFailure.php
new file mode 100644
index 000000000000..33f0078b121e
--- /dev/null
+++ b/app/DataMapper/Analytics/EmailFailure.php
@@ -0,0 +1,57 @@
+ 'strip',
+ 'allow_unsafe_links' => false,
+ ]);
+
+ return $converter->convertToHtml(self::transformText('payment_message'));
+ }
+
public static function emailPaymentPartialSubject()
{
return ctrans('texts.payment_subject');
diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php
index b5489b4804f5..bdbb69a6f86c 100644
--- a/app/Http/Controllers/ClientPortal/PaymentController.php
+++ b/app/Http/Controllers/ClientPortal/PaymentController.php
@@ -69,7 +69,7 @@ class PaymentController extends Controller
*/
public function process()
{
- $gateway = CompanyGateway::findOrFail('id', request()->input('company_gateway_id'));
+ $gateway = CompanyGateway::findOrFail(request()->input('company_gateway_id'));
/*find invoices*/
diff --git a/app/Jobs/Mail/BaseMailerJob.php b/app/Jobs/Mail/BaseMailerJob.php
index a05283344e04..2567f954eef6 100644
--- a/app/Jobs/Mail/BaseMailerJob.php
+++ b/app/Jobs/Mail/BaseMailerJob.php
@@ -11,6 +11,7 @@
namespace App\Jobs\Mail;
+use App\DataMapper\Analytics\EmailFailure;
use App\Libraries\Google\Google;
use App\Libraries\MultiDB;
use App\Models\User;
@@ -21,6 +22,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Config;
+use Turbo124\Beacon\Facades\LightLogs;
/*Multi Mailer implemented*/
@@ -76,4 +78,18 @@ class BaseMailerJob implements ShouldQueue
$recipient_object
);
}
+
+ public function failed($exception = null)
+ {
+
+ info('the job failed');
+
+ $job_failure = new EmailFailure();
+ $job_failure->string_metric5 = get_parent_class($this);
+ $job_failure->string_metric6 = $exception->getMessage();
+
+ LightLogs::create($job_failure)
+ ->batch();
+
+ }
}
diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php
index 725352c06ab1..595078dbbcf0 100644
--- a/app/Models/Gateway.php
+++ b/app/Models/Gateway.php
@@ -56,7 +56,6 @@ class Gateway extends StaticModel
return $this->getMethods();
}
-
/**
* Test if gateway is custom.
* @return bool TRUE|FALSE
diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php
index caf7be6f0494..4d11d07b0295 100644
--- a/app/PaymentDrivers/StripePaymentDriver.php
+++ b/app/PaymentDrivers/StripePaymentDriver.php
@@ -39,7 +39,7 @@ use Stripe\PaymentIntent;
use Stripe\SetupIntent;
use Stripe\Stripe;
-class StripePaymentDriver extends BasePaymentDriver
+class StripePaymentDriver extends BaseDriver
{
use MakesHash, Utilities;
@@ -54,7 +54,7 @@ class StripePaymentDriver extends BasePaymentDriver
protected $customer_reference = 'customerReferenceParam';
- protected $payment_method;
+ public $payment_method;
public static $methods = [
@@ -319,7 +319,7 @@ class StripePaymentDriver extends BasePaymentDriver
return $customer;
}
- public function refund(Payment $payment, $amount)
+ public function refund(Payment $payment, $amount, $return_client_response = false)
{
$this->init();
@@ -427,4 +427,11 @@ class StripePaymentDriver extends BasePaymentDriver
], SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_STRIPE, $this->client);
}
}
+
+
+ public function getCompanyGatewayId(): int
+ {
+ return $this->company_gateway->id;
+ }
+
}
diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php
index 11af9ed337f8..51a98d161675 100644
--- a/app/Services/Invoice/InvoiceService.php
+++ b/app/Services/Invoice/InvoiceService.php
@@ -12,6 +12,7 @@
namespace App\Services\Invoice;
use App\Jobs\Invoice\CreateInvoicePdf;
+use App\Jobs\Util\UnlinkFile;
use App\Models\CompanyGateway;
use App\Models\Invoice;
use App\Models\Payment;
@@ -211,7 +212,7 @@ class InvoiceService
public function updateStatus()
{
- if($this->invoice->balance == 0)
+ if((int)$this->invoice->balance == 0)
$this->setStatus(Invoice::STATUS_PAID);
if($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount)
@@ -233,6 +234,15 @@ class InvoiceService
$this->invoice = $this->invoice->calc()->getInvoice();
+ $this->deletePdf();
+
+ return $this;
+ }
+
+ public function deletePdf()
+ {
+ UnlinkFile::dispatchNow(config('filesystems.default'),$this->invoice->client->invoice_filepath() . $this->invoice->number.'.pdf');
+
return $this;
}
diff --git a/app/Utils/Traits/AppSetup.php b/app/Utils/Traits/AppSetup.php
index a1f5280e66ac..9c303823fa2b 100644
--- a/app/Utils/Traits/AppSetup.php
+++ b/app/Utils/Traits/AppSetup.php
@@ -80,6 +80,10 @@ trait AppSetup
'subject' => EmailTemplateDefaults::emailPaymentSubject(),
'body' => EmailTemplateDefaults::emailPaymentTemplate(),
],
+ 'payment_partial' => [
+ 'subject' => EmailTemplateDefaults::emailPaymentPartialSubject(),
+ 'body' => EmailTemplateDefaults::emailPaymentPartialTemplate(),
+ ],
'reminder1' => [
'subject' => EmailTemplateDefaults::emailReminder1Subject(),
'body' => EmailTemplateDefaults::emailReminder1Template(),
diff --git a/phpunit.xml b/phpunit.xml
index 30a152af0175..e1b0575994e1 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,37 +1,6 @@
-
-
- ./app
-
-
- ./vendor
- ./app/Providers
- ./app/Http
- ./app/Models
- ./app/Transformers
- ./app/Events
- ./app/Observers
- ./app/Policies
- ./app/Jobs
- ./app/Factory
- ./app/Helpers
- ./app/Libraries
- ./app/Listeners
- ./app/Mail
- ./app/Notifications
- ./app/Providers
- ./app/Repositories
- ./app/Filters
- ./app/Console/Kernel.php
- ./app/Constants.php
- ./app/Libraries/OFX.php
- ./app/Exceptions/Handler.php
-
-
-
-
-
+
./tests/Unit
@@ -53,6 +22,8 @@
+
+
diff --git a/tests/Browser/Client/PaymentMethods.php b/tests/Browser/Client/PaymentMethods.php
deleted file mode 100644
index 6fd8e6968e4e..000000000000
--- a/tests/Browser/Client/PaymentMethods.php
+++ /dev/null
@@ -1,32 +0,0 @@
-browse(function (Browser $browser) {
- $browser->visit('/client/login')
- ->type('email', 'user@example.com')
- ->type('password', config('ninja.testvars.password'))
- ->press('Login')
- ->assertPathIs('/client/dashboard');
-
- $browser->visit(route('client.payment_methods.index'))
- ->waitFor('.dataTable')
- ->waitFor('.dataTables_empty')
- ->assertSee('No records found');
-
- // TODO: Testing Stripe