Merge branch 'v5-develop' into preview

This commit is contained in:
David Bomba 2023-10-14 08:43:33 +11:00
commit 0cb6247680
292 changed files with 180933 additions and 176386 deletions

View File

@ -50,8 +50,7 @@ We offer a $30 per year white-label license to remove the Invoice Ninja branding
## Quick Hosting Setup ## Quick Hosting Setup
```sh ```sh
git clone https://github.com/invoiceninja/invoiceninja.git git clone --single-branch --branch v5-stable https://github.com/invoiceninja/invoiceninja.git
git checkout v5-stable
cp .env.example .env cp .env.example .env
composer i -o --no-dev composer i -o --no-dev
php artisan key:generate php artisan key:generate

View File

@ -1 +1 @@
5.7.22 5.7.30

View File

@ -63,17 +63,25 @@ class CreateAccount extends Command
private function createAccount() private function createAccount()
{ {
$settings = CompanySettings::defaults();
$settings->name = "Untitled Company";
$settings->currency_id = '1';
$settings->language_id = '1';
$account = Account::factory()->create(); $account = Account::factory()->create();
$company = Company::factory()->create([ $company = Company::factory()->create([
'account_id' => $account->id, 'account_id' => $account->id,
'portal_domain' => config('ninja.app_url'), 'portal_domain' => config('ninja.app_url'),
'portal_mode' => 'domain', 'portal_mode' => 'domain',
'settings' => $settings,
]); ]);
$company->client_registration_fields = ClientRegistrationFields::generate(); $company->client_registration_fields = ClientRegistrationFields::generate();
$company->save(); $company->save();
$account->default_company_id = $company->id; $account->default_company_id = $company->id;
$account->set_react_as_default_ap = true;
$account->save(); $account->save();
$email = $this->option('email') ?? 'admin@example.com'; $email = $this->option('email') ?? 'admin@example.com';

View File

@ -48,7 +48,15 @@ class ReactBuilder extends Command
{ {
$includes = ''; $includes = '';
$directoryIterator = new \RecursiveDirectoryIterator(public_path('react/v'.config('ninja.app_version').'/'), \RecursiveDirectoryIterator::SKIP_DOTS); $directoryIterator = false;
try {
$directoryIterator = new \RecursiveDirectoryIterator(public_path('react/v'.config('ninja.app_version').'/'), \RecursiveDirectoryIterator::SKIP_DOTS);
}
catch (\Exception $e) {
$this->error('React files not found');
return;
}
foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) { foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {
if ($file->getExtension() == 'js') { if ($file->getExtension() == 'js') {

View File

@ -841,6 +841,8 @@ class CompanySettings extends BaseSettings
{ {
$notification = new stdClass; $notification = new stdClass;
$notification->email = []; $notification->email = [];
$notification->email = ['invoice_sent_all'];
// $notification->email = ['all_notifications']; // $notification->email = ['all_notifications'];
return $notification; return $notification;

View File

@ -26,7 +26,7 @@ class UserWasCreated
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public User $user, public User $creating_user, public Company $company, public array $event_vars) public function __construct(public User $user, public User $creating_user, public Company $company, public array $event_vars, public $is_react = true)
{ {
} }

View File

@ -54,7 +54,7 @@ class ActivityExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
@ -159,8 +159,6 @@ class ActivityExport extends BaseExport
foreach (array_values($this->input['report_keys']) as $key => $value) { foreach (array_values($this->input['report_keys']) as $key => $value) {
nlog("key: {$key}, value: {$value}");
nlog($row);
$clean_row[$key]['entity'] = 'activity'; $clean_row[$key]['entity'] = 'activity';
$clean_row[$key]['id'] = $key; $clean_row[$key]['id'] = $key;
$clean_row[$key]['hashed_id'] = null; $clean_row[$key]['hashed_id'] = null;

View File

@ -83,6 +83,7 @@ class BaseExport
'contact_custom_value4' => 'vendor_contact.custom_value4', 'contact_custom_value4' => 'vendor_contact.custom_value4',
'email' => 'vendor_contact.email', 'email' => 'vendor_contact.email',
'status' => 'vendor.status', 'status' => 'vendor.status',
'classification' => 'vendor.classification',
]; ];
protected array $client_report_keys = [ protected array $client_report_keys = [
@ -125,10 +126,13 @@ class BaseExport
"contact_custom_value2" => "contact.custom_value2", "contact_custom_value2" => "contact.custom_value2",
"contact_custom_value3" => "contact.custom_value3", "contact_custom_value3" => "contact.custom_value3",
"contact_custom_value4" => "contact.custom_value4", "contact_custom_value4" => "contact.custom_value4",
'payment_balance' => 'client.payment_balance',
'credit_balance' => 'client.credit_balance',
'classification' => 'client.classification',
]; ];
protected array $invoice_report_keys = [ protected array $invoice_report_keys = [
'name' => 'client.name',
"invoice_number" => "invoice.number", "invoice_number" => "invoice.number",
"amount" => "invoice.amount", "amount" => "invoice.amount",
"balance" => "invoice.balance", "balance" => "invoice.balance",

View File

@ -71,7 +71,11 @@ class ClientExport extends BaseExport
'contact_custom_value3' => 'contact.custom_value3', 'contact_custom_value3' => 'contact.custom_value3',
'contact_custom_value4' => 'contact.custom_value4', 'contact_custom_value4' => 'contact.custom_value4',
'email' => 'contact.email', 'email' => 'contact.email',
'status' => 'status' 'status' => 'status',
'payment_balance' => 'client.payment_balance',
'credit_balance' => 'client.credit_balance',
'classification' => 'client.classification',
]; ];
public function __construct(Company $company, array $input) public function __construct(Company $company, array $input)
@ -89,7 +93,7 @@ class ClientExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()
@ -185,7 +189,7 @@ class ClientExport extends BaseExport
$clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0]; $clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0];
$clean_row[$key]['hashed_id'] = $report_keys[0] == 'client' ? null : $resource->{$report_keys[0]}->hashed_id ?? null; $clean_row[$key]['hashed_id'] = $report_keys[0] == 'client' ? null : $resource->{$report_keys[0]}->hashed_id ?? null;
$clean_row[$key]['value'] = $row[$column_key]; $clean_row[$key]['value'] = $row[$column_key];
$clean_row[$key]['identifier'] = $value; $clean_row[$key]['identifier'] = $key;
if(in_array($clean_row[$key]['id'], ['paid_to_date', 'balance', 'credit_balance','payment_balance'])) if(in_array($clean_row[$key]['id'], ['paid_to_date', 'balance', 'credit_balance','payment_balance']))
$clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $resource); $clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $resource);
@ -223,6 +227,10 @@ class ClientExport extends BaseExport
$entity['industry_id'] = $client->industry ? ctrans("texts.industry_{$client->industry->name}") : ''; $entity['industry_id'] = $client->industry ? ctrans("texts.industry_{$client->industry->name}") : '';
} }
if (in_array('client.classification', $this->input['report_keys']) && isset($client->classification)) {
$entity['client.classification'] = ctrans("texts.{$client->classification}") ?? '';
}
return $entity; return $entity;
} }

View File

@ -89,7 +89,7 @@ class ContactExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -44,7 +44,7 @@ class CreditExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -50,7 +50,7 @@ class DocumentExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -44,7 +44,7 @@ class ExpenseExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -73,7 +73,7 @@ class InvoiceExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -81,7 +81,7 @@ class InvoiceItemExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();

View File

@ -66,7 +66,7 @@ class PaymentExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use ($headerdisplay) { $header = collect($this->input['report_keys'])->map(function ($key, $value) use ($headerdisplay) {
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -42,7 +42,7 @@ class ProductExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -114,7 +114,7 @@ class PurchaseOrderExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -73,7 +73,7 @@ class PurchaseOrderItemExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$query->cursor() $query->cursor()

View File

@ -75,7 +75,7 @@ class QuoteExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use ($headerdisplay) { $header = collect($this->input['report_keys'])->map(function ($key, $value) use ($headerdisplay) {
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -75,7 +75,7 @@ class QuoteItemExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$query->cursor() $query->cursor()

View File

@ -87,7 +87,7 @@ class RecurringInvoiceExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()

View File

@ -100,7 +100,7 @@ class TaskExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$query->cursor() $query->cursor()
@ -115,7 +115,7 @@ class TaskExport extends BaseExport
$this->storage_array = []; $this->storage_array = [];
}); });
nlog($this->storage_item_array); // nlog($this->storage_item_array);
return array_merge(['columns' => $header], $this->storage_item_array); return array_merge(['columns' => $header], $this->storage_item_array);
} }

View File

@ -74,7 +74,7 @@ class VendorExport extends BaseExport
$headerdisplay = $this->buildHeader(); $headerdisplay = $this->buildHeader();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
$report = $query->cursor() $report = $query->cursor()
@ -139,7 +139,11 @@ class VendorExport extends BaseExport
$entity['currency'] = $vendor->currency() ? $vendor->currency()->code : $vendor->company->currency()->code; $entity['currency'] = $vendor->currency() ? $vendor->currency()->code : $vendor->company->currency()->code;
} }
$entity['status'] = $this->calculateStatus($vendor); if (in_array('vendor.classification', $this->input['report_keys']) && isset($vendor->classification)) {
$entity['vendor.classification'] = ctrans("texts.{$vendor->classification}") ?? '';
}
// $entity['status'] = $this->calculateStatus($vendor);
return $entity; return $entity;
} }

View File

@ -34,7 +34,7 @@ class ProductFactory
$product->custom_value2 = ''; $product->custom_value2 = '';
$product->custom_value3 = ''; $product->custom_value3 = '';
$product->custom_value4 = ''; $product->custom_value4 = '';
$product->is_deleted = 0; $product->is_deleted = false;
$product->tax_id = 1; $product->tax_id = 1;
return $product; return $product;

View File

@ -20,7 +20,6 @@ class ProjectFactory
$project = new Project; $project = new Project;
$project->company_id = $company_id; $project->company_id = $company_id;
$project->user_id = $user_id; $project->user_id = $user_id;
$project->public_notes = ''; $project->public_notes = '';
$project->private_notes = ''; $project->private_notes = '';
$project->budgeted_hours = 0; $project->budgeted_hours = 0;
@ -30,7 +29,7 @@ class ProjectFactory
$project->custom_value2 = ''; $project->custom_value2 = '';
$project->custom_value3 = ''; $project->custom_value3 = '';
$project->custom_value4 = ''; $project->custom_value4 = '';
$project->is_deleted = 0; $project->is_deleted = false;
return $project; return $project;
} }

View File

@ -28,7 +28,7 @@ class VendorFactory
$vendor->country_id = 4; $vendor->country_id = 4;
$vendor->is_deleted = 0; $vendor->is_deleted = 0;
$vendor->vendor_hash = Str::random(40); $vendor->vendor_hash = Str::random(40);
$vendor->classification = ''; // $vendor->classification = '';
return $vendor; return $vendor;
} }

View File

@ -25,7 +25,8 @@ class GroupSettingFilters extends QueryFilters
* @return Builder * @return Builder
*/ */
public function name(string $name = ''): Builder public function name(string $name = ''): Builder
{nlog("filter"); {
if (strlen($name) == 0) { if (strlen($name) == 0) {
return $this->builder; return $this->builder;
} }

View File

@ -114,7 +114,7 @@ class PaymentFilters extends QueryFilters
} }
if(in_array('partially_unapplied', $status_parameters)) { if(in_array('partially_unapplied', $status_parameters)) {
$query->where('amount', '>', 'applied')->where('refunded', 0); $query->whereColumn('amount', '>', 'applied')->where('refunded', 0);
} }
}); });

View File

@ -112,6 +112,12 @@ class QuoteFilters extends QueryFilters
->orderBy('due_date', 'DESC'); ->orderBy('due_date', 'DESC');
}); });
} }
if(in_array('converted', $status_parameters)) {
$query->orWhere(function ($q) {
$q->whereNotNull('invoice_id');
});
}
}); });
return $this->builder; return $this->builder;

View File

@ -41,22 +41,29 @@ class EpcQrGenerator
public function getQrCode() public function getQrCode()
{ {
$renderer = new ImageRenderer( $qr = '';
new RendererStyle(200),
new SvgImageBackEnd()
);
$writer = new Writer($renderer);
$this->validateFields();
try { try {
$renderer = new ImageRenderer(
new RendererStyle(200),
new SvgImageBackEnd()
);
$writer = new Writer($renderer);
$this->validateFields();
$qr = $writer->writeString($this->encodeMessage(), 'utf-8'); $qr = $writer->writeString($this->encodeMessage(), 'utf-8');
return "<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";
} catch(\Throwable $e) { } catch(\Throwable $e) {
return ''; return '';
} catch(\Exception $e) {
return '';
} }
return "<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";
} }
public function encodeMessage() public function encodeMessage()

View File

@ -144,7 +144,7 @@ class InvoiceItemSum
public function process(): self public function process(): self
{ {
if (!$this->invoice->line_items || !is_array($this->invoice->line_items)) { if (!$this->invoice->line_items || !is_iterable($this->invoice->line_items)) {
$this->items = []; $this->items = [];
return $this; return $this;
} }

View File

@ -131,8 +131,7 @@ class InvoiceItemSumInclusive
public function process() public function process()
{ {
if (! $this->invoice->line_items || ! is_array($this->invoice->line_items) || count($this->invoice->line_items) == 0) { if (!$this->invoice->line_items || ! is_iterable($this->invoice->line_items) || count($this->invoice->line_items) == 0) {
return $this; return $this;
} }

View File

@ -80,6 +80,7 @@ class ContactForgotPasswordController extends Controller
'passwordEmailRoute' => 'client.password.email', 'passwordEmailRoute' => 'client.password.email',
'account' => $account, 'account' => $account,
'company' => $company, 'company' => $company,
'is_react' => false,
]); ]);
} }

View File

@ -90,6 +90,8 @@ class ForgotPasswordController extends Controller
$account = Account::find($account_id); $account = Account::find($account_id);
} }
return $this->render('auth.passwords.request', ['root' => 'themes', 'account' => $account]); $is_react = request()->has('react') ? true : false;
return $this->render('auth.passwords.request', ['root' => 'themes', 'account' => $account, 'is_react' => $is_react]);
} }
} }

View File

@ -12,37 +12,37 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use Google_Client;
use App\Models\User;
use App\Utils\Ninja;
use App\Models\Account;
use App\Libraries\MultiDB;
use App\Utils\TruthSource;
use Microsoft\Graph\Model;
use App\Models\CompanyUser;
use App\Models\CompanyToken;
use Illuminate\Http\Request;
use App\Libraries\OAuth\OAuth;
use App\Events\User\UserLoggedIn;
use Illuminate\Http\JsonResponse;
use PragmaRX\Google2FA\Google2FA;
use App\Jobs\Account\CreateAccount;
use Illuminate\Support\Facades\Auth;
use App\Utils\Traits\User\LoginCache;
use Illuminate\Support\Facades\Cache;
use Turbo124\Beacon\Facades\LightLogs;
use App\Http\Controllers\BaseController;
use App\Jobs\Company\CreateCompanyToken;
use Illuminate\Support\Facades\Response;
use Laravel\Socialite\Facades\Socialite;
use App\Http\Requests\Login\LoginRequest;
use App\Libraries\OAuth\Providers\Google;
use Illuminate\Database\Eloquent\Builder;
use App\DataMapper\Analytics\LoginFailure; use App\DataMapper\Analytics\LoginFailure;
use App\DataMapper\Analytics\LoginSuccess; use App\DataMapper\Analytics\LoginSuccess;
use App\Utils\Traits\UserSessionAttributes; use App\Events\User\UserLoggedIn;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Login\LoginRequest;
use App\Jobs\Account\CreateAccount;
use App\Jobs\Company\CreateCompanyToken;
use App\Libraries\MultiDB;
use App\Libraries\OAuth\OAuth;
use App\Libraries\OAuth\Providers\Google;
use App\Models\Account;
use App\Models\CompanyToken;
use App\Models\CompanyUser;
use App\Models\User;
use App\Transformers\CompanyUserTransformer; use App\Transformers\CompanyUserTransformer;
use App\Utils\Ninja;
use App\Utils\Traits\User\LoginCache;
use App\Utils\Traits\UserSessionAttributes;
use App\Utils\TruthSource;
use Google_Client;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Response;
use Laravel\Socialite\Facades\Socialite;
use Microsoft\Graph\Model;
use PragmaRX\Google2FA\Google2FA;
use Turbo124\Beacon\Facades\LightLogs;
class LoginController extends BaseController class LoginController extends BaseController
{ {
@ -418,7 +418,7 @@ class LoginController extends BaseController
->setReturnType(Model\User::class) ->setReturnType(Model\User::class)
->execute(); ->execute();
nlog($user); nlog($user);
if ($user) { if ($user) {
$account = request()->input('account'); $account = request()->input('account');
@ -641,8 +641,9 @@ class LoginController extends BaseController
$parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url') . "/auth/microsoft"]; $parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url') . "/auth/microsoft"];
} }
if(request()->hasHeader('X-REACT') || request()->query('react')) if(request()->hasHeader('X-REACT') || request()->query('react')) {
Cache::put("react_redir:".auth()->user()?->account->key, 'true', 300); Cache::put("react_redir:".auth()->user()?->account->key, 'true', 300);
}
if (request()->has('code')) { if (request()->has('code')) {
return $this->handleProviderCallback($provider); return $this->handleProviderCallback($provider);
@ -699,7 +700,7 @@ class LoginController extends BaseController
$request_from_react = Cache::pull("react_redir:".auth()->user()?->account?->key); $request_from_react = Cache::pull("react_redir:".auth()->user()?->account?->key);
// if($request_from_react) // if($request_from_react)
$redirect_url = config('ninja.react_url')."/#/settings/user_details/connect"; $redirect_url = config('ninja.react_url')."/#/settings/user_details/connect";
return redirect($redirect_url); return redirect($redirect_url);
} }

View File

@ -70,6 +70,7 @@ class ResetPasswordController extends Controller
$account = Account::first(); $account = Account::first();
} }
return $this->render('auth.passwords.reset', ['root' => 'themes', 'token' => $token, 'account' => $account, 'email' => $request->email]); return $this->render('auth.passwords.reset', ['root' => 'themes', 'token' => $token, 'account' => $account, 'email' => $request->email]);
} }
@ -110,6 +111,9 @@ class ResetPasswordController extends Controller
{ {
auth()->logout(); auth()->logout();
if(request()->has('react') || request()->hasHeader('X-React'))
return redirect(config('ninja.react_url').'/#/login');
return redirect('/'); return redirect('/');
} }
@ -126,10 +130,10 @@ class ResetPasswordController extends Controller
return new JsonResponse(['message' => trans($response)], 200); return new JsonResponse(['message' => trans($response)], 200);
} }
if(Ninja::isHosted() && $request->hasHeader('X-React')){ if($request->hasHeader('X-REACT') || $request->has('react')){
return redirect('https://app.invoicing.co/#/login'); return redirect(config('ninja.react_url').'/#/login');
} }
elseif($request->hasHeader('X-React')) else
return redirect('/#/login'); return redirect('/#/login');
return redirect($this->redirectPath()) return redirect($this->redirectPath())

View File

@ -123,11 +123,14 @@ class ClientController extends BaseController
return $request->disallowUpdate(); return $request->disallowUpdate();
} }
/** @var \App\Models\User $user */
$user = auth()->user();
$client = $this->client_repo->save($request->all(), $client); $client = $this->client_repo->save($request->all(), $client);
$this->uploadLogo($request->file('company_logo'), $client->company, $client); $this->uploadLogo($request->file('company_logo'), $client->company, $client);
event(new ClientWasUpdated($client, $client->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new ClientWasUpdated($client, $client->company, Ninja::eventVars($user ? $user->id : null)));
return $this->itemResponse($client->fresh()); return $this->itemResponse($client->fresh());
} }
@ -141,7 +144,10 @@ class ClientController extends BaseController
*/ */
public function create(CreateClientRequest $request) public function create(CreateClientRequest $request)
{ {
$client = ClientFactory::create(auth()->user()->company()->id, auth()->user()->id); /** @var \App\Models\User $user */
$user = auth()->user();
$client = ClientFactory::create($user->company()->id, $user->id);
return $this->itemResponse($client); return $this->itemResponse($client);
} }
@ -155,7 +161,10 @@ class ClientController extends BaseController
*/ */
public function store(StoreClientRequest $request) public function store(StoreClientRequest $request)
{ {
$client = $this->client_repo->save($request->all(), ClientFactory::create(auth()->user()->company()->id, auth()->user()->id)); /** @var \App\Models\User $user */
$user = auth()->user();
$client = $this->client_repo->save($request->all(), ClientFactory::create($user->company()->id, $user->id));
$client->load('contacts', 'primary_contact'); $client->load('contacts', 'primary_contact');
@ -166,7 +175,7 @@ class ClientController extends BaseController
$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() ? $user->id : null)));
return $this->itemResponse($client); return $this->itemResponse($client);
} }
@ -273,9 +282,12 @@ class ClientController extends BaseController
public function merge(PurgeClientRequest $request, Client $client, string $mergeable_client) public function merge(PurgeClientRequest $request, Client $client, string $mergeable_client)
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$m_client = Client::withTrashed() $m_client = Client::withTrashed()
->where('id', $this->decodePrimaryKey($mergeable_client)) ->where('id', $this->decodePrimaryKey($mergeable_client))
->where('company_id', auth()->user()->company()->id) ->where('company_id', $user->company()->id)
->first(); ->first();
if (!$m_client) { if (!$m_client) {

View File

@ -93,7 +93,7 @@ class ClientGatewayTokenController extends BaseController
*/ */
public function index(ListClientGatewayTokenRequest $request) public function index(ListClientGatewayTokenRequest $request)
{ {
$client_gateway_token_gateway_tokens = ClientGatewayToken::scope(); $client_gateway_token_gateway_tokens = ClientGatewayToken::query()->company();
return $this->listResponse($client_gateway_token_gateway_tokens); return $this->listResponse($client_gateway_token_gateway_tokens);
} }

View File

@ -87,6 +87,12 @@ class InvoiceController extends Controller
public function showBlob($hash) public function showBlob($hash)
{ {
$data = Cache::get($hash); $data = Cache::get($hash);
if(!$data){
usleep(200000);
$data = Cache::get($hash);
}
$invitation = false; $invitation = false;
match($data['entity_type'] ?? false){ match($data['entity_type'] ?? false){
@ -94,6 +100,7 @@ class InvoiceController extends Controller
'quote' => $invitation = QuoteInvitation::withTrashed()->find($data['invitation_id']), 'quote' => $invitation = QuoteInvitation::withTrashed()->find($data['invitation_id']),
'credit' => $invitation = CreditInvitation::withTrashed()->find($data['invitation_id']), 'credit' => $invitation = CreditInvitation::withTrashed()->find($data['invitation_id']),
'recurring_invoice' => $invitation = RecurringInvoiceInvitation::withTrashed()->find($data['invitation_id']), 'recurring_invoice' => $invitation = RecurringInvoiceInvitation::withTrashed()->find($data['invitation_id']),
false => $invitation = false,
}; };
if (! $invitation) { if (! $invitation) {
@ -188,7 +195,7 @@ class InvoiceController extends Controller
//format data //format data
$invoices->map(function ($invoice) { $invoices->map(function ($invoice) {
$invoice->service()->removeUnpaidGatewayFees(); // $invoice->service()->removeUnpaidGatewayFees();
$invoice->balance = $invoice->balance > 0 ? Number::formatValue($invoice->balance, $invoice->client->currency()) : 0; $invoice->balance = $invoice->balance > 0 ? Number::formatValue($invoice->balance, $invoice->client->currency()) : 0;
$invoice->partial = $invoice->partial > 0 ? Number::formatValue($invoice->partial, $invoice->client->currency()) : 0; $invoice->partial = $invoice->partial > 0 ? Number::formatValue($invoice->partial, $invoice->client->currency()) : 0;

View File

@ -32,7 +32,7 @@ class EmailHistoryController extends BaseController
->orderBy('id', 'DESC') ->orderBy('id', 'DESC')
->cursor() ->cursor()
->map(function ($system_log) { ->map(function ($system_log) {
if($system_log->log['history'] ?? false) { if(($system_log->log['history'] && $system_log->log['history']['events'] && count($system_log->log['history']['events']) >=1) ?? false) {
return $system_log->log['history']; return $system_log->log['history'];
} }
}); });
@ -57,7 +57,7 @@ class EmailHistoryController extends BaseController
->orderBy('id', 'DESC') ->orderBy('id', 'DESC')
->cursor() ->cursor()
->map(function ($system_log) { ->map(function ($system_log) {
if($system_log->log['history'] ?? false) { if(($system_log->log['history'] && $system_log->log['history']['events'] && count($system_log->log['history']['events']) >=1) ?? false) {
return $system_log->log['history']; return $system_log->log['history'];
} }
}); });

View File

@ -74,7 +74,7 @@ class OneTimeTokenController extends BaseController
'user_id' => $user->id, 'user_id' => $user->id,
'company_key'=> $user->company()->company_key, 'company_key'=> $user->company()->company_key,
'context' => $request->input('context'), 'context' => $request->input('context'),
'is_react' => $request->request()->hasHeader('X-REACT') ? true : false, 'is_react' => $request->hasHeader('X-REACT') ? true : false,
]; ];
Cache::put($hash, $data, 3600); Cache::put($hash, $data, 3600);

View File

@ -290,10 +290,14 @@ class PreviewController extends BaseController
return $maker->getCompiledHTML(); return $maker->getCompiledHTML();
} }
} catch(\Exception $e) { } catch(\Exception $e) {
// nlog($e->getMessage());
DB::connection(config('database.default'))->rollBack(); DB::connection(config('database.default'))->rollBack();
return; if (DB::connection(config('database.default'))->transactionLevel() > 0) {
DB::connection(config('database.default'))->rollBack();
}
return response()->json(['message' => 'Error generating preview. Please retry again shortly.'], 400);
} }
//if phantom js...... inject here.. //if phantom js...... inject here..

View File

@ -458,14 +458,15 @@ class ProductController extends BaseController
*/ */
public function bulk(BulkProductRequest $request) public function bulk(BulkProductRequest $request)
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$action = $request->input('action'); $action = $request->input('action');
$ids = $request->input('ids'); $ids = $request->input('ids');
$products = Product::withTrashed()->whereIn('id', $ids); $products = Product::withTrashed()->whereIn('id', $ids);
nlog($products->count());
if($action == 'set_tax_id'){ if($action == 'set_tax_id'){
$tax_id = $request->input('tax_id'); $tax_id = $request->input('tax_id');
@ -475,8 +476,8 @@ class ProductController extends BaseController
return $this->listResponse(Product::withTrashed()->whereIn('id', $ids)); return $this->listResponse(Product::withTrashed()->whereIn('id', $ids));
} }
$products->cursor()->each(function ($product, $key) use ($action) { $products->cursor()->each(function ($product, $key) use ($action, $user) {
if (auth()->user()->can('edit', $product)) { if ($user->can('edit', $product)) {
$this->product_repo->{$action}($product); $this->product_repo->{$action}($product);
} }
}); });

View File

@ -99,7 +99,7 @@ class SearchController extends Controller
'name' => $invoice->client->present()->name() . ' - ' . $invoice->number, 'name' => $invoice->client->present()->name() . ' - ' . $invoice->number,
'type' => '/invoice', 'type' => '/invoice',
'id' => $invoice->hashed_id, 'id' => $invoice->hashed_id,
'path' => "/clients/{$invoice->hashed_id}/edit" 'path' => "/invoices/{$invoice->hashed_id}/edit"
]; ];
} }

View File

@ -182,7 +182,8 @@ class SetupController extends Controller
* @return Application|ResponseFactory|JsonResponse|Response * @return Application|ResponseFactory|JsonResponse|Response
*/ */
public function checkDB(CheckDatabaseRequest $request) public function checkDB(CheckDatabaseRequest $request)
{nlog("trying"); {
try { try {
$status = SystemHealth::dbCheck($request); $status = SystemHealth::dbCheck($request);

View File

@ -56,7 +56,7 @@ class StaticController extends BaseController
/** @var \App\Models\User $user */ /** @var \App\Models\User $user */
$user = auth()->user(); $user = auth()->user();
$response = Statics::company($user->company()->getLocale()); $response = Statics::company($user->getLocale() ?? $user->company()->getLocale());
return response()->json($response, 200, ['Content-type'=> 'application/json; charset=utf-8'], JSON_PRETTY_PRINT); return response()->json($response, 200, ['Content-type'=> 'application/json; charset=utf-8'], JSON_PRETTY_PRINT);
} }

View File

@ -73,7 +73,10 @@ class TaskStatusController extends BaseController
*/ */
public function create(CreateTaskStatusRequest $request) public function create(CreateTaskStatusRequest $request)
{ {
$task_status = TaskStatusFactory::create(auth()->user()->company()->id, auth()->user()->id); /** @var \App\Models\User $user */
$user = auth()->user();
$task_status = TaskStatusFactory::create($user->company()->id, auth()->user()->id);
return $this->itemResponse($task_status); return $this->itemResponse($task_status);
} }
@ -87,8 +90,10 @@ class TaskStatusController extends BaseController
*/ */
public function store(StoreTaskStatusRequest $request) public function store(StoreTaskStatusRequest $request)
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$task_status = TaskStatusFactory::create(auth()->user()->company()->id, auth()->user()->id); $task_status = TaskStatusFactory::create($user->company()->id, auth()->user()->id);
$task_status->fill($request->all()); $task_status->fill($request->all());
$task_status->save(); $task_status->save();

View File

@ -125,7 +125,10 @@ class TaxRateController extends BaseController
*/ */
public function create(CreateTaxRateRequest $request) public function create(CreateTaxRateRequest $request)
{ {
$tax_rate = TaxRateFactory::create(auth()->user()->company()->id, auth()->user()->id); /** @var \App\Models\User $user */
$user = auth()->user();
$tax_rate = TaxRateFactory::create($user->company()->id, auth()->user()->id);
return $this->itemResponse($tax_rate); return $this->itemResponse($tax_rate);
} }
@ -138,7 +141,10 @@ class TaxRateController extends BaseController
*/ */
public function store(StoreTaxRateRequest $request) public function store(StoreTaxRateRequest $request)
{ {
$tax_rate = TaxRateFactory::create(auth()->user()->company()->id, auth()->user()->id); /** @var \App\Models\User $user */
$user = auth()->user();
$tax_rate = TaxRateFactory::create($user->company()->id, $user->id);
$tax_rate->fill($request->all()); $tax_rate->fill($request->all());
$tax_rate->save(); $tax_rate->save();
@ -417,15 +423,33 @@ class TaxRateController extends BaseController
*/ */
public function bulk() public function bulk()
{ {
$action = request()->input('action'); /** @var \App\Models\User $user */
$user = auth()->user();
$action = request()->input('action');
$ids = request()->input('ids'); $ids = request()->input('ids');
$tax_rates = TaxRate::withTrashed()->find($this->transformKeys($ids)); $tax_rates = TaxRate::withTrashed()->find($this->transformKeys($ids));
$tax_rates->each(function ($tax_rate, $key) use ($action) { $tax_rates->each(function ($tax_rate, $key) use ($action, $user) {
if (auth()->user()->can('edit', $tax_rate)) { if ($user->can('edit', $tax_rate)) {
if(in_array($action, ['archive','delete'])) {
$settings = $user->company()->settings;
foreach(['tax_name1','tax_name2','tax_name3'] as $tax_name) {
if($settings->{$tax_name} == $tax_rate->name) {
$settings->{$tax_name} = '';
$settings->{str_replace("name", "rate", $tax_name)} = '';
}
}
$user->company()->saveSettings($settings, $user->company());
}
$this->base_repo->{$action}($tax_rate); $this->base_repo->{$action}($tax_rate);
} }
}); });

View File

@ -42,7 +42,6 @@ trait VerifiesUserEmail
} }
$user->email_verified_at = now(); $user->email_verified_at = now();
// $user->confirmation_code = null; //this prevented the form from showing validation errors.
$user->save(); $user->save();
if (isset($user->oauth_user_id)) { if (isset($user->oauth_user_id)) {
@ -69,7 +68,6 @@ trait VerifiesUserEmail
$user = User::where('id', $this->decodePrimaryKey(request()->user_id))->firstOrFail(); $user = User::where('id', $this->decodePrimaryKey(request()->user_id))->firstOrFail();
$validator = Validator::make(request()->all(), [ $validator = Validator::make(request()->all(), [
//'password' => ['required', 'min:6'],
'password' => 'min:6|required_with:password_confirmation|same:password_confirmation', 'password' => 'min:6|required_with:password_confirmation|same:password_confirmation',
'password_confirmation' => 'min:6' 'password_confirmation' => 'min:6'
]); ]);

View File

@ -109,9 +109,11 @@ class UserController extends BaseController
$user_agent = request()->input('token_name') ?: request()->server('HTTP_USER_AGENT'); $user_agent = request()->input('token_name') ?: request()->server('HTTP_USER_AGENT');
$is_react = $request->hasHeader('X-React') ?? false;
$ct = (new CreateCompanyToken($company, $user, $user_agent))->handle(); $ct = (new CreateCompanyToken($company, $user, $user_agent))->handle();
event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $is_react));
$user->setCompany($company); $user->setCompany($company);
$user->company_id = $company->id; $user->company_id = $company->id;
@ -176,7 +178,7 @@ class UserController extends BaseController
$user->oauth_user_token = null; $user->oauth_user_token = null;
$user->save(); $user->save();
UserEmailChanged::dispatch($new_user, json_decode($old_user), $logged_in_user->company()); UserEmailChanged::dispatch($new_user, json_decode($old_user), $logged_in_user->company(), $request->hasHeader('X-React'));
} }
event(new UserWasUpdated($user, $logged_in_user, $logged_in_user->company(), Ninja::eventVars($logged_in_user->id))); event(new UserWasUpdated($user, $logged_in_user, $logged_in_user->company(), Ninja::eventVars($logged_in_user->id)));
@ -292,7 +294,7 @@ class UserController extends BaseController
/** @var \App\Models\User $logged_in_user */ /** @var \App\Models\User $logged_in_user */
$logged_in_user = auth()->user(); $logged_in_user = auth()->user();
$user->service()->invite($logged_in_user->company()); $user->service()->invite($logged_in_user->company(), $request->hasHeader('X-REACT'));
return response()->json(['message' => ctrans('texts.confirmation_resent')], 200); return response()->json(['message' => ctrans('texts.confirmation_resent')], 200);
} }
@ -310,7 +312,7 @@ class UserController extends BaseController
/** @var \App\Models\User $logged_in_user */ /** @var \App\Models\User $logged_in_user */
$logged_in_user = auth()->user(); $logged_in_user = auth()->user();
$user->service()->invite($logged_in_user->company()); $user->service()->invite($logged_in_user->company(), $request->hasHeader('X-REACT'));
return response()->json(['message' => ctrans('texts.confirmation_resent')], 200); return response()->json(['message' => ctrans('texts.confirmation_resent')], 200);
} }

View File

@ -425,8 +425,8 @@ class BillingPortalPurchase extends Component
return $this->subscription->service()->startTrial([ return $this->subscription->service()->startTrial([
'email' => $this->email ?? $this->contact->email, 'email' => $this->email ?? $this->contact->email,
'quantity' => $this->quantity, 'quantity' => $this->quantity,
'contact_id' => $this->contact->id, 'contact_id' => $this->contact->hashed_id,
'client_id' => $this->contact->client->id, 'client_id' => $this->contact->client->hashed_id,
]); ]);
} }

View File

@ -59,11 +59,16 @@ class PdfSlot extends Component
public function mount() public function mount()
{ {
MultiDB::setDb($this->db); MultiDB::setDb($this->db);
if(!$this->invitation) {
$this->entity->service()->createInvitations();
$this->invitation = $this->entity->invitations()->first();
}
} }
public function getPdf() public function getPdf()
{ {
// $this->pdf = $this->entity->fullscreenPdfViewer($this->invitation);
$blob = [ $blob = [
'entity_type' => $this->resolveEntityType(), 'entity_type' => $this->resolveEntityType(),
@ -74,7 +79,7 @@ class PdfSlot extends Component
$hash = Str::random(64); $hash = Str::random(64);
Cache::put($hash, $blob, now()->addMinutes(2)); Cache::put($hash, $blob, 1800);
$this->pdf = $hash; $this->pdf = $hash;

View File

@ -26,14 +26,21 @@ class StoreBankTransactionRequest extends Request
*/ */
public function authorize() : bool public function authorize() : bool
{ {
return auth()->user()->can('create', BankTransaction::class); /** @var \App\Models\User $user */
$user = auth()->user();
return $user->can('create', BankTransaction::class);
} }
public function rules() public function rules()
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = []; $rules = [];
$rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.$user->company()->id.',is_deleted,0';
return $rules; return $rules;
} }

View File

@ -93,7 +93,7 @@ class StoreClientRequest extends Request
$rules['number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)]; $rules['number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)];
$rules['id_number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)]; $rules['id_number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)];
$rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; $rules['classification'] = 'bail|sometimes|nullable|in:individual,business,partnership,trust,charity,government,other';
return $rules; return $rules;
} }

View File

@ -60,7 +60,7 @@ class UpdateClientRequest extends Request
$rules['size_id'] = 'integer|nullable'; $rules['size_id'] = 'integer|nullable';
$rules['country_id'] = 'integer|nullable'; $rules['country_id'] = 'integer|nullable';
$rules['shipping_country_id'] = 'integer|nullable'; $rules['shipping_country_id'] = 'integer|nullable';
$rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; $rules['classification'] = 'bail|sometimes|nullable|in:individual,business,partnership,trust,charity,government,other';
if ($this->id_number) { if ($this->id_number) {
$rules['id_number'] = Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id); $rules['id_number'] = Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id);

View File

@ -22,6 +22,9 @@ class ListClientGatewayTokenRequest extends Request
*/ */
public function authorize() : bool public function authorize() : bool
{ {
return auth()->user()->isAdmin(); /** @var \App\Models\User $user */
$user = auth()->user();
return $user->isAdmin();
} }
} }

View File

@ -33,11 +33,18 @@ class StorePaymentRequest extends Request
*/ */
public function authorize() : bool public function authorize() : bool
{ {
return auth()->user()->can('create', Payment::class); /** @var \App\Models\User $user */
$user = auth()->user();
return $user->can('create', Payment::class);
} }
public function prepareForValidation() public function prepareForValidation()
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$input = $this->all(); $input = $this->all();
$invoices_total = 0; $invoices_total = 0;
@ -86,7 +93,7 @@ class StorePaymentRequest extends Request
} }
if (! isset($input['date'])) { if (! isset($input['date'])) {
$input['date'] = now()->addSeconds(auth()->user()->company()->timezone()->utc_offset)->format('Y-m-d'); $input['date'] = now()->addSeconds($user->company()->timezone()->utc_offset)->format('Y-m-d');
} }
$this->replace($input); $this->replace($input);
@ -94,10 +101,13 @@ class StorePaymentRequest extends Request
public function rules() public function rules()
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$rules = [ $rules = [
'amount' => ['numeric', 'bail', new PaymentAmountsBalanceRule(), new ValidCreditsPresentRule($this->all())], 'amount' => ['numeric', 'bail', new PaymentAmountsBalanceRule(), new ValidCreditsPresentRule($this->all())],
// 'client_id' => 'bail|required|exists:clients,id', // 'client_id' => 'bail|required|exists:clients,id',
'client_id' => 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0', 'client_id' => 'bail|required|exists:clients,id,company_id,'.$user->company()->id.',is_deleted,0',
'invoices.*.invoice_id' => 'bail|required|distinct|exists:invoices,id', 'invoices.*.invoice_id' => 'bail|required|distinct|exists:invoices,id',
'invoices.*.amount' => 'bail|required', 'invoices.*.amount' => 'bail|required',
'invoices.*.invoice_id' => new ValidInvoicesRules($this->all()), 'invoices.*.invoice_id' => new ValidInvoicesRules($this->all()),
@ -105,8 +115,8 @@ class StorePaymentRequest extends Request
'credits.*.credit_id' => new ValidCreditsRules($this->all()), 'credits.*.credit_id' => new ValidCreditsRules($this->all()),
'credits.*.amount' => ['bail','required', new CreditsSumRule($this->all())], 'credits.*.amount' => ['bail','required', new CreditsSumRule($this->all())],
'invoices' => new ValidPayableInvoicesRule(), 'invoices' => new ValidPayableInvoicesRule(),
'number' => ['nullable', 'bail', Rule::unique('payments')->where('company_id', auth()->user()->company()->id)], 'number' => ['nullable', 'bail', Rule::unique('payments')->where('company_id', $user->company()->id)],
'idempotency_key' => ['nullable', 'bail', 'string','max:64', Rule::unique('payments')->where('company_id', auth()->user()->company()->id)], 'idempotency_key' => ['nullable', 'bail', 'string','max:64', Rule::unique('payments')->where('company_id', $user->company()->id)],
]; ];

View File

@ -30,7 +30,10 @@ class StoreUserRequest extends Request
*/ */
public function authorize() : bool public function authorize() : bool
{ {
return auth()->user()->isAdmin(); /** @var \App\Models\User $user */
$user = auth()->user();
return $user->isAdmin();
} }
public function rules() public function rules()

View File

@ -44,18 +44,20 @@ class ValidProjectForClient implements Rule
return true; return true;
} }
// if (is_string($this->input['project_id'])) {
// $this->input['project_id'] = $this->decodePrimaryKey($this->input['project_id']);
// }
$project = Project::withTrashed()->find($this->input['project_id']); $project = Project::withTrashed()->find($this->input['project_id']);
if (! $project) { if (! $project) {
$this->message = 'Project not found'; $this->message = 'Project not found';
return; return;
} }
if(!isset($this->input['client_id'])){
$this->message = 'No Client ID provided.';
return false;
}
return $project->client_id == $this->input['client_id']; return $project->client_id == $this->input['client_id'];
} }

View File

@ -47,6 +47,9 @@ class BaseTransformer
public function parseDate($date) public function parseDate($date)
{ {
if(stripos($date,"/") !== false && $this->company->settings->country_id != 840)
$date = str_replace('/', '-', $date);
try { try {
$parsed_date = Carbon::parse($date); $parsed_date = Carbon::parse($date);

View File

@ -80,8 +80,8 @@ class UpdateCalculatedFields
$project->tasks->each(function ($task) use (&$duration) { $project->tasks->each(function ($task) use (&$duration) {
if(is_iterable($task->time_log)) {
foreach(json_decode($task->time_log) as $log){ foreach(json_decode($task->time_log) as $log) {
$start_time = $log[0]; $start_time = $log[0];
$end_time = $log[1] == 0 ? time() : $log[1]; $end_time = $log[1] == 0 ? time() : $log[1];
@ -89,6 +89,7 @@ class UpdateCalculatedFields
$duration += $end_time - $start_time; $duration += $end_time - $start_time;
} }
}
}); });

View File

@ -11,6 +11,7 @@
namespace App\Jobs\Ninja; namespace App\Jobs\Ninja;
use App\Models\Payment;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use App\Models\ClientGatewayToken; use App\Models\ClientGatewayToken;
@ -74,6 +75,53 @@ class CheckACHStatus implements ShouldQueue
} }
}); });
Payment::where('status_id', 1)
->whereHas('company_gateway', function ($q){
$q->whereIn('gateway_key', ['d14dd26a47cecc30fdd65700bfb67b34', 'd14dd26a37cecc30fdd65700bfb55b23']);
})
->cursor()
->each(function ($p) {
try {
$stripe = $p->company_gateway->driver($p->client)->init();
} catch(\Exception $e) {
return;
}
$pi = false;
try {
$pi = $stripe->getPaymentIntent($p->transaction_reference);
} catch(\Exception $e) {
}
if(!$pi) {
try {
$pi = \Stripe\Charge::retrieve($p->transaction_reference, $stripe->stripe_connect_auth);
} catch(\Exception $e) {
return;
}
}
if($pi && $pi->status == 'succeeded') {
$p->status_id = Payment::STATUS_COMPLETED;
$p->saveQuietly();
} else {
if($pi) {
nlog("{$p->id} did not complete {$p->transaction_reference}");
} else {
nlog("did not find a payment intent {$p->transaction_reference}");
}
}
});
} }
} }
} }

View File

@ -81,7 +81,7 @@ class CreateUser
]); ]);
if (! Ninja::isSelfHost()) { if (! Ninja::isSelfHost()) {
event(new UserWasCreated($user, $user, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new UserWasCreated($user, $user, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), request()->hasHeader('X-REACT') ?? false));
} }
return $user; return $user;

View File

@ -39,7 +39,7 @@ class UserEmailChanged implements ShouldQueue
* @param \stdClass $old_user * @param \stdClass $old_user
* @param \App\Models\Company $company * @param \App\Models\Company $company
*/ */
public function __construct(protected User $new_user, protected \stdClass $old_user, protected Company $company) public function __construct(protected User $new_user, protected \stdClass $old_user, protected Company $company, protected bool $is_react = false)
{ {
$this->settings = $this->company->settings; $this->settings = $this->company->settings;
} }
@ -72,7 +72,7 @@ class UserEmailChanged implements ShouldQueue
NinjaMailerJob::dispatch($nmo, true); NinjaMailerJob::dispatch($nmo, true);
$this->new_user->service()->invite($this->company); $this->new_user->service()->invite($this->company, $this->is_react);
} }
private function getData() private function getData()

View File

@ -47,7 +47,7 @@ class SendVerificationNotification implements ShouldQueue
{ {
MultiDB::setDB($event->company->db); MultiDB::setDB($event->company->db);
$event->user->service()->invite($event->company); $event->user->service()->invite($event->company, $event->is_react);
if (Carbon::parse($event->company->created_at)->lt(now()->subDay())) { if (Carbon::parse($event->company->created_at)->lt(now()->subDay())) {
App::forgetInstance('translator'); App::forgetInstance('translator');

View File

@ -75,6 +75,7 @@ class ClientPaymentFailureObject
$mail_obj->data = $this->getData(); $mail_obj->data = $this->getData();
$mail_obj->markdown = 'email.client.generic'; $mail_obj->markdown = 'email.client.generic';
$mail_obj->tag = $this->company->company_key; $mail_obj->tag = $this->company->company_key;
$mail_obj->text_view = 'email.template.text';
return $mail_obj; return $mail_obj;
} }
@ -122,10 +123,13 @@ class ClientPaymentFailureObject
'button' => ctrans('texts.pay_now'), 'button' => ctrans('texts.pay_now'),
'additional_info' => false, 'additional_info' => false,
'company' => $this->company, 'company' => $this->company,
'text_body' => ctrans('texts.client_payment_failure_body', ['invoice' => implode(',', $this->invoices->pluck('number')->toArray()), 'amount' => $this->getAmount()]),
'additional_info' => $this->error ?? '',
]; ];
if (strlen($this->error > 1)) { if (strlen($this->error > 1)) {
$data['content'] .= "\n\n".$this->error; // $data['content'] .= "\n\n{$this->error}";
$data['text_body'] .= "\n\n".$this->error;
} }
return $data; return $data;

View File

@ -98,7 +98,7 @@ class EntitySentObject
$mail_obj->markdown = 'email.admin.generic'; $mail_obj->markdown = 'email.admin.generic';
$mail_obj->tag = $this->company->company_key; $mail_obj->tag = $this->company->company_key;
} }
nlog($mail_obj); // nlog($mail_obj);
return $mail_obj; return $mail_obj;
} }

View File

@ -11,22 +11,16 @@
namespace App\Mail\Admin; namespace App\Mail\Admin;
use App\Models\Company;
use App\Models\User;
use App\Utils\Ninja; use App\Utils\Ninja;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
class ResetPasswordObject class ResetPasswordObject
{ {
public $user;
public $token; public function __construct(private string $token, protected User $user, protected Company $company, protected bool $is_react)
public $company;
public function __construct($token, $user, $company)
{ {
$this->token = $token;
$this->user = $user;
$this->company = $company;
} }
public function build() public function build()
@ -42,7 +36,7 @@ class ResetPasswordObject
$data = [ $data = [
'title' => ctrans('texts.your_password_reset_link'), 'title' => ctrans('texts.your_password_reset_link'),
'message' => ctrans('texts.reset_password'), 'message' => ctrans('texts.reset_password'),
'url' => route('password.reset', ['token' => $this->token, 'email' => $this->user->email]), 'url' => route('password.reset', ['token' => $this->token, 'email' => $this->user->email, 'react' => $this->is_react ? 'true' : 'false']),
'button' => ctrans('texts.reset'), 'button' => ctrans('texts.reset'),
'signature' => $this->company->settings->email_signature, 'signature' => $this->company->settings->email_signature,
'settings' => $this->company->settings, 'settings' => $this->company->settings,

View File

@ -11,6 +11,8 @@
namespace App\Mail\Admin; namespace App\Mail\Admin;
use App\Models\Company;
use App\Models\User;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
@ -19,14 +21,8 @@ class VerifyUserObject
{ {
use MakesHash; use MakesHash;
public $user; public function __construct(public User $user, public Company $company, private bool $is_react = false)
public $company;
public function __construct($user, $company)
{ {
$this->user = $user;
$this->company = $company;
} }
public function build() public function build()
@ -44,8 +40,9 @@ class VerifyUserObject
$react_redirect = ''; $react_redirect = '';
if(Ninja::isHosted()) { if($this->is_react) {
$react_redirect = '?react=true'; $react_redirect = '?react=true';
nlog("is react");
} }
$data = [ $data = [

View File

@ -107,16 +107,15 @@ class PaymentEmailEngine extends BaseEmailEngine
}); });
} }
if($this->client->getSetting('enable_e_invoice')) // if($this->client->getSetting('enable_e_invoice'))
{ // {
$e_invoice_filepath = $invoice->service()->getEInvoice($this->contact); // $e_invoice_filepath = $invoice->service()->getEInvoice($this->contact);
if(Storage::disk(config('filesystems.default'))->exists($e_invoice_filepath)) { // if($e_invoice_filepath && strlen($e_invoice_filepath) > 1)
$this->setAttachments([['path' => Storage::disk(config('filesystems.default'))->path($e_invoice_filepath), 'name' => $invoice->getFileName("xml"), 'mime' => null]]); // $this->setAttachments([['file' => base64_encode($e_invoice_filepath), 'name' => $invoice->getFileName("xml")]]);
}
} // }
}); });
} }

View File

@ -148,8 +148,6 @@ class TemplateEmail extends Mailable
if ($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
$ubl_string = (new CreateUbl($this->invitation->invoice))->handle(); $ubl_string = (new CreateUbl($this->invitation->invoice))->handle();
nlog("template {$ubl_string}");
if ($ubl_string) { if ($ubl_string) {
$this->attachData($ubl_string, $this->invitation->invoice->getFileName('xml')); $this->attachData($ubl_string, $this->invitation->invoice->getFileName('xml'));
} }
@ -158,8 +156,6 @@ class TemplateEmail extends Mailable
if ($this->invitation && $this->invitation->invoice && $this->invitation->invoice->client->getSetting('enable_e_invoice') && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->invitation && $this->invitation->invoice && $this->invitation->invoice->client->getSetting('enable_e_invoice') && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
$xml_string = $this->invitation->invoice->service()->getEInvoice($this->invitation->contact); $xml_string = $this->invitation->invoice->service()->getEInvoice($this->invitation->contact);
nlog("template {$xml_string}");
if($xml_string) { if($xml_string) {
$this->attachData($xml_string, $this->invitation->invoice->getEFileName("xml")); $this->attachData($xml_string, $this->invitation->invoice->getEFileName("xml"));
} }

View File

@ -11,8 +11,9 @@
namespace App\Models; namespace App\Models;
use App\Services\Bank\BankService; use App\Models\Expense;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Services\Bank\BankService;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
/** /**
@ -137,11 +138,6 @@ class BankTransaction extends BaseModel
return $this->belongsTo(Vendor::class)->withTrashed(); return $this->belongsTo(Vendor::class)->withTrashed();
} }
public function expense(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Expense::class)->withTrashed();
}
public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{ {
return $this->belongsTo(User::class)->withTrashed(); return $this->belongsTo(User::class)->withTrashed();
@ -162,8 +158,18 @@ class BankTransaction extends BaseModel
return $this->belongsTo(Payment::class)->withTrashed(); return $this->belongsTo(Payment::class)->withTrashed();
} }
// public function expense(): \Illuminate\Database\Eloquent\Relations\BelongsTo
// {
// return $this->belongsTo(Expense::class)->withTrashed();
// }
public function service() :BankService public function service() :BankService
{ {
return new BankService($this); return new BankService($this);
} }
public function getExpenses()
{
return Expense::whereIn('id', $this->getExpenseIds())->get();
}
} }

View File

@ -84,7 +84,12 @@ class ClientGatewayToken extends BaseModel
return $this->hasOne(GatewayType::class, 'id', 'gateway_type_id'); return $this->hasOne(GatewayType::class, 'id', 'gateway_type_id');
} }
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo /**
* Company
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function company()
{ {
return $this->belongsTo(Company::class); return $this->belongsTo(Company::class);
} }

View File

@ -20,10 +20,11 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int|null $user_id * @property int|null $user_id
* @property int|null $company_id * @property int|null $company_id
* @property string $name * @property string $name
* @property int $is_custom * @property bool $is_custom
* @property int $is_active * @property bool $is_active
* @property object|null $design * @property object|null $design
* @property int $is_deleted * @property bool $is_deleted
* @property bool $is_template
* @property int|null $created_at * @property int|null $created_at
* @property int|null $updated_at * @property int|null $updated_at
* @property int|null $deleted_at * @property int|null $deleted_at

View File

@ -33,22 +33,22 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int|null $payment_type_id * @property int|null $payment_type_id
* @property int|null $recurring_expense_id * @property int|null $recurring_expense_id
* @property bool $is_deleted * @property bool $is_deleted
* @property string $amount * @property float $amount
* @property string $foreign_amount * @property float $foreign_amount
* @property string $exchange_rate * @property string $exchange_rate
* @property string|null $tax_name1 * @property string|null $tax_name1
* @property string $tax_rate1 * @property float $tax_rate1
* @property string|null $tax_name2 * @property string|null $tax_name2
* @property string $tax_rate2 * @property float $tax_rate2
* @property string|null $tax_name3 * @property string|null $tax_name3
* @property string $tax_rate3 * @property float $tax_rate3
* @property string|null $date * @property string|null $date
* @property string|null $payment_date * @property string|null $payment_date
* @property string|null $private_notes * @property string|null $private_notes
* @property string|null $public_notes * @property string|null $public_notes
* @property string|null $transaction_reference * @property string|null $transaction_reference
* @property int $should_be_invoiced * @property bool $should_be_invoiced
* @property int $invoice_documents * @property bool $invoice_documents
* @property int|null $transaction_id * @property int|null $transaction_id
* @property string|null $custom_value1 * @property string|null $custom_value1
* @property string|null $custom_value2 * @property string|null $custom_value2
@ -56,9 +56,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $custom_value4 * @property string|null $custom_value4
* @property string|null $number * @property string|null $number
* @property int|null $project_id * @property int|null $project_id
* @property string $tax_amount1 * @property float $tax_amount1
* @property string $tax_amount2 * @property float $tax_amount2
* @property string $tax_amount3 * @property float $tax_amount3
* @property int $uses_inclusive_taxes * @property int $uses_inclusive_taxes
* @property int $calculate_tax_by_amount * @property int $calculate_tax_by_amount
* @property-read \App\Models\User|null $assigned_user * @property-read \App\Models\User|null $assigned_user
@ -220,7 +220,7 @@ class Expense extends BaseModel
public function transaction(): \Illuminate\Database\Eloquent\Relations\BelongsTo public function transaction(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{ {
return $this->belongsTo(BankTransaction::class); return $this->belongsTo(BankTransaction::class)->withTrashed();
} }
public function stringStatus() public function stringStatus()

View File

@ -54,7 +54,7 @@ use Illuminate\Database\Eloquent\Relations\HasManyThrough;
* @property string|null $last_sent_date * @property string|null $last_sent_date
* @property string|null $due_date * @property string|null $due_date
* @property bool $is_deleted * @property bool $is_deleted
* @property object|array $line_items * @property object|array|string $line_items
* @property object|null $backup * @property object|null $backup
* @property string|null $footer * @property string|null $footer
* @property string|null $public_notes * @property string|null $public_notes
@ -316,7 +316,15 @@ class Invoice extends BaseModel
*/ */
public function payments(): \Illuminate\Database\Eloquent\Relations\MorphToMany public function payments(): \Illuminate\Database\Eloquent\Relations\MorphToMany
{ {
return $this->morphToMany(Payment::class, 'paymentable')->withTrashed()->withPivot('amount', 'refunded')->withTimestamps(); return $this->morphToMany(Payment::class, 'paymentable')->withTrashed()->withPivot('amount', 'refunded', 'deleted_at')->withTimestamps();
}
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany<Payment>
*/
public function net_payments(): \Illuminate\Database\Eloquent\Relations\MorphToMany
{
return $this->morphToMany(Payment::class, 'paymentable')->withTrashed()->where('is_deleted',0)->withPivot('amount', 'refunded', 'deleted_at')->withTimestamps();
} }
/** /**

View File

@ -27,8 +27,8 @@ use Illuminate\Support\Facades\Storage;
* @property int $id * @property int $id
* @property int $company_id * @property int $company_id
* @property int $user_id * @property int $user_id
* @property int $client_contact_id * @property int|null $client_contact_id
* @property int $invoice_id * @property int|null $invoice_id
* @property string $key * @property string $key
* @property string|null $transaction_reference * @property string|null $transaction_reference
* @property string|null $message_id * @property string|null $message_id

View File

@ -220,7 +220,7 @@ class Payment extends BaseModel
*/ */
public function invoices(): \Illuminate\Database\Eloquent\Relations\MorphToMany public function invoices(): \Illuminate\Database\Eloquent\Relations\MorphToMany
{ {
return $this->morphedByMany(Invoice::class, 'paymentable')->withTrashed()->withPivot('amount', 'refunded')->withTimestamps(); return $this->morphedByMany(Invoice::class, 'paymentable')->withTrashed()->withPivot('amount', 'refunded', 'deleted_at')->withTimestamps();
} }
/** /**
@ -228,7 +228,7 @@ class Payment extends BaseModel
*/ */
public function credits(): \Illuminate\Database\Eloquent\Relations\MorphToMany public function credits(): \Illuminate\Database\Eloquent\Relations\MorphToMany
{ {
return $this->morphedByMany(Credit::class, 'paymentable')->withTrashed()->withPivot('amount', 'refunded')->withTimestamps(); return $this->morphedByMany(Credit::class, 'paymentable')->withTrashed()->withPivot('amount', 'refunded', 'deleted_at')->withTimestamps();
} }
/** /**
@ -251,7 +251,7 @@ class Payment extends BaseModel
public function transaction(): \Illuminate\Database\Eloquent\Relations\BelongsTo public function transaction(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{ {
return $this->belongsTo(BankTransaction::class); return $this->belongsTo(BankTransaction::class)->withTrashed();
} }
public function exchange_currency(): \Illuminate\Database\Eloquent\Relations\BelongsTo public function exchange_currency(): \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@ -30,21 +30,21 @@ use League\CommonMark\CommonMarkConverter;
* @property string|null $custom_value4 * @property string|null $custom_value4
* @property string|null $product_key * @property string|null $product_key
* @property string|null $notes * @property string|null $notes
* @property string $cost * @property float $cost
* @property string $price * @property float $price
* @property string $quantity * @property float $quantity
* @property string|null $tax_name1 * @property string|null $tax_name1
* @property string $tax_rate1 * @property float $tax_rate1
* @property string|null $tax_name2 * @property string|null $tax_name2
* @property string $tax_rate2 * @property float $tax_rate2
* @property string|null $tax_name3 * @property string|null $tax_name3
* @property string $tax_rate3 * @property float $tax_rate3
* @property int|null $deleted_at * @property int|null $deleted_at
* @property int|null $created_at * @property int|null $created_at
* @property int|null $updated_at * @property int|null $updated_at
* @property int $is_deleted * @property bool $is_deleted
* @property int $in_stock_quantity * @property float $in_stock_quantity
* @property int $stock_notification * @property bool $stock_notification
* @property int $stock_notification_threshold * @property int $stock_notification_threshold
* @property int|null $max_quantity * @property int|null $max_quantity
* @property string|null $product_image * @property string|null $product_image

View File

@ -15,10 +15,10 @@ use Laracasts\Presenter\PresentableTrait;
* @property int $company_id * @property int $company_id
* @property int|null $client_id * @property int|null $client_id
* @property string $name * @property string $name
* @property string $task_rate * @property float $task_rate
* @property string|null $due_date * @property string|null $due_date
* @property string|null $private_notes * @property string|null $private_notes
* @property string $budgeted_hours * @property float $budgeted_hours
* @property string|null $custom_value1 * @property string|null $custom_value1
* @property string|null $custom_value2 * @property string|null $custom_value2
* @property string|null $custom_value3 * @property string|null $custom_value3
@ -27,7 +27,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property int|null $updated_at * @property int|null $updated_at
* @property int|null $deleted_at * @property int|null $deleted_at
* @property string|null $public_notes * @property string|null $public_notes
* @property int $is_deleted * @property bool $is_deleted
* @property string|null $number * @property string|null $number
* @property string $color * @property string $color
* @property-read \App\Models\Client|null $client * @property-read \App\Models\Client|null $client

View File

@ -42,8 +42,8 @@ use Illuminate\Support\Facades\Storage;
* @property string|null $date * @property string|null $date
* @property string|null $last_sent_date * @property string|null $last_sent_date
* @property string|null $due_date * @property string|null $due_date
* @property int $is_deleted * @property bool $is_deleted
* @property object|null $line_items * @property object|array|string $line_items
* @property object|null $backup * @property object|null $backup
* @property string|null $footer * @property string|null $footer
* @property string|null $public_notes * @property string|null $public_notes

View File

@ -19,6 +19,7 @@ use App\Utils\Traits\MakesHash;
use App\Jobs\Mail\NinjaMailerJob; use App\Jobs\Mail\NinjaMailerJob;
use App\Services\User\UserService; use App\Services\User\UserService;
use App\Utils\Traits\UserSettings; use App\Utils\Traits\UserSettings;
use Illuminate\Support\Facades\App;
use App\Jobs\Mail\NinjaMailerObject; use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\Admin\ResetPasswordObject; use App\Mail\Admin\ResetPasswordObject;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -61,6 +62,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
* @property string|null $last_login * @property string|null $last_login
* @property string|null $signature * @property string|null $signature
* @property string $password * @property string $password
* @property string $language_id
* @property string|null $remember_token * @property string|null $remember_token
* @property string|null $custom_value1 * @property string|null $custom_value1
* @property string|null $custom_value2 * @property string|null $custom_value2
@ -153,6 +155,7 @@ class User extends Authenticatable implements MustVerifyEmail
'custom_value4', 'custom_value4',
'is_deleted', 'is_deleted',
'shopify_user_id', 'shopify_user_id',
'language_id',
// 'oauth_user_token', // 'oauth_user_token',
// 'oauth_user_refresh_token', // 'oauth_user_refresh_token',
]; ];
@ -633,15 +636,16 @@ class User extends Authenticatable implements MustVerifyEmail
*/ */
public function sendPasswordResetNotification($token) public function sendPasswordResetNotification($token)
{ {
$is_react = request()->has('react') || request()->hasHeader('X-React') ? true : false;
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new ResetPasswordObject($token, $this, $this->account->default_company))->build()); $nmo->mailable = new NinjaMailer((new ResetPasswordObject($token, $this, $this->account->default_company, $is_react))->build());
$nmo->to_user = $this; $nmo->to_user = $this;
$nmo->settings = $this->account->default_company->settings; $nmo->settings = $this->account->default_company->settings;
$nmo->company = $this->account->default_company; $nmo->company = $this->account->default_company;
NinjaMailerJob::dispatch($nmo, true); NinjaMailerJob::dispatch($nmo, true);
//$this->notify(new ResetPasswordNotification($token));
} }
public function service() public function service()
@ -649,6 +653,21 @@ class User extends Authenticatable implements MustVerifyEmail
return new UserService($this); return new UserService($this);
} }
public function language()
{
return $this->belongsTo(Language::class);
}
public function getLocale()
{
$locale = $this->language->locale ?? null;
if($locale)
App::setLocale($locale);
return $locale;
}
public function translate_entity() public function translate_entity()
{ {
return ctrans('texts.user'); return ctrans('texts.user');

View File

@ -12,14 +12,15 @@
namespace App\PaymentDrivers\Authorize; namespace App\PaymentDrivers\Authorize;
use App\Exceptions\GenericPaymentDriverFailure;
use App\Models\Client; use App\Models\Client;
use App\PaymentDrivers\AuthorizePaymentDriver; use App\PaymentDrivers\AuthorizePaymentDriver;
use net\authorize\api\contract\v1\CreateCustomerProfileRequest; use App\Exceptions\GenericPaymentDriverFailure;
use net\authorize\api\contract\v1\CustomerAddressType;
use net\authorize\api\contract\v1\CustomerProfileType; use net\authorize\api\contract\v1\CustomerProfileType;
use net\authorize\api\contract\v1\GetCustomerProfileRequest; use net\authorize\api\contract\v1\GetCustomerProfileRequest;
use net\authorize\api\controller\CreateCustomerProfileController;
use net\authorize\api\controller\GetCustomerProfileController; use net\authorize\api\controller\GetCustomerProfileController;
use net\authorize\api\contract\v1\CreateCustomerProfileRequest;
use net\authorize\api\controller\CreateCustomerProfileController;
/** /**
* Class BaseDriver. * Class BaseDriver.
@ -53,6 +54,28 @@ class AuthorizeCreateCustomer
$customerProfile->setMerchantCustomerId('M_'.time()); $customerProfile->setMerchantCustomerId('M_'.time());
$customerProfile->setEmail($this->client->present()->email()); $customerProfile->setEmail($this->client->present()->email());
// if($this->client) {
// $primary_contact = $this->client->primary_contact()->first() ?? $this->client->contacts()->first();
// $shipTo = new CustomerAddressType();
// $shipTo->setFirstName(substr($primary_contact->present()->first_name(), 0, 50));
// $shipTo->setLastName(substr($primary_contact->present()->last_name(), 0, 50));
// $shipTo->setCompany(substr($this->client->present()->name(), 0, 50));
// $shipTo->setAddress(substr($this->client->shipping_address1, 0, 60));
// $shipTo->setCity(substr($this->client->shipping_city, 0, 40));
// $shipTo->setState(substr($this->client->shipping_state, 0, 40));
// $shipTo->setZip(substr($this->client->shipping_postal_code, 0, 20));
// if ($this->client->country_id) {
// $shipTo->setCountry($this->client->shipping_country->name);
// }
// $shipTo->setPhoneNumber(substr($this->client->phone, 0, 20));
// $customerProfile->setShipToList([$shipTo]);
// }
// Assemble the complete transaction request // Assemble the complete transaction request
$request = new CreateCustomerProfileRequest(); $request = new CreateCustomerProfileRequest();
$request->setMerchantAuthentication($this->authorize->merchant_authentication); $request->setMerchantAuthentication($this->authorize->merchant_authentication);

View File

@ -330,7 +330,7 @@ class BaseDriver extends AbstractPaymentDriver
$payment->gateway_type_id = $data['gateway_type_id']; $payment->gateway_type_id = $data['gateway_type_id'];
$client_contact = $this->getContact(); $client_contact = $this->getContact();
$client_contact_id = $client_contact ? $client_contact->id : null; $client_contact_id = $client_contact ? $client_contact->id : $this->client->contacts()->first()->id;
$payment->amount = $data['amount']; $payment->amount = $data['amount'];
$payment->type_id = $data['payment_type']; $payment->type_id = $data['payment_type'];
@ -430,9 +430,9 @@ class BaseDriver extends AbstractPaymentDriver
public function getContact() public function getContact()
{ {
if ($this->invitation) { if ($this->invitation) {
return ClientContact::find($this->invitation->client_contact_id); return ClientContact::withTrashed()->find($this->invitation->client_contact_id);
} elseif (auth()->guard('contact')->user()) { } elseif (auth()->guard('contact')->user()) {
return auth()->user(); return auth()->guard('contact')->user();
} else { } else {
return false; return false;
} }

View File

@ -98,11 +98,8 @@ trait Utilities
$error_message = $_payment['actions'][0]['response_summary']; $error_message = $_payment['actions'][0]['response_summary'];
} }
if(isset($_payment['actions'][0]['response_code']) ?? false) { //checkout does not return a integer status code as an alias for a http status code.
$error_code = $_payment['actions'][0]['response_code']; $error_code = 400;
}
else
$error_code = 400;
$this->getParent()->sendFailureMail($error_message); $this->getParent()->sendFailureMail($error_message);

View File

@ -121,10 +121,11 @@ class CheckoutComPaymentDriver extends BaseDriver
$this->is_four_api = true; //was four api, now known as previous. $this->is_four_api = true; //was four api, now known as previous.
/** @phpstan-ignore-next-line **/
$builder = CheckoutSdk::builder() $builder = CheckoutSdk::builder()
->previous() ->previous()
->staticKeys() ->staticKeys()
->environment($this->company_gateway->getConfigField('testMode') ? Environment::sandbox() : Environment::production()) ->environment($this->company_gateway->getConfigField('testMode') ? Environment::sandbox() : Environment::production()) /** phpstan-ignore-line **/
->publicKey($this->company_gateway->getConfigField('publicApiKey')) ->publicKey($this->company_gateway->getConfigField('publicApiKey'))
->secretKey($this->company_gateway->getConfigField('secretApiKey')); ->secretKey($this->company_gateway->getConfigField('secretApiKey'));
@ -132,10 +133,12 @@ class CheckoutComPaymentDriver extends BaseDriver
} else { } else {
$builder = CheckoutSdk::builder()->staticKeys() /** @phpstan-ignore-next-line **/
$builder = CheckoutSdk::builder()
->staticKeys()
->environment($this->company_gateway->getConfigField('testMode') ? Environment::sandbox() : Environment::production()) /** phpstan-ignore-line **/
->publicKey($this->company_gateway->getConfigField('publicApiKey')) ->publicKey($this->company_gateway->getConfigField('publicApiKey'))
->secretKey($this->company_gateway->getConfigField('secretApiKey')) ->secretKey($this->company_gateway->getConfigField('secretApiKey'));
->environment($this->company_gateway->getConfigField('testMode') ? Environment::sandbox() : Environment::production());
$this->gateway = $builder->build(); $this->gateway = $builder->build();
@ -221,6 +224,16 @@ class CheckoutComPaymentDriver extends BaseDriver
$response = $this->gateway->getPaymentsClient()->refundPayment($payment->transaction_reference, $request); $response = $this->gateway->getPaymentsClient()->refundPayment($payment->transaction_reference, $request);
SystemLogger::dispatch(
array_merge(['message' => "Gateway Refund"], $response),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_CHECKOUT,
$payment->client,
$payment->company,
);
return [ return [
'transaction_reference' => $response['action_id'], 'transaction_reference' => $response['action_id'],
'transaction_response' => json_encode($response), 'transaction_response' => json_encode($response),
@ -228,13 +241,21 @@ class CheckoutComPaymentDriver extends BaseDriver
'description' => $response['reference'], 'description' => $response['reference'],
'code' => 202, 'code' => 202,
]; ];
} catch (CheckoutApiException $e) { } catch (CheckoutApiException $e) {
// API error // API error
throw new PaymentFailed($e->getMessage(), $e->getCode()); throw new PaymentFailed($e->getMessage(), $e->getCode());
} catch (CheckoutArgumentException $e) { } catch (CheckoutArgumentException $e) {
// Bad arguments // Bad arguments
// throw new PaymentFailed($e->getMessage(), $e->getCode()); SystemLogger::dispatch(
$e->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_CHECKOUT,
$payment->client,
$payment->company,
);
return [ return [
'transaction_reference' => null, 'transaction_reference' => null,
@ -243,9 +264,17 @@ class CheckoutComPaymentDriver extends BaseDriver
'description' => $e->getMessage(), 'description' => $e->getMessage(),
'code' => $e->getCode(), 'code' => $e->getCode(),
]; ];
} catch (CheckoutAuthorizationException $e) { } catch (CheckoutAuthorizationException $e) {
// throw new PaymentFailed("The was a problem with the Checkout Gateway Credentials.", $e->getCode()); SystemLogger::dispatch(
$e->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_CHECKOUT,
$payment->client,
$payment->company,
);
return [ return [
'transaction_reference' => null, 'transaction_reference' => null,
@ -268,13 +297,14 @@ class CheckoutComPaymentDriver extends BaseDriver
$request = new CustomerRequest(); $request = new CustomerRequest();
$phone = new Phone(); $phone = new Phone();
// $phone->number = $this->client->present()->phone();
$phone->number = substr(str_pad($this->client->present()->phone(), 6, "0", STR_PAD_RIGHT), 0, 24); $phone->number = substr(str_pad($this->client->present()->phone(), 6, "0", STR_PAD_RIGHT), 0, 24);
$request->email = $this->client->present()->email(); $request->email = $this->client->present()->email();
$request->name = $this->client->present()->name(); $request->name = $this->client->present()->name();
$request->phone = $phone; $request->phone = $phone;
// if($this->company_gateway->update_details)
// $this->updateCustomer();
try { try {
$response = $this->gateway->getCustomersClient()->create($request); $response = $this->gateway->getCustomersClient()->create($request);
} catch (CheckoutApiException $e) { } catch (CheckoutApiException $e) {
@ -301,6 +331,27 @@ class CheckoutComPaymentDriver extends BaseDriver
} }
} }
public function updateCustomer()
{
$phone = new Phone();
$phone->number = substr(str_pad($this->client->present()->phone(), 6, "0", STR_PAD_RIGHT), 0, 24);
$request = new CustomerRequest();
$request->email = $this->client->present()->email();
$request->name = $this->client->present()->name();
$request->phone = $phone;
try {
$response = $this->gateway->getCustomersClient()->update("customer_id", $request);
} catch (CheckoutApiException $e) {
} catch (CheckoutAuthorizationException $e) {
}
}
/** /**
* Boots a request for a token payment * Boots a request for a token payment
* *

View File

@ -98,10 +98,17 @@ class GoCardlessPaymentDriver extends BaseDriver
public function init(): self public function init(): self
{ {
$this->gateway = new \GoCardlessPro\Client([ try {
'access_token' => $this->company_gateway->getConfigField('accessToken'), $this->gateway = new \GoCardlessPro\Client([
'environment' => $this->company_gateway->getConfigField('testMode') ? \GoCardlessPro\Environment::SANDBOX : \GoCardlessPro\Environment::LIVE, 'access_token' => $this->company_gateway->getConfigField('accessToken'),
]); 'environment' => $this->company_gateway->getConfigField('testMode') ? \GoCardlessPro\Environment::SANDBOX : \GoCardlessPro\Environment::LIVE,
]);
}
catch(\GoCardlessPro\Core\Exception\AuthenticationException $e){
throw new \Exception('GoCardless: Invalid Access Token', 403);
}
return $this; return $this;
} }

View File

@ -206,6 +206,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
'transactionId' => $this->payment_hash->hash.'-'.time(), 'transactionId' => $this->payment_hash->hash.'-'.time(),
'ButtonSource' => 'InvoiceNinja_SP', 'ButtonSource' => 'InvoiceNinja_SP',
'solutionType' => 'Sole', 'solutionType' => 'Sole',
'no_shipping' => $this->company_gateway->require_shipping_address ? 0 : 1,
]; ];
} }

View File

@ -142,6 +142,11 @@ class CreditCard implements MethodInterface
return $this->processSuccessfulPayment($response); return $this->processSuccessfulPayment($response);
} }
if(is_array($response)) {
nlog("square");
nlog($response);
}
return $this->processUnsuccessfulPayment($response); return $this->processUnsuccessfulPayment($response);
} }
@ -293,7 +298,7 @@ class CreditCard implements MethodInterface
$body->setFamilyName(''); $body->setFamilyName('');
$body->setEmailAddress($this->square_driver->client->present()->email()); $body->setEmailAddress($this->square_driver->client->present()->email());
$body->setAddress($billing_address); $body->setAddress($billing_address);
$body->setPhoneNumber($this->square_driver->client->phone); // $body->setPhoneNumber($this->square_driver->client->phone);
$body->setReferenceId($this->square_driver->client->number); $body->setReferenceId($this->square_driver->client->number);
$body->setNote('Created by Invoice Ninja.'); $body->setNote('Created by Invoice Ninja.');
@ -309,8 +314,8 @@ class CreditCard implements MethodInterface
return $result->getCustomer()->getId(); return $result->getCustomer()->getId();
} else { } else {
$errors = $api_response->getErrors(); $errors = $api_response->getErrors();
nlog($errors);
return $this->processUnsuccessfulPayment($errors); return $this->processUnsuccessfulPayment($api_response);
} }
} }
} }

View File

@ -307,6 +307,7 @@ class ACH
switch ($e) { switch ($e) {
case $e instanceof CardException: case $e instanceof CardException:
/** @var CardException $e */
$data['status'] = $e->getHttpStatus(); $data['status'] = $e->getHttpStatus();
$data['error_type'] = $e->getError()->type; $data['error_type'] = $e->getError()->type;
$data['error_code'] = $e->getError()->code; $data['error_code'] = $e->getError()->code;

View File

@ -87,7 +87,9 @@ class Alipay
return $this->processSuccesfulRedirect($pi); return $this->processSuccesfulRedirect($pi);
} }
if ($pi->status == 'requires_source_action') { /** @phpstan-ignore-next-line */
if ($pi->status == 'requires_source_action' && $pi->next_action->alipay_handle_redirect) {
/** @phpstan-ignore-next-line */
return redirect($pi->next_action->alipay_handle_redirect->url); return redirect($pi->next_action->alipay_handle_redirect->url);
} }
} }

View File

@ -112,7 +112,7 @@ class ImportCustomers
$client->address2 = $customer->address->line2 ? $customer->address->line2 : ''; $client->address2 = $customer->address->line2 ? $customer->address->line2 : '';
$client->city = $customer->address->city ? $customer->address->city : ''; $client->city = $customer->address->city ? $customer->address->city : '';
$client->state = $customer->address->state ? $customer->address->state : ''; $client->state = $customer->address->state ? $customer->address->state : '';
$client->phone = $customer->address->phone ? $customer->phone : ''; $client->phone = $customer->phone ?? '';
if ($customer->address->country) { if ($customer->address->country) {
$country = Country::query()->where('iso_3166_2', $customer->address->country)->first(); $country = Country::query()->where('iso_3166_2', $customer->address->country)->first();

View File

@ -39,7 +39,6 @@ use App\PaymentDrivers\Stripe\FPX;
use App\PaymentDrivers\Stripe\GIROPAY; use App\PaymentDrivers\Stripe\GIROPAY;
use App\PaymentDrivers\Stripe\iDeal; use App\PaymentDrivers\Stripe\iDeal;
use App\PaymentDrivers\Stripe\ImportCustomers; use App\PaymentDrivers\Stripe\ImportCustomers;
use App\PaymentDrivers\Stripe\Jobs\ChargeRefunded;
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook; use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook;
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentPartiallyFundedWebhook; use App\PaymentDrivers\Stripe\Jobs\PaymentIntentPartiallyFundedWebhook;
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentProcessingWebhook; use App\PaymentDrivers\Stripe\Jobs\PaymentIntentProcessingWebhook;
@ -791,12 +790,6 @@ class StripePaymentDriver extends BaseDriver
} elseif ($request->data['object']['status'] == "pending") { } elseif ($request->data['object']['status'] == "pending") {
return response()->json([], 200); return response()->json([], 200);
} }
} elseif ($request->type === "charge.refunded") {
ChargeRefunded::dispatch($request->data, $request->company_key, $this->company_gateway->id)->delay(now()->addSeconds(rand(5, 10)));
return response()->json([], 200);
} }
return response()->json([], 200); return response()->json([], 200);

View File

@ -135,7 +135,6 @@ class ActivityRepository extends BaseRepository
$design = Design::withTrashed()->find($entity_design_id); $design = Design::withTrashed()->find($entity_design_id);
if (! $entity->invitations()->exists() || ! $design) { if (! $entity->invitations()->exists() || ! $design) {
nlog("No invitations for entity {$entity->id} - {$entity->number}");
return ''; return '';
} }
@ -204,8 +203,6 @@ class ActivityRepository extends BaseRepository
$design = Design::withTrashed()->find($entity_design_id); $design = Design::withTrashed()->find($entity_design_id);
if (! $entity->invitations()->exists() || ! $design) { if (! $entity->invitations()->exists() || ! $design) {
nlog("No invitations for entity {$entity->id} - {$entity->number}");
return ''; return '';
} }

View File

@ -38,11 +38,14 @@ class BankTransactionRepository extends BaseRepository
public function convert_matched($bank_transactions) public function convert_matched($bank_transactions)
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
$data['transactions'] = $bank_transactions->map(function ($bt) { $data['transactions'] = $bank_transactions->map(function ($bt) {
return ['id' => $bt->id, 'invoice_ids' => $bt->invoice_ids, 'ninja_category_id' => $bt->ninja_category_id]; return ['id' => $bt->id, 'invoice_ids' => $bt->invoice_ids, 'ninja_category_id' => $bt->ninja_category_id];
})->toArray(); })->toArray();
$bts = (new MatchBankTransactions(auth()->user()->company()->id, auth()->user()->company()->db, $data))->handle(); $bts = (new MatchBankTransactions($user->company()->id, $user->company()->db, $data))->handle();
} }
public function unlink($bt) public function unlink($bt)

View File

@ -67,9 +67,12 @@ class ExpenseRepository extends BaseRepository
*/ */
public function create($expense): ?Expense public function create($expense): ?Expense
{ {
/** @var \App\Models\User $user */
$user = auth()->user();
return $this->save( return $this->save(
$expense, $expense,
ExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id) ExpenseFactory::create($user->company()->id, $user->id)
); );
} }

View File

@ -101,6 +101,9 @@ class PaymentRepository extends BaseRepository
$client->saveQuietly(); $client->saveQuietly();
} }
}, 1); }, 1);
$client = Client::query()->where('id', $data['client_id'])->withTrashed()->first();
} }
/*Fill the payment*/ /*Fill the payment*/
@ -108,7 +111,7 @@ class PaymentRepository extends BaseRepository
$payment->is_manual = true; $payment->is_manual = true;
$payment->status_id = Payment::STATUS_COMPLETED; $payment->status_id = Payment::STATUS_COMPLETED;
if (! $payment->currency_id && $client) { if ((!$payment->currency_id || $payment->currency_id == 0) && $client) {
if (property_exists($client->settings, 'currency_id')) { if (property_exists($client->settings, 'currency_id')) {
$payment->currency_id = $client->settings->currency_id; $payment->currency_id = $client->settings->currency_id;
} else { } else {

View File

@ -44,6 +44,19 @@ class ClientService
}, 2); }, 2);
} catch (\Throwable $throwable) { } catch (\Throwable $throwable) {
nlog("DB ERROR " . $throwable->getMessage()); nlog("DB ERROR " . $throwable->getMessage());
DB::connection(config('database.default'))->rollBack();
if (DB::connection(config('database.default'))->transactionLevel() > 0) {
DB::connection(config('database.default'))->rollBack();
}
} catch(\Exception $exception){
nlog("DB ERROR " . $exception->getMessage());
DB::connection(config('database.default'))->rollBack();
if (DB::connection(config('database.default'))->transactionLevel() > 0) {
DB::connection(config('database.default'))->rollBack();
}
} }
return $this; return $this;
@ -60,6 +73,17 @@ class ClientService
}, 2); }, 2);
} catch (\Throwable $throwable) { } catch (\Throwable $throwable) {
nlog("DB ERROR " . $throwable->getMessage()); nlog("DB ERROR " . $throwable->getMessage());
if (DB::connection(config('database.default'))->transactionLevel() > 0) {
DB::connection(config('database.default'))->rollBack();
}
} catch(\Exception $exception){
nlog("DB ERROR " . $exception->getMessage());
if (DB::connection(config('database.default'))->transactionLevel() > 0) {
DB::connection(config('database.default'))->rollBack();
}
} }
return $this; return $this;
@ -67,11 +91,27 @@ class ClientService
public function updatePaidToDate(float $amount) public function updatePaidToDate(float $amount)
{ {
DB::connection(config('database.default'))->transaction(function () use ($amount) { try {
$this->client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first(); DB::connection(config('database.default'))->transaction(function () use ($amount) {
$this->client->paid_to_date += $amount; $this->client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first();
$this->client->saveQuietly(); $this->client->paid_to_date += $amount;
}, 2); $this->client->saveQuietly();
}, 2);
}
catch (\Throwable $throwable) {
nlog("DB ERROR " . $throwable->getMessage());
if (DB::connection(config('database.default'))->transactionLevel() > 0) {
DB::connection(config('database.default'))->rollBack();
}
} catch(\Exception $exception){
nlog("DB ERROR " . $exception->getMessage());
if (DB::connection(config('database.default'))->transactionLevel() > 0) {
DB::connection(config('database.default'))->rollBack();
}
}
return $this; return $this;
} }

View File

@ -62,6 +62,7 @@ class Statement
} }
$variables = $html->generateLabelsAndValues(); $variables = $html->generateLabelsAndValues();
$variables['values']['$show_paid_stamp'] = 'none'; //do not show paid stamp on statement
$state = [ $state = [
'template' => $template->elements([ 'template' => $template->elements([

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