mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
commit
7f680c947f
2
.github/workflows/phpunit.yml
vendored
2
.github/workflows/phpunit.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
operating-system: ['ubuntu-18.04', 'ubuntu-20.04', 'ubuntu-22.04']
|
||||
operating-system: ['ubuntu-20.04', 'ubuntu-22.04']
|
||||
php-versions: ['8.1']
|
||||
phpunit-versions: ['latest']
|
||||
|
||||
|
@ -1 +1 @@
|
||||
5.5.17
|
||||
5.5.21
|
@ -29,6 +29,7 @@ use App\Models\Payment;
|
||||
use App\Models\Paymentable;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Utils\Ninja;
|
||||
use Exception;
|
||||
@ -75,7 +76,7 @@ class CheckData extends Command
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:check-data {--database=} {--fix=} {--client_id=} {--vendor_id=} {--paid_to_date=} {--client_balance=} {--ledger_balance=} {--balance_status=}';
|
||||
protected $signature = 'ninja:check-data {--database=} {--fix=} {--portal_url=} {--client_id=} {--vendor_id=} {--paid_to_date=} {--client_balance=} {--ledger_balance=} {--balance_status=}';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@ -115,9 +116,12 @@ class CheckData extends Command
|
||||
$this->checkCompanyData();
|
||||
$this->checkBalanceVsPaidStatus();
|
||||
$this->checkDuplicateRecurringInvoices();
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$this->checkOauthSanity();
|
||||
|
||||
if(Ninja::isHosted()){
|
||||
$this->checkAccountStatuses();
|
||||
$this->checkNinjaPortalUrls();
|
||||
}
|
||||
|
||||
if (! $this->option('client_id')) {
|
||||
$this->checkOAuth();
|
||||
@ -146,6 +150,15 @@ class CheckData extends Command
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
|
||||
private function checkOauthSanity()
|
||||
{
|
||||
User::where('oauth_provider_id', '1')->cursor()->each(function ($user){
|
||||
|
||||
$this->logMessage("Invalid provider ID for user id# {$user->id}");
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private function checkDuplicateRecurringInvoices()
|
||||
{
|
||||
|
||||
@ -494,12 +507,12 @@ class CheckData extends Command
|
||||
|
||||
$this->wrong_paid_to_dates++;
|
||||
|
||||
$this->logMessage($client->present()->name.' id = # '.$client->id." - Client Paid To Date = {$client->paid_to_date} != Invoice Payments = {$total_paid_to_date} - {$_client->payments_applied} + {$credits_used_for_payments[0]->credit_payment}");
|
||||
$this->logMessage($client->present()->name().' id = # '.$client->id." - Client Paid To Date = {$client->paid_to_date} != Invoice Payments = {$total_paid_to_date} - {$_client->payments_applied} + {$credits_used_for_payments[0]->credit_payment}");
|
||||
|
||||
$this->isValid = false;
|
||||
|
||||
if($this->option('paid_to_date')){
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->paid_to_date} to {$total_paid_to_date}");
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->paid_to_date} to {$total_paid_to_date}");
|
||||
$client->paid_to_date = $total_paid_to_date;
|
||||
$client->save();
|
||||
}
|
||||
@ -571,12 +584,12 @@ class CheckData extends Command
|
||||
if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) {
|
||||
$this->wrong_paid_to_dates++;
|
||||
|
||||
$this->logMessage($client->present()->name.' id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}");
|
||||
$this->logMessage($client->present()->name().' id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}");
|
||||
|
||||
$this->isValid = false;
|
||||
|
||||
if($this->option('paid_to_date')){
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->paid_to_date} to {$total_invoice_payments}");
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->paid_to_date} to {$total_invoice_payments}");
|
||||
$client->paid_to_date = $total_invoice_payments;
|
||||
$client->save();
|
||||
}
|
||||
@ -607,7 +620,7 @@ class CheckData extends Command
|
||||
if ((string)$total_paid != (string)($invoice->amount - $invoice->balance - $total_credit)) {
|
||||
$this->wrong_balances++;
|
||||
|
||||
$this->logMessage($client->present()->name.' - '.$client->id." - Total Paid = {$total_paid} != Calculated Total = {$calculated_paid_amount}");
|
||||
$this->logMessage($client->present()->name().' - '.$client->id." - Total Paid = {$total_paid} != Calculated Total = {$calculated_paid_amount}");
|
||||
|
||||
$this->isValid = false;
|
||||
}
|
||||
@ -655,11 +668,11 @@ class CheckData extends Command
|
||||
|
||||
$client_object = Client::withTrashed()->find($client['client_id']);
|
||||
|
||||
$this->logMessage($client_object->present()->name.' - '.$client_object->id." - calculated client balances do not match Invoice Balances = ". $client['invoice_balance'] ." - Client Balance = ".rtrim($client['client_balance'], '0'));
|
||||
$this->logMessage($client_object->present()->name().' - '.$client_object->id." - calculated client balances do not match Invoice Balances = ". $client['invoice_balance'] ." - Client Balance = ".rtrim($client['client_balance'], '0'));
|
||||
|
||||
if($this->option('client_balance')){
|
||||
|
||||
$this->logMessage("# {$client_object->id} " . $client_object->present()->name.' - '.$client_object->number." Fixing {$client_object->balance} to " . $client['invoice_balance']);
|
||||
$this->logMessage("# {$client_object->id} " . $client_object->present()->name().' - '.$client_object->number." Fixing {$client_object->balance} to " . $client['invoice_balance']);
|
||||
$client_object->balance = $client['invoice_balance'];
|
||||
$client_object->save();
|
||||
|
||||
@ -693,7 +706,7 @@ class CheckData extends Command
|
||||
|
||||
if($this->option('client_balance')){
|
||||
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->balance} to 0");
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to 0");
|
||||
|
||||
$client->balance = 0;
|
||||
$client->save();
|
||||
@ -747,13 +760,13 @@ class CheckData extends Command
|
||||
$this->wrong_balances++;
|
||||
$ledger_balance = $ledger ? $ledger->balance : 0;
|
||||
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger_balance}");
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger_balance}");
|
||||
|
||||
$this->isValid = false;
|
||||
|
||||
if($this->option('client_balance')){
|
||||
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
|
||||
$client->balance = $invoice_balance;
|
||||
$client->save();
|
||||
|
||||
@ -785,14 +798,14 @@ class CheckData extends Command
|
||||
|
||||
if ($ledger && number_format($ledger->balance, 4) != number_format($client->balance, 4)) {
|
||||
$this->wrong_balances++;
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." - Balance Failure - Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}");
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." - Balance Failure - Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}");
|
||||
|
||||
$this->isValid = false;
|
||||
|
||||
|
||||
if($this->option('ledger_balance')){
|
||||
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
|
||||
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
|
||||
$client->balance = $invoice_balance;
|
||||
$client->save();
|
||||
|
||||
@ -955,7 +968,7 @@ class CheckData extends Command
|
||||
{
|
||||
$this->wrong_paid_status = 0;
|
||||
|
||||
foreach(Invoice::with(['payments'])->whereHas('payments')->where('status_id', 4)->where('balance', '>', 0)->where('is_deleted',0)->cursor() as $invoice)
|
||||
foreach(Invoice::with(['payments'])->where('is_deleted',0)->where('balance', '>', 0)->whereHas('payments')->where('status_id', 4)->cursor() as $invoice)
|
||||
{
|
||||
$this->wrong_paid_status++;
|
||||
|
||||
@ -992,4 +1005,31 @@ class CheckData extends Command
|
||||
$this->logMessage($this->wrong_paid_status." wrong invoices with bad balance state");
|
||||
|
||||
}
|
||||
|
||||
public function checkNinjaPortalUrls()
|
||||
{
|
||||
|
||||
$wrong_count = CompanyUser::where('is_owner',1)->where('ninja_portal_url', '')->count();
|
||||
|
||||
$this->logMessage("Missing ninja portal Urls = {$wrong_count}");
|
||||
|
||||
if(!$this->option('portal_url'))
|
||||
return;
|
||||
|
||||
CompanyUser::where('is_owner',1)->where('ninja_portal_url', '')->cursor()->each(function ($cu){
|
||||
|
||||
$cc = ClientContact::on('db-ninja-01')->where('company_id', config('ninja.ninja_default_company_id'))->where('email', $cu->user->email)->first();
|
||||
|
||||
if($cc){
|
||||
$ninja_portal_url = "https://invoiceninja.invoicing.co/client/ninja/{$cc->contact_key}/{$cu->company->company_key}";
|
||||
|
||||
$cu->ninja_portal_url = $ninja_portal_url;
|
||||
$cu->save();
|
||||
|
||||
$this->logMessage("Fixing - {$ninja_portal_url}");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
@ -189,7 +189,7 @@ class CreateSingleAccount extends Command
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
|
||||
CreateCompanyTaskStatuses::dispatchNow($company, $user);
|
||||
(new CreateCompanyTaskStatuses($company, $user))->handle();
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
$client = $company->clients->random();
|
||||
|
@ -132,7 +132,8 @@ class DemoMode extends Command
|
||||
'enabled_modules' => 32767,
|
||||
'company_key' => 'KEY',
|
||||
'enable_shop_api' => true,
|
||||
'markdown_email_enabled' => false,
|
||||
'markdown_email_enabled' => true,
|
||||
'markdown_enabled' => false,
|
||||
]);
|
||||
|
||||
$settings = $company->settings;
|
||||
|
@ -97,10 +97,6 @@ class SendRemindersCron extends Command
|
||||
}
|
||||
});
|
||||
|
||||
// SendReminders::dispatchNow();
|
||||
|
||||
// $this->webHookOverdueInvoices();
|
||||
// $this->webHookExpiredQuotes();
|
||||
}
|
||||
|
||||
private function calcLateFee($invoice, $template) :Invoice
|
||||
|
59
app/DataMapper/Analytics/EmailCount.php
Normal file
59
app/DataMapper/Analytics/EmailCount.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Analytics;
|
||||
|
||||
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
|
||||
|
||||
class EmailCount extends GenericMixedMetric
|
||||
{
|
||||
/**
|
||||
* The type of Sample.
|
||||
*
|
||||
* Monotonically incrementing counter
|
||||
*
|
||||
* - counter
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'mixed_metric';
|
||||
|
||||
/**
|
||||
* The name of the counter.
|
||||
* @var string
|
||||
*/
|
||||
public $name = 'account.daily_email_count';
|
||||
|
||||
/**
|
||||
* The datetime of the counter measurement.
|
||||
*
|
||||
* date("Y-m-d H:i:s")
|
||||
*
|
||||
* @var DateTime
|
||||
*/
|
||||
public $datetime;
|
||||
|
||||
/**
|
||||
* The Class failure name
|
||||
* set to 0.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $string_metric5 = 'account_key';
|
||||
|
||||
public $int_metric1 = 1;
|
||||
|
||||
public function __construct($int_metric1, $string_metric5)
|
||||
{
|
||||
$this->int_metric1 = $int_metric1;
|
||||
$this->string_metric5 = $string_metric5;
|
||||
}
|
||||
}
|
@ -436,9 +436,12 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $auto_archive_invoice_cancelled = false;
|
||||
|
||||
public $vendor_portal_enable_uploads=false;
|
||||
public $vendor_portal_enable_uploads = false;
|
||||
|
||||
public $send_email_on_mark_paid = false;
|
||||
|
||||
public static $casts = [
|
||||
'send_email_on_mark_paid' => 'bool',
|
||||
'vendor_portal_enable_uploads' => 'bool',
|
||||
'besr_id' => 'string',
|
||||
'qr_iban' => 'string',
|
||||
@ -724,7 +727,6 @@ class CompanySettings extends BaseSettings
|
||||
*/
|
||||
public static function defaults(): stdClass
|
||||
{
|
||||
$config = json_decode(config('ninja.settings'));
|
||||
|
||||
$data = (object) get_class_vars(self::class);
|
||||
|
||||
|
@ -46,7 +46,7 @@ class CompanyFactory
|
||||
|
||||
$company->enabled_modules = config('ninja.enabled_modules'); //32767;//8191; //4095
|
||||
$company->default_password_timeout = 1800000;
|
||||
$company->markdown_email_enabled = false;
|
||||
$company->markdown_email_enabled = true;
|
||||
$company->markdown_enabled = false;
|
||||
|
||||
return $company;
|
||||
|
@ -29,10 +29,7 @@ class RecurringInvoiceToInvoiceFactory
|
||||
$invoice->terms = self::tranformObject($recurring_invoice->terms, $client);
|
||||
$invoice->public_notes = self::tranformObject($recurring_invoice->public_notes, $client);
|
||||
$invoice->private_notes = $recurring_invoice->private_notes;
|
||||
//$invoice->date = now()->format($client->date_format());
|
||||
//$invoice->due_date = $recurring_invoice->calculateDueDate(now());
|
||||
$invoice->is_deleted = $recurring_invoice->is_deleted;
|
||||
// $invoice->line_items = $recurring_invoice->line_items;
|
||||
$invoice->line_items = self::transformItems($recurring_invoice, $client);
|
||||
$invoice->tax_name1 = $recurring_invoice->tax_name1;
|
||||
$invoice->tax_rate1 = $recurring_invoice->tax_rate1;
|
||||
|
@ -168,6 +168,9 @@ class ClientFilters extends QueryFilters
|
||||
{
|
||||
$sort_col = explode('|', $sort);
|
||||
|
||||
if($sort_col[0] == 'display_name')
|
||||
$sort_col[0] = 'name';
|
||||
|
||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,7 @@ class ActivityController extends BaseController
|
||||
'payment' => $activity->payment ? $activity->payment : '',
|
||||
'credit' => $activity->credit ? $activity->credit : '',
|
||||
'task' => $activity->task ? $activity->task : '',
|
||||
'vendor' => $activity->vendor ? $activity->vendor : '',
|
||||
];
|
||||
|
||||
return array_merge($arr, $activity->toArray());
|
||||
|
@ -46,7 +46,7 @@ class ContactRegisterController extends Controller
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($company->settings));
|
||||
|
||||
return render('auth.register', ['register_company' => $company, 'account' => $company->account]);
|
||||
return render('auth.register', ['register_company' => $company, 'account' => $company->account, 'submitsForm' => false]);
|
||||
}
|
||||
|
||||
public function register(RegisterRequest $request)
|
||||
|
@ -351,7 +351,7 @@ class LoginController extends BaseController
|
||||
private function handleSocialiteLogin($provider, $token)
|
||||
{
|
||||
$user = $this->getSocialiteUser($provider, $token);
|
||||
nlog($user);
|
||||
|
||||
if ($user) {
|
||||
return $this->loginOrCreateFromSocialite($user, $provider);
|
||||
}
|
||||
@ -368,6 +368,7 @@ class LoginController extends BaseController
|
||||
'oauth_user_id' => $user->id,
|
||||
'oauth_provider_id' => $provider,
|
||||
];
|
||||
|
||||
if ($existing_user = MultiDB::hasUser($query)) {
|
||||
if (!$existing_user->account) {
|
||||
return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
|
||||
@ -749,10 +750,6 @@ class LoginController extends BaseController
|
||||
public function handleMicrosoftProviderCallback($provider = 'microsoft')
|
||||
{
|
||||
$socialite_user = Socialite::driver($provider)->user();
|
||||
nlog($socialite_user);
|
||||
|
||||
nlog('refresh token ' . $socialite_user->accessTokenResponseBody['refresh_token']);
|
||||
nlog('access token ' . $socialite_user->accessTokenResponseBody['access_token']);
|
||||
|
||||
$oauth_user_token = $socialite_user->accessTokenResponseBody['access_token'];
|
||||
|
||||
|
@ -773,7 +773,8 @@ class BaseController extends Controller
|
||||
// 10-01-2022 need to ensure we snake case properly here to ensure permissions work as expected
|
||||
// 28-03-2022 this is definitely correct here, do not append _ to the view, it resolved correctly when snake cased
|
||||
if (auth()->user() && ! auth()->user()->hasPermission('view'.lcfirst(class_basename(Str::snake($this->entity_type))))) {
|
||||
$query->where('user_id', '=', auth()->user()->id);
|
||||
//03-09-2022
|
||||
$query->where('user_id', '=', auth()->user()->id)->orWhere('assigned_user_id', auth()->user()->id);
|
||||
}
|
||||
|
||||
if (request()->has('updated_at') && request()->input('updated_at') > 0) {
|
||||
@ -900,6 +901,11 @@ class BaseController extends Controller
|
||||
return redirect('/')->with(['signup' => 'true']);
|
||||
}
|
||||
|
||||
// 06-09-2022 - parse the path if loaded in a subdirectory for canvaskit resolution
|
||||
$canvas_path_array = parse_url(config('ninja.app_url'));
|
||||
$canvas_path = (array_key_exists('path', $canvas_path_array)) ? $canvas_path_array['path'] : '';
|
||||
$canvas_path = rtrim(str_replace("index.php", "", $canvas_path),'/');
|
||||
|
||||
$data = [];
|
||||
|
||||
//pass report errors bool to front end
|
||||
@ -910,6 +916,7 @@ class BaseController extends Controller
|
||||
$data['build'] = request()->has('build') ? request()->input('build') : '';
|
||||
$data['login'] = request()->has('login') ? request()->input('login') : 'false';
|
||||
$data['signup'] = request()->has('signup') ? request()->input('signup') : 'false';
|
||||
$data['canvas_path'] = $canvas_path;
|
||||
|
||||
if (request()->session()->has('login')) {
|
||||
$data['login'] = 'true';
|
||||
|
@ -69,7 +69,7 @@ class InvoiceController extends Controller
|
||||
|
||||
$data = [
|
||||
'invoice' => $invoice,
|
||||
'invitation' => $invitation,
|
||||
'invitation' => $invitation ?: $invoice->invitations->first(),
|
||||
'key' => $invitation ? $invitation->key : false,
|
||||
];
|
||||
|
||||
|
@ -195,6 +195,7 @@ class NinjaPlanController extends Controller
|
||||
|
||||
public function plan()
|
||||
{
|
||||
|
||||
// return $this->trial();
|
||||
//harvest the current plan
|
||||
$data = [];
|
||||
|
@ -101,7 +101,8 @@ class PaymentController extends Controller
|
||||
|
||||
$data = [
|
||||
'invoice' => $invoice,
|
||||
'key' => false
|
||||
'key' => false,
|
||||
'invitation' => $invoice->invitations->first()
|
||||
];
|
||||
|
||||
if ($request->query('mode') === 'fullscreen') {
|
||||
|
@ -131,7 +131,7 @@ class EmailController extends BaseController
|
||||
if(Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified)
|
||||
return response(['message' => 'Please verify your account to send emails.'], 400);
|
||||
|
||||
if($entity == 'purchaseOrder' || $template == 'purchase_order'){
|
||||
if($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order'){
|
||||
return $this->sendPurchaseOrder($entity_obj, $data);
|
||||
}
|
||||
|
||||
|
@ -578,6 +578,16 @@ class InvoiceController extends BaseController
|
||||
return response()->json(['message' => ctrans('texts.sent_message')], 200);
|
||||
}
|
||||
|
||||
if($action == 'download' && $invoices->count() >=1 && auth()->user()->can('view', $invoices->first())) {
|
||||
|
||||
$file = $invoices->first()->service()->getInvoicePdf();
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file), ['Content-Type' => 'application/pdf']);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Send the other actions to the switch
|
||||
*/
|
||||
|
@ -306,7 +306,7 @@ class PreviewController extends BaseController
|
||||
if (Ninja::isHosted()) {
|
||||
LightLogs::create(new LivePreview())
|
||||
->increment()
|
||||
->queue();
|
||||
->batch();
|
||||
}
|
||||
|
||||
$response = Response::make($file_path, 200);
|
||||
|
@ -173,7 +173,7 @@ class PreviewPurchaseOrderController extends BaseController
|
||||
}
|
||||
|
||||
//else
|
||||
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
||||
$file_path = (new PreviewPdf($maker->getCompiledHTML(true), auth()->user()->company()))->handle();
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
@ -285,14 +285,14 @@ class PreviewPurchaseOrderController extends BaseController
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), $company);
|
||||
$file_path = (new PreviewPdf($maker->getCompiledHTML(true), $company))->handle();
|
||||
|
||||
|
||||
if(Ninja::isHosted())
|
||||
{
|
||||
LightLogs::create(new LivePreview())
|
||||
->increment()
|
||||
->queue();
|
||||
->batch();
|
||||
}
|
||||
|
||||
|
||||
@ -363,7 +363,7 @@ class PreviewPurchaseOrderController extends BaseController
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
||||
$file_path = (new PreviewPdf($maker->getCompiledHTML(true), auth()->user()->company()))->handle();
|
||||
|
||||
$response = Response::make($file_path, 200);
|
||||
$response->header('Content-Type', 'application/pdf');
|
||||
@ -460,7 +460,7 @@ class PreviewPurchaseOrderController extends BaseController
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
||||
$file_path = (new PreviewPdf($maker->getCompiledHTML(true), auth()->user()->company()))->handle();
|
||||
|
||||
$response = Response::make($file_path, 200);
|
||||
$response->header('Content-Type', 'application/pdf');
|
||||
|
@ -210,7 +210,7 @@ class UserController extends BaseController
|
||||
|
||||
$user_agent = request()->input('token_name') ?: request()->server('HTTP_USER_AGENT');
|
||||
|
||||
$ct = CreateCompanyToken::dispatchNow($company, $user, $user_agent);
|
||||
$ct = (new CreateCompanyToken($company, $user, $user_agent))->handle();
|
||||
|
||||
event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
|
@ -108,8 +108,6 @@ class InvitationController extends Controller
|
||||
|
||||
$file_name = $invitation->purchase_order->numberFormatter().'.pdf';
|
||||
|
||||
// $file = CreateRawPdf::dispatchNow($invitation, $invitation->company->db);
|
||||
|
||||
$file = (new CreatePurchaseOrderPdf($invitation))->rawPdf();
|
||||
|
||||
$headers = ['Content-Type' => 'application/pdf'];
|
||||
|
@ -507,7 +507,7 @@ class BillingPortalPurchase extends Component
|
||||
$mailer->settings = $this->subscription->company->settings;
|
||||
$mailer->to_user = $contact;
|
||||
|
||||
NinjaMailerJob::dispatchNow($mailer);
|
||||
NinjaMailerJob::dispatch($mailer);
|
||||
|
||||
$this->steps['passwordless_login_sent'] = true;
|
||||
$this->passwordless_login_btn = false;
|
||||
|
@ -29,10 +29,10 @@ class NameWebsiteLogo extends Component
|
||||
{
|
||||
$this->fill([
|
||||
'profile' => auth()->guard('contact')->user()->client,
|
||||
'name' => auth()->guard('contact')->user()->client->present()->name,
|
||||
'vat_number' => auth()->guard('contact')->user()->client->present()->vat_number,
|
||||
'website' => auth()->guard('contact')->user()->client->present()->website,
|
||||
'phone' => auth()->guard('contact')->user()->client->present()->phone,
|
||||
'name' => auth()->guard('contact')->user()->client->present()->name(),
|
||||
'vat_number' => auth()->guard('contact')->user()->client->vat_number ?: '',
|
||||
'website' => auth()->guard('contact')->user()->client->website,
|
||||
'phone' => auth()->guard('contact')->user()->client->present()->phone(),
|
||||
'saved' => ctrans('texts.save'),
|
||||
]);
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ class SubscriptionPlanSwitch extends Component
|
||||
*/
|
||||
public $total;
|
||||
|
||||
public $hide_button = false;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -139,12 +140,20 @@ class SubscriptionPlanSwitch extends Component
|
||||
|
||||
public function handlePaymentNotRequired()
|
||||
{
|
||||
return $this->target->service()->createChangePlanCredit([
|
||||
$this->hide_button = true;
|
||||
|
||||
$response = $this->target->service()->createChangePlanCredit([
|
||||
'recurring_invoice' => $this->recurring_invoice,
|
||||
'subscription' => $this->subscription,
|
||||
'target' => $this->target,
|
||||
'hash' => $this->hash,
|
||||
]);
|
||||
|
||||
$this->hide_button = true;
|
||||
|
||||
$this->dispatchBrowserEvent('redirectRoute', ['route' => $response]);
|
||||
|
||||
// return redirect($response);
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
@ -21,6 +21,7 @@ class ContactRegister
|
||||
{
|
||||
$domain_name = $request->getHost();
|
||||
|
||||
/* Hosted */
|
||||
if (strpos($domain_name, 'invoicing.co') !== false) {
|
||||
$subdomain = explode('.', $domain_name)[0];
|
||||
|
||||
@ -42,6 +43,7 @@ class ContactRegister
|
||||
}
|
||||
}
|
||||
|
||||
/* Hosted */
|
||||
if (Ninja::isHosted()) {
|
||||
$query = [
|
||||
'portal_domain' => $request->getSchemeAndHttpHost(),
|
||||
|
@ -68,8 +68,8 @@ class QueryLogging
|
||||
$ip = request()->ip();
|
||||
}
|
||||
|
||||
LightLogs::create(new DbQuery($request->method(), urldecode($request->url()), $count, $time, $ip))
|
||||
->queue();
|
||||
LightLogs::create(new DbQuery($request->method(), substr(urldecode($request->url()),0,180), $count, $time, $ip))
|
||||
->batch();
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
@ -65,7 +65,7 @@ class StoreClientRequest extends Request
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
//$rules['name'] = 'required|min:1';
|
||||
$rules['settings'] = new ValidClientGroupSettingsRule();
|
||||
$rules['contacts'] = 'array';
|
||||
$rules['contacts'] = 'bail|array';
|
||||
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
|
||||
$rules['contacts.*.password'] = [
|
||||
'bail',
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\ClientPortal;
|
||||
|
||||
@ -7,6 +16,7 @@ use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class RegisterRequest extends FormRequest
|
||||
{
|
||||
@ -31,13 +41,13 @@ class RegisterRequest extends FormRequest
|
||||
|
||||
foreach ($this->company()->client_registration_fields as $field) {
|
||||
if ($field['required']) {
|
||||
$rules[$field['key']] = ['required'];
|
||||
$rules[$field['key']] = ['bail','required'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rules as $field => $properties) {
|
||||
if ($field === 'email') {
|
||||
$rules[$field] = array_merge($rules[$field], ['email:rfc,dns', 'max:255']);
|
||||
$rules[$field] = array_merge($rules[$field], ['email:rfc,dns', 'max:255', Rule::unique('client_contacts')->where('company_id', $this->company()->id)]);
|
||||
}
|
||||
|
||||
if ($field === 'current_password') {
|
||||
|
@ -49,7 +49,7 @@ class StoreGroupSettingRequest extends Request
|
||||
}
|
||||
}
|
||||
|
||||
$input['settings'] = $group_settings;
|
||||
$input['settings'] = (array)$group_settings;
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -73,6 +73,6 @@ class UpdateGroupSettingRequest extends Request
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
return (array)$settings;
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class StorePaymentRequest extends Request
|
||||
$invoices_total = 0;
|
||||
$credits_total = 0;
|
||||
|
||||
if (isset($input['client_id'])) {
|
||||
if (isset($input['client_id']) && is_string($input['client_id']) ) {
|
||||
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
||||
}
|
||||
|
||||
@ -53,7 +53,9 @@ class StorePaymentRequest extends Request
|
||||
|
||||
if (isset($input['invoices']) && is_array($input['invoices']) !== false) {
|
||||
foreach ($input['invoices'] as $key => $value) {
|
||||
$input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']);
|
||||
|
||||
if(is_string($value['invoice_id']))
|
||||
$input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']);
|
||||
|
||||
if (array_key_exists('amount', $value)) {
|
||||
$invoices_total += $value['amount'];
|
||||
@ -97,7 +99,8 @@ class StorePaymentRequest extends Request
|
||||
{
|
||||
$rules = [
|
||||
'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',
|
||||
'invoices.*.invoice_id' => 'bail|required|distinct|exists:invoices,id',
|
||||
'invoices.*.amount' => 'bail|required',
|
||||
'invoices.*.invoice_id' => new ValidInvoicesRules($this->all()),
|
||||
|
@ -139,9 +139,14 @@ class Request extends FormRequest
|
||||
$input['invitations'][$key]['id'] = $this->decodePrimaryKey($input['invitations'][$key]['id']);
|
||||
}
|
||||
|
||||
if (is_string($input['invitations'][$key]['client_contact_id'])) {
|
||||
if (array_key_exists('client_contact_id', $input['invitations'][$key]) && is_string($input['invitations'][$key]['client_contact_id'])) {
|
||||
$input['invitations'][$key]['client_contact_id'] = $this->decodePrimaryKey($input['invitations'][$key]['client_contact_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('vendor_contact_id', $input['invitations'][$key]) && is_string($input['invitations'][$key]['vendor_contact_id'])) {
|
||||
$input['invitations'][$key]['vendor_contact_id'] = $this->decodePrimaryKey($input['invitations'][$key]['vendor_contact_id']);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ class BlackListRule implements Rule
|
||||
'arxxwalls.com',
|
||||
'superhostforumla.com',
|
||||
'wnpop.com',
|
||||
'dataservices.space',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -33,6 +33,15 @@ class VendorMap
|
||||
14 => 'vendor.state',
|
||||
15 => 'vendor.postal_code',
|
||||
16 => 'vendor.country_id',
|
||||
17 => 'contact.first_name',
|
||||
18 => 'contact.last_name',
|
||||
19 => 'contact.email',
|
||||
20 => 'contact.phone',
|
||||
21 => 'contact.custom_value1',
|
||||
22 => 'contact.custom_value2',
|
||||
23 => 'contact.custom_value3',
|
||||
24 => 'contact.custom_value4',
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@ -56,6 +65,14 @@ class VendorMap
|
||||
14 => 'texts.state',
|
||||
15 => 'texts.postal_code',
|
||||
16 => 'texts.country',
|
||||
17 => 'texts.first_name',
|
||||
18 => 'texts.last_name',
|
||||
19 => 'texts.email',
|
||||
20 => 'texts.phone',
|
||||
21 => 'texts.custom_value',
|
||||
22 => 'texts.custom_value',
|
||||
23 => 'texts.custom_value',
|
||||
24 => 'texts.custom_value',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -173,18 +173,18 @@ class BaseImport
|
||||
$is_free_hosted_client = $this->company->account->isFreeHostedClient();
|
||||
$hosted_client_count = $this->company->account->hosted_client_count;
|
||||
|
||||
if($this->factory_name instanceof ClientFactory && $is_free_hosted_client && (count($data) > $hosted_client_count))
|
||||
{
|
||||
$this->error_array[$entity_type][] = [
|
||||
$entity_type => 'client',
|
||||
'error' => 'Error, you are attempting to import more clients than your plan allows',
|
||||
];
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
foreach ($data as $key => $record) {
|
||||
|
||||
if($this->factory_name instanceof ClientFactory && $is_free_hosted_client && ($this->company->clients()->count() > $hosted_client_count))
|
||||
{
|
||||
$this->error_array[$entity_type][] = [
|
||||
$entity_type => $record,
|
||||
'error' => 'Client limit reached',
|
||||
];
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
try {
|
||||
$entity = $this->transformer->transform($record);
|
||||
// $validator = $this->request_name::runFormRequest($entity);
|
||||
|
@ -16,12 +16,14 @@ use App\Factory\ExpenseFactory;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Factory\ProductFactory;
|
||||
use App\Factory\QuoteFactory;
|
||||
use App\Factory\VendorFactory;
|
||||
use App\Http\Requests\Client\StoreClientRequest;
|
||||
use App\Http\Requests\Expense\StoreExpenseRequest;
|
||||
use App\Http\Requests\Invoice\StoreInvoiceRequest;
|
||||
use App\Http\Requests\Payment\StorePaymentRequest;
|
||||
use App\Http\Requests\Product\StoreProductRequest;
|
||||
use App\Http\Requests\Quote\StoreQuoteRequest;
|
||||
use App\Http\Requests\Vendor\StoreVendorRequest;
|
||||
use App\Import\ImportException;
|
||||
use App\Import\Providers\BaseImport;
|
||||
@ -31,12 +33,14 @@ use App\Import\Transformer\Csv\ExpenseTransformer;
|
||||
use App\Import\Transformer\Csv\InvoiceTransformer;
|
||||
use App\Import\Transformer\Csv\PaymentTransformer;
|
||||
use App\Import\Transformer\Csv\ProductTransformer;
|
||||
use App\Import\Transformer\Csv\QuoteTransformer;
|
||||
use App\Import\Transformer\Csv\VendorTransformer;
|
||||
use App\Repositories\ClientRepository;
|
||||
use App\Repositories\ExpenseRepository;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Repositories\PaymentRepository;
|
||||
use App\Repositories\ProductRepository;
|
||||
use App\Repositories\QuoteRepository;
|
||||
use App\Repositories\VendorRepository;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
@ -55,6 +59,7 @@ class Csv extends BaseImport implements ImportInterface
|
||||
'payment',
|
||||
'vendor',
|
||||
'expense',
|
||||
'quote',
|
||||
])
|
||||
) {
|
||||
$this->{$entity}();
|
||||
@ -151,6 +156,35 @@ class Csv extends BaseImport implements ImportInterface
|
||||
$this->entity_count['invoices'] = $invoice_count;
|
||||
}
|
||||
|
||||
public function quote()
|
||||
{
|
||||
$entity_type = 'quote';
|
||||
|
||||
$data = $this->getCsvData($entity_type);
|
||||
|
||||
if (is_array($data)) {
|
||||
$data = $this->preTransformCsv($data, $entity_type);
|
||||
}
|
||||
|
||||
if (empty($data)) {
|
||||
$this->entity_count['quotes'] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->request_name = StoreQuoteRequest::class;
|
||||
$this->repository_name = QuoteRepository::class;
|
||||
$this->factory_name = QuoteFactory::class;
|
||||
|
||||
$this->repository = app()->make($this->repository_name);
|
||||
$this->repository->import_mode = true;
|
||||
|
||||
$this->transformer = new QuoteTransformer($this->company);
|
||||
|
||||
$quote_count = $this->ingestQuotes($data, 'quote.number');
|
||||
|
||||
$this->entity_count['quotes'] = $quote_count;
|
||||
}
|
||||
|
||||
public function payment()
|
||||
{
|
||||
$entity_type = 'payment';
|
||||
@ -241,10 +275,6 @@ class Csv extends BaseImport implements ImportInterface
|
||||
$this->entity_count['expenses'] = $expense_count;
|
||||
}
|
||||
|
||||
public function quote()
|
||||
{
|
||||
}
|
||||
|
||||
public function task()
|
||||
{
|
||||
}
|
||||
|
@ -19,6 +19,14 @@ use App\Models\Country;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\User;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Project;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Client;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\Product;
|
||||
use App\Models\Vendor;
|
||||
use App\Utils\Number;
|
||||
use Exception;
|
||||
use Illuminate\Support\Carbon;
|
||||
@ -67,8 +75,7 @@ class BaseTransformer
|
||||
{
|
||||
|
||||
if (! empty($client_name)) {
|
||||
$client_id_search = $this->company
|
||||
->clients()
|
||||
$client_id_search = Client::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->where('id_number', $client_name);
|
||||
|
||||
@ -76,10 +83,11 @@ class BaseTransformer
|
||||
return $client_id_search->first()->id;
|
||||
}
|
||||
|
||||
$client_name_search = $this->company
|
||||
->clients()
|
||||
$client_name_search = Client::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->where('name', $client_name);
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $client_name)),
|
||||
]);
|
||||
|
||||
if ($client_name_search->count() >= 1) {
|
||||
return $client_name_search->first()->id;
|
||||
@ -108,8 +116,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function hasClient($name)
|
||||
{
|
||||
return $this->company
|
||||
->clients()
|
||||
return Client::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $name)),
|
||||
@ -124,8 +131,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function hasVendor($name)
|
||||
{
|
||||
return $this->company
|
||||
->vendors()
|
||||
return Vendor::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $name)),
|
||||
@ -140,8 +146,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function hasProject($name)
|
||||
{
|
||||
return $this->company
|
||||
->projects()
|
||||
return Project::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $name)),
|
||||
@ -156,8 +161,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function hasProduct($key)
|
||||
{
|
||||
return $this->company
|
||||
->products()
|
||||
return Product::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`product_key`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $key)),
|
||||
@ -174,12 +178,14 @@ class BaseTransformer
|
||||
public function getFloat($data, $field)
|
||||
{
|
||||
if (array_key_exists($field, $data)) {
|
||||
$number = preg_replace('/[^0-9-.]+/', '', $data[$field]);
|
||||
//$number = preg_replace('/[^0-9-.]+/', '', $data[$field]);
|
||||
return Number::parseStringFloat($data[$field]);
|
||||
} else {
|
||||
$number = 0;
|
||||
//$number = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Number::parseFloat($number);
|
||||
// return Number::parseFloat($number);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,8 +195,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function getClientId($name)
|
||||
{
|
||||
$client = $this->company
|
||||
->clients()
|
||||
$client = Client::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $name)),
|
||||
@ -207,8 +212,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function getProduct($key)
|
||||
{
|
||||
$product = $this->company
|
||||
->products()
|
||||
$product = Product::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`product_key`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $key)),
|
||||
@ -225,8 +229,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function getContact($email)
|
||||
{
|
||||
$contact = $this->company
|
||||
->client_contacts()
|
||||
$contact = ClientContact::where('company_id', $this->company->id)
|
||||
->whereRaw("LOWER(REPLACE(`email`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $email)),
|
||||
])
|
||||
@ -278,8 +281,7 @@ class BaseTransformer
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
$tax_rate = $this->company
|
||||
->tax_rates()
|
||||
$tax_rate = TaxRate::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $name)),
|
||||
@ -298,8 +300,7 @@ class BaseTransformer
|
||||
{
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
$tax_rate = $this->company
|
||||
->tax_rates()
|
||||
$tax_rate = TaxRate::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $name)),
|
||||
@ -348,8 +349,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function getInvoiceId($invoice_number)
|
||||
{
|
||||
$invoice = $this->company
|
||||
->invoices()
|
||||
$invoice = Invoice::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $invoice_number)),
|
||||
@ -366,8 +366,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function hasInvoice($invoice_number)
|
||||
{
|
||||
return $this->company
|
||||
->invoices()
|
||||
return Invoice::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $invoice_number)),
|
||||
@ -380,8 +379,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function hasExpense($expense_number)
|
||||
{
|
||||
return $this->company
|
||||
->expenses()
|
||||
return Expense::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $expense_number)),
|
||||
@ -396,8 +394,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function hasQuote($quote_number)
|
||||
{
|
||||
return $this->company
|
||||
->quotes()
|
||||
return Quote::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $quote_number)),
|
||||
@ -412,8 +409,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function getInvoiceClientId($invoice_number)
|
||||
{
|
||||
$invoice = $this->company
|
||||
->invoices()
|
||||
$invoice = Invoice::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`number`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $invoice_number)),
|
||||
@ -430,8 +426,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function getVendorId($name)
|
||||
{
|
||||
$vendor = $this->company
|
||||
->vendors()
|
||||
$vendor = Vendor::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $name)),
|
||||
@ -467,8 +462,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function getExpenseCategoryId($name)
|
||||
{
|
||||
$ec = $this->company
|
||||
->expense_categories()
|
||||
$ec = ExpenseCategory::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $name)),
|
||||
@ -504,8 +498,7 @@ class BaseTransformer
|
||||
*/
|
||||
public function getProjectId($name, $clientId = null)
|
||||
{
|
||||
$project = $this->company
|
||||
->projects()
|
||||
$project = Project::where('company_id', $this->company->id)
|
||||
->where('is_deleted', false)
|
||||
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
|
||||
strtolower(str_replace(' ', '', $name)),
|
||||
|
@ -52,15 +52,42 @@ class VendorTransformer extends BaseTransformer
|
||||
'custom_value2' => $this->getString($data, 'vendor.custom_value2'),
|
||||
'custom_value3' => $this->getString($data, 'vendor.custom_value3'),
|
||||
'custom_value4' => $this->getString($data, 'vendor.custom_value4'),
|
||||
'vendor_contacts' => [
|
||||
// 'vendor_contacts' => [
|
||||
// [
|
||||
// 'first_name' => $this->getString(
|
||||
// $data,
|
||||
// 'vendor.first_name'
|
||||
// ),
|
||||
// 'last_name' => $this->getString($data, 'vendor.last_name'),
|
||||
// 'email' => $this->getString($data, 'vendor.email'),
|
||||
// 'phone' => $this->getString($data, 'vendor.phone'),
|
||||
// ],
|
||||
// ],
|
||||
'contacts' => [
|
||||
[
|
||||
'first_name' => $this->getString(
|
||||
$data,
|
||||
'vendor.first_name'
|
||||
'contact.first_name'
|
||||
),
|
||||
'last_name' => $this->getString($data, 'contact.last_name'),
|
||||
'email' => $this->getString($data, 'contact.email'),
|
||||
'phone' => $this->getString($data, 'contact.phone'),
|
||||
'custom_value1' => $this->getString(
|
||||
$data,
|
||||
'contact.custom_value1'
|
||||
),
|
||||
'custom_value2' => $this->getString(
|
||||
$data,
|
||||
'contact.custom_value2'
|
||||
),
|
||||
'custom_value3' => $this->getString(
|
||||
$data,
|
||||
'contact.custom_value3'
|
||||
),
|
||||
'custom_value4' => $this->getString(
|
||||
$data,
|
||||
'contact.custom_value4'
|
||||
),
|
||||
'last_name' => $this->getString($data, 'vendor.last_name'),
|
||||
'email' => $this->getString($data, 'vendor.email'),
|
||||
'phone' => $this->getString($data, 'vendor.phone'),
|
||||
],
|
||||
],
|
||||
'country_id' => isset($data['vendor.country_id'])
|
||||
|
@ -288,7 +288,7 @@ class CompanyImport implements ShouldQueue
|
||||
$nmo->company = $_company;
|
||||
$nmo->settings = $_company->settings;
|
||||
$nmo->to_user = $_company->owner();
|
||||
NinjaMailerJob::dispatchNow($nmo);
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
}
|
||||
catch(\Exception $e){
|
||||
@ -1644,7 +1644,7 @@ class CompanyImport implements ShouldQueue
|
||||
$nmo->company = $this->company;
|
||||
$nmo->settings = $this->company->settings;
|
||||
$nmo->to_user = $this->company->owner();
|
||||
NinjaMailerJob::dispatchNow($nmo);
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
}
|
||||
}
|
@ -64,7 +64,8 @@ class CreateCompany
|
||||
$company->custom_fields = new \stdClass;
|
||||
$company->default_password_timeout = 1800000;
|
||||
$company->client_registration_fields = ClientRegistrationFields::generate();
|
||||
$company->markdown_email_enabled = false;
|
||||
$company->markdown_email_enabled = true;
|
||||
$company->markdown_enabled = false;
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
$company->subdomain = MultiDB::randomSubdomainGenerator();
|
||||
|
@ -13,6 +13,7 @@ namespace App\Jobs\Cron;
|
||||
|
||||
use App\Jobs\RecurringInvoice\SendRecurring;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Support\Carbon;
|
||||
@ -44,12 +45,12 @@ class RecurringInvoicesCron
|
||||
nlog('Sending recurring invoices '.$start);
|
||||
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
$recurring_invoices = RecurringInvoice::where('next_send_date', '<=', now()->toDateTimeString())
|
||||
$recurring_invoices = RecurringInvoice::where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('is_deleted', false)
|
||||
->where('remaining_cycles', '!=', '0')
|
||||
->whereNotNull('next_send_date')
|
||||
->whereNull('deleted_at')
|
||||
->where('is_deleted', false)
|
||||
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('remaining_cycles', '!=', '0')
|
||||
->where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereHas('client', function ($query) {
|
||||
$query->where('is_deleted', 0)
|
||||
->where('deleted_at', null);
|
||||
@ -84,12 +85,12 @@ class RecurringInvoicesCron
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$recurring_invoices = RecurringInvoice::where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereNotNull('next_send_date')
|
||||
->whereNull('deleted_at')
|
||||
$recurring_invoices = RecurringInvoice::where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('is_deleted', false)
|
||||
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('remaining_cycles', '!=', '0')
|
||||
->whereNull('deleted_at')
|
||||
->whereNotNull('next_send_date')
|
||||
->where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereHas('client', function ($query) {
|
||||
$query->where('is_deleted', 0)
|
||||
->where('deleted_at', null);
|
||||
@ -107,7 +108,7 @@ class RecurringInvoicesCron
|
||||
nlog("Trying to send {$recurring_invoice->number}");
|
||||
|
||||
if ($recurring_invoice->company->stop_on_unpaid_recurring) {
|
||||
if ($recurring_invoice->invoices()->whereIn('status_id', [2, 3])->where('is_deleted', 0)->where('balance', '>', 0)->exists()) {
|
||||
if (Invoice::where('recurring_id', $recurring_invoice->id)->whereIn('status_id', [2, 3])->where('is_deleted', 0)->where('balance', '>', 0)->exists()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ class CSVIngest implements ShouldQueue
|
||||
|
||||
$engine = $this->bootEngine();
|
||||
|
||||
foreach (['client', 'product', 'invoice', 'payment', 'vendor', 'expense'] as $entity) {
|
||||
foreach (['client', 'product', 'invoice', 'payment', 'vendor', 'expense', 'quote'] as $entity) {
|
||||
$engine->import($entity);
|
||||
}
|
||||
|
||||
|
@ -80,9 +80,9 @@ class AdjustProductInventory implements ShouldQueue
|
||||
$p->in_stock_quantity -= $item->quantity;
|
||||
$p->saveQuietly();
|
||||
|
||||
if ($p->stock_notification_threshold && $p->in_stock_quantity <= $p->stock_notification_threshold) {
|
||||
if ($this->company->stock_notification && $p->stock_notification && $p->stock_notification_threshold && $p->in_stock_quantity <= $p->stock_notification_threshold) {
|
||||
$this->notifyStockLevels($p, 'product');
|
||||
} elseif ($this->company->stock_notification_threshold && $p->in_stock_quantity <= $this->company->stock_notification_threshold) {
|
||||
} elseif ($this->company->stock_notification && $p->stock_notification && $this->company->inventory_notification_threshold && $p->in_stock_quantity <= $this->company->inventory_notification_threshold) {
|
||||
$this->notifyStocklevels($p, 'company');
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ class NinjaMailerJob implements ShouldQueue
|
||||
->send($this->nmo->mailable);
|
||||
|
||||
LightLogs::create(new EmailSuccess($this->nmo->company->company_key))
|
||||
->queue();
|
||||
->batch();
|
||||
|
||||
/* Count the amount of emails sent across all the users accounts */
|
||||
Cache::increment($this->company->account->key);
|
||||
@ -354,14 +354,19 @@ class NinjaMailerJob implements ShouldQueue
|
||||
if(!str_contains($this->nmo->to_user->email, "@"))
|
||||
return true;
|
||||
|
||||
/* On the hosted platform if the user has not verified their account we fail here - but still check what they are trying to send! */
|
||||
if(Ninja::isHosted() && $this->company->account && !$this->company->account->account_sms_verified){
|
||||
|
||||
if(class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class))
|
||||
return (new \Modules\Admin\Jobs\Account\EmailQuality($this->nmo, $this->company))->run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* On the hosted platform we actively scan all outbound emails to ensure outbound email quality remains high */
|
||||
if(class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class))
|
||||
return (new \Modules\Admin\Jobs\Account\EmailQuality($this->nmo, $this->company))->run();
|
||||
|
||||
/* On the hosted platform if the user has not verified their account we fail here */
|
||||
if(Ninja::isHosted() && $this->company->account && !$this->company->account->account_sms_verified)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Jobs\Ninja;
|
||||
|
||||
use App\DataMapper\Analytics\EmailCount;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Utils\Ninja;
|
||||
@ -20,6 +21,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
|
||||
class AdjustEmailQuota implements ShouldQueue
|
||||
{
|
||||
@ -58,8 +60,15 @@ class AdjustEmailQuota implements ShouldQueue
|
||||
{
|
||||
Account::query()->cursor()->each(function ($account) {
|
||||
nlog("resetting email quota for {$account->key}");
|
||||
|
||||
$email_count = Cache::get($account->key);
|
||||
|
||||
if($email_count > 0)
|
||||
LightLogs::create(new EmailCount($email_count, $account->key))->batch();
|
||||
|
||||
Cache::forget($account->key);
|
||||
Cache::forget("throttle_notified:{$account->key}");
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -52,8 +52,6 @@ class CheckCompanyData implements ShouldQueue
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
@ -110,7 +108,7 @@ class CheckCompanyData implements ShouldQueue
|
||||
if ($ledger && number_format($invoice_balance, 4) != number_format($client->balance, 4)) {
|
||||
$wrong_balances++;
|
||||
|
||||
$this->company_data[] = "# {$client->id} ".$client->present()->name.' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger->balance} ";
|
||||
$this->company_data[] = "# {$client->id} ".$client->present()->name().' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger->balance} ";
|
||||
|
||||
$this->is_valid = false;
|
||||
}
|
||||
@ -136,7 +134,7 @@ class CheckCompanyData implements ShouldQueue
|
||||
if ((string) $total_paid != (string) ($invoice->amount - $invoice->balance - $total_credit)) {
|
||||
$wrong_balances++;
|
||||
|
||||
$this->company_data[] = $client->present()->name.' - '.$client->id." - Total Amount = {$total_amount} != Calculated Total = {$calculated_paid_amount} - Total Refund = {$total_refund} Total credit = {$total_credit}";
|
||||
$this->company_data[] = $client->present()->name().' - '.$client->id." - Total Amount = {$total_amount} != Calculated Total = {$calculated_paid_amount} - Total Refund = {$total_refund} Total credit = {$total_credit}";
|
||||
|
||||
$this->is_valid = false;
|
||||
}
|
||||
@ -175,7 +173,7 @@ class CheckCompanyData implements ShouldQueue
|
||||
if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) {
|
||||
$wrong_paid_to_dates++;
|
||||
|
||||
$this->company_data[] = $client->present()->name.'id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}";
|
||||
$this->company_data[] = $client->present()->name().'id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}";
|
||||
|
||||
$this->is_valid = false;
|
||||
}
|
||||
@ -204,7 +202,7 @@ class CheckCompanyData implements ShouldQueue
|
||||
if ($ledger && (string) $invoice_balance != (string) $client->balance) {
|
||||
$wrong_paid_to_dates++;
|
||||
|
||||
$this->company_data[] = $client->present()->name.' - '.$client->id." - calculated client balances do not match {$invoice_balance} - ".rtrim($client->balance, '0').'';
|
||||
$this->company_data[] = $client->present()->name().' - '.$client->id." - calculated client balances do not match {$invoice_balance} - ".rtrim($client->balance, '0').'';
|
||||
|
||||
$this->is_valid = false;
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ class ProcessPostmarkWebhook implements ShouldQueue
|
||||
$this->request['MessageID']
|
||||
);
|
||||
|
||||
LightLogs::create($bounce)->queue();
|
||||
LightLogs::create($bounce)->batch();
|
||||
|
||||
SystemLogger::dispatch($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
||||
|
||||
@ -263,7 +263,7 @@ class ProcessPostmarkWebhook implements ShouldQueue
|
||||
$this->request['MessageID']
|
||||
);
|
||||
|
||||
LightLogs::create($spam)->queue();
|
||||
LightLogs::create($spam)->batch();
|
||||
|
||||
SystemLogger::dispatch($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
||||
|
||||
|
@ -73,8 +73,11 @@ class SendRecurring implements ShouldQueue
|
||||
$invoice->auto_bill_enabled = false;
|
||||
}
|
||||
|
||||
$invoice->date = now()->format('Y-m-d');
|
||||
$invoice->due_date = $this->recurring_invoice->calculateDueDate(now()->format('Y-m-d'));
|
||||
$invoice->date = date('Y-m-d');
|
||||
|
||||
nlog("Recurring Invoice Date Set on Invoice = {$invoice->date} - ". now()->format('Y-m-d'));
|
||||
|
||||
$invoice->due_date = $this->recurring_invoice->calculateDueDate(date('Y-m-d'));
|
||||
$invoice->recurring_id = $this->recurring_invoice->id;
|
||||
$invoice->saveQuietly();
|
||||
|
||||
@ -108,9 +111,9 @@ class SendRecurring implements ShouldQueue
|
||||
$this->recurring_invoice->setCompleted();
|
||||
}
|
||||
|
||||
// nlog('next send date = '.$this->recurring_invoice->next_send_date);
|
||||
nlog('next send date = '.$this->recurring_invoice->next_send_date);
|
||||
// nlog('remaining cycles = '.$this->recurring_invoice->remaining_cycles);
|
||||
// nlog('last send date = '.$this->recurring_invoice->last_sent_date);
|
||||
nlog('last send date = '.$this->recurring_invoice->last_sent_date);
|
||||
|
||||
$this->recurring_invoice->save();
|
||||
|
||||
|
@ -252,7 +252,7 @@ class Import implements ShouldQueue
|
||||
$this->setInitialCompanyLedgerBalances();
|
||||
|
||||
// $this->fixClientBalances();
|
||||
$check_data = CheckCompanyData::dispatchNow($this->company, md5(time()));
|
||||
$check_data = (new CheckCompanyData($this->company, md5(time())))->handle();
|
||||
|
||||
// if(Ninja::isHosted() && array_key_exists('ninja_tokens', $data))
|
||||
$this->processNinjaTokens($data['ninja_tokens']);
|
||||
@ -591,7 +591,7 @@ class Import implements ShouldQueue
|
||||
|
||||
$user_agent = array_key_exists('token_name', $resource) ?: request()->server('HTTP_USER_AGENT');
|
||||
|
||||
CreateCompanyToken::dispatchNow($this->company, $user, $user_agent);
|
||||
(new CreateCompanyToken($this->company, $user, $user_agent))->handle();
|
||||
|
||||
$key = "users_{$resource['id']}";
|
||||
|
||||
@ -1899,7 +1899,7 @@ class Import implements ShouldQueue
|
||||
if(Ninja::isHosted()){
|
||||
|
||||
try{
|
||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow($data, $this->company);
|
||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatch($data, $this->company);
|
||||
}
|
||||
catch(\Exception $e){
|
||||
nlog($e->getMessage());
|
||||
|
@ -14,8 +14,10 @@ namespace App\Jobs\Util;
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\Events\Invoice\InvoiceWasEmailed;
|
||||
use App\Jobs\Entity\EmailEntity;
|
||||
use App\Jobs\Ninja\TransactionLog;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\TransactionEvent;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
@ -60,11 +62,11 @@ class ReminderJob implements ShouldQueue
|
||||
{
|
||||
nlog('Sending invoice reminders '.now()->format('Y-m-d h:i:s'));
|
||||
|
||||
Invoice::where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereNull('deleted_at')
|
||||
->where('is_deleted', 0)
|
||||
Invoice::where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->whereNull('deleted_at')
|
||||
->where('balance', '>', 0)
|
||||
->where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereHas('client', function ($query) {
|
||||
$query->where('is_deleted', 0)
|
||||
->where('deleted_at', null);
|
||||
@ -201,10 +203,20 @@ class ReminderJob implements ShouldQueue
|
||||
$client = $invoice->client;
|
||||
$client = $client->fresh();
|
||||
|
||||
nlog('adjusting client balance and invoice balance by '.($invoice->balance - $temp_invoice_balance));
|
||||
nlog('adjusting client balance and invoice balance by #'.$invoice->number.' '.($invoice->balance - $temp_invoice_balance));
|
||||
$client->service()->updateBalance($invoice->balance - $temp_invoice_balance)->save();
|
||||
$invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}");
|
||||
|
||||
$transaction = [
|
||||
'invoice' => $invoice->transaction_event(),
|
||||
'payment' => [],
|
||||
'client' => $client->transaction_event(),
|
||||
'credit' => [],
|
||||
'metadata' => ['setLateFee'],
|
||||
];
|
||||
|
||||
TransactionLog::dispatch(TransactionEvent::CLIENT_STATUS, $transaction, $invoice->company->db);
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
}
|
||||
|
@ -329,6 +329,24 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByInappTransactionId($transaction_id) :bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
foreach (self::$dbs as $db) {
|
||||
if (Account::on($db)->where('inapp_transaction_id', $transaction_id)->exists()) {
|
||||
self::setDb($db);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
self::setDB($current_db);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static function findAndSetDbByContactKey($contact_key) :bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
58
app/Listeners/Subscription/AppStoreRenewSubscription.php
Normal file
58
app/Listeners/Subscription/AppStoreRenewSubscription.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\Subscription;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Imdhemy\Purchases\Events\AppStore\DidRenew;
|
||||
|
||||
class AppStoreRenewSubscription implements ShouldQueue
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(DidRenew $event)
|
||||
{
|
||||
|
||||
$inapp_transaction_id = $event->getSubscriptionId(); //$subscription_id
|
||||
|
||||
MultiDB::findAndSetDbByInappTransactionId($inapp_transaction_id);
|
||||
|
||||
$account = Account::where('inapp_transaction_id', $inapp_transaction_id)->first();
|
||||
|
||||
if($account->plan_term == 'month')
|
||||
$account->plan_expires = now()->addMonth();
|
||||
elseif($account->plan_term == 'year')
|
||||
$account->plan_expires = now()->addYear();
|
||||
|
||||
$account->save();
|
||||
|
||||
// $server_notification = $event->getServerNotification();
|
||||
// $subscription = $event->getSubscription();
|
||||
// $subscription_identifier = $event->getSubscriptionIdentifier();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -112,7 +112,7 @@ class ClientPaymentFailureObject
|
||||
'invoice' => $this->invoices->first()->number,
|
||||
]
|
||||
),
|
||||
'greeting' => ctrans('texts.email_salutation', ['name' => $this->client->present()->name]),
|
||||
'greeting' => ctrans('texts.email_salutation', ['name' => $this->client->present()->name()]),
|
||||
'content' => ctrans('texts.client_payment_failure_body', ['invoice' => implode(',', $this->invoices->pluck('number')->toArray()), 'amount' => $this->getAmount()]),
|
||||
'signature' => $signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
|
@ -222,6 +222,7 @@ class PaymentEmailEngine extends BaseEmailEngine
|
||||
|
||||
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->payment->getLink().'">'.ctrans('texts.view_payment').'</a>', 'label' => ctrans('texts.view_payment')];
|
||||
$data['$view_button'] = &$data['$view_link'];
|
||||
$data['$viewButton'] = &$data['$view_link'];
|
||||
$data['$viewLink'] = &$data['$view_link'];
|
||||
$data['$paymentLink'] = &$data['$view_link'];
|
||||
$data['$portalButton'] = ['value' => "<a href='{$this->payment->getPortalLink()}'>".ctrans('texts.login').'</a>', 'label' =>''];
|
||||
@ -237,6 +238,10 @@ class PaymentEmailEngine extends BaseEmailEngine
|
||||
$data['$invoice.po_number'] = ['value' => $this->formatPoNumber(), 'label' => ctrans('texts.po_number')];
|
||||
$data['$poNumber'] = &$data['$invoice.po_number'];
|
||||
$data['$payment.status'] = ['value' => $this->payment->stringStatus($this->payment->status_id), 'label' => ctrans('texts.payment_status')];
|
||||
$data['$invoices.amount'] = ['value' => $this->formatInvoiceField('amount'), 'label' => ctrans('texts.invoices')];
|
||||
$data['$invoices.balance'] = ['value' => $this->formatInvoiceField('balance'), 'label' => ctrans('texts.invoices')];
|
||||
$data['$invoices.due_date'] = ['value' => $this->formatInvoiceField('due_date'), 'label' => ctrans('texts.invoices')];
|
||||
$data['$invoices.po_number'] = ['value' => $this->formatInvoiceField('po_number'), 'label' => ctrans('texts.invoices')];
|
||||
|
||||
$arrKeysLength = array_map('strlen', array_keys($data));
|
||||
array_multisort($arrKeysLength, SORT_DESC, $data);
|
||||
@ -244,6 +249,22 @@ class PaymentEmailEngine extends BaseEmailEngine
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function formatInvoiceField($field)
|
||||
{
|
||||
$invoice = '';
|
||||
|
||||
foreach ($this->payment->invoices as $invoice) {
|
||||
|
||||
$invoice_field = $invoice->{$field};
|
||||
|
||||
$invoice .= ctrans('texts.invoice_number_short') . "{$invoice->number} {$invoice_field}";
|
||||
|
||||
}
|
||||
|
||||
return $invoice;
|
||||
|
||||
}
|
||||
|
||||
private function formatInvoice()
|
||||
{
|
||||
$invoice = '';
|
||||
@ -282,11 +303,15 @@ class PaymentEmailEngine extends BaseEmailEngine
|
||||
$invoice_list = '<br><br>';
|
||||
|
||||
foreach ($this->payment->invoices as $invoice) {
|
||||
$invoice_list .= ctrans('texts.po_number')." {$invoice->po_number} <br>";
|
||||
|
||||
if(strlen($invoice->po_number) > 1)
|
||||
$invoice_list .= ctrans('texts.po_number')." {$invoice->po_number} <br>";
|
||||
|
||||
$invoice_list .= ctrans('texts.invoice_number_short')." {$invoice->number} <br>";
|
||||
$invoice_list .= ctrans('texts.invoice_amount').' '.Number::formatMoney($invoice->pivot->amount, $this->client).'<br>';
|
||||
$invoice_list .= ctrans('texts.invoice_balance').' '.Number::formatMoney($invoice->fresh()->balance, $this->client).'<br>';
|
||||
$invoice_list .= '-----<br>';
|
||||
|
||||
}
|
||||
|
||||
return $invoice_list;
|
||||
|
@ -70,8 +70,13 @@ class SupportMessageSent extends Mailable
|
||||
$trial = $account->isTrial() ? 'T' : '';
|
||||
$plan = str_replace('_', ' ', $plan);
|
||||
|
||||
$plan_status = '';
|
||||
|
||||
if(Carbon::parse($account->plan_expires)->lt(now()))
|
||||
$plan_status = 'Plan Expired';
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
$subject = "{$priority}Hosted-{$db}-{$is_large}{$platform}{$migrated}{$trial} :: {$plan} :: ".date('M jS, g:ia');
|
||||
$subject = "{$priority}Hosted-{$db}-{$is_large}{$platform}{$migrated}{$trial} :: {$plan} :: {$plan_status} ".date('M jS, g:ia');
|
||||
} else {
|
||||
$subject = "{$priority}Self Hosted :: {$plan} :: {$is_large}{$platform}{$migrated} :: ".date('M jS, g:ia');
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ class VendorTemplateEmail extends Mailable
|
||||
}
|
||||
|
||||
if ($this->build_email->getTemplate() == 'custom') {
|
||||
$this->build_email->setBody(str_replace('$body', $this->build_email->getBody(), $this->client->getSetting('email_style_custom')));
|
||||
$this->build_email->setBody(str_replace('$body', $this->build_email->getBody(), $this->company->getSetting('email_style_custom')));
|
||||
}
|
||||
|
||||
$settings = $this->company->settings;
|
||||
|
@ -266,6 +266,11 @@ class Activity extends StaticModel
|
||||
return $this->belongsTo(Invoice::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function vendor()
|
||||
{
|
||||
return $this->belongsTo(Vendor::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
|
@ -121,6 +121,7 @@ class Company extends BaseModel
|
||||
'stock_notification',
|
||||
'enabled_expense_tax_rates',
|
||||
'invoice_task_project',
|
||||
'report_include_deleted',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
|
@ -212,14 +212,14 @@ class PurchaseOrder extends BaseModel
|
||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||
}
|
||||
elseif(Ninja::isHosted() && $portal){
|
||||
$file_path = CreatePurchaseOrderPdf::dispatchNow($invitation,config('filesystems.default'));
|
||||
$file_path = (new CreatePurchaseOrderPdf($invitation,config('filesystems.default')))->handle();
|
||||
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||
}
|
||||
|
||||
if(Storage::disk('public')->exists($file_path))
|
||||
return Storage::disk('public')->{$type}($file_path);
|
||||
|
||||
$file_path = CreatePurchaseOrderPdf::dispatchNow($invitation);
|
||||
$file_path = (new CreatePurchaseOrderPdf($invitation))->handle();
|
||||
return Storage::disk('public')->{$type}($file_path);
|
||||
}
|
||||
|
||||
|
@ -560,7 +560,7 @@ class RecurringInvoice extends BaseModel
|
||||
break;
|
||||
|
||||
case 'on_receipt':
|
||||
return Carbon::Parse($date)->copy();
|
||||
return Carbon::parse($date)->copy();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -64,7 +64,7 @@ class Vendor extends BaseModel
|
||||
protected $touches = [];
|
||||
|
||||
protected $with = [
|
||||
'company',
|
||||
'contacts.company',
|
||||
];
|
||||
|
||||
protected $presenter = VendorPresenter::class;
|
||||
|
@ -45,10 +45,7 @@ class VendorContact extends Authenticatable implements HasLocalePreference
|
||||
'hashed_id',
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
// 'vendor',
|
||||
// 'company'
|
||||
];
|
||||
protected $with = [];
|
||||
|
||||
protected $casts = [
|
||||
'updated_at' => 'timestamp',
|
||||
|
@ -110,17 +110,17 @@ class BraintreePaymentDriver extends BaseDriver
|
||||
}
|
||||
|
||||
$result = $this->gateway->customer()->create([
|
||||
'firstName' => $this->client->present()->name,
|
||||
'email' => $this->client->present()->email,
|
||||
'phone' => $this->client->present()->phone,
|
||||
'firstName' => $this->client->present()->name(),
|
||||
'email' => $this->client->present()->email(),
|
||||
'phone' => $this->client->present()->phone(),
|
||||
]);
|
||||
|
||||
if ($result->success) {
|
||||
$address = $this->gateway->address()->create([
|
||||
'customerId' => $result->customer->id,
|
||||
'firstName' => $this->client->present()->name,
|
||||
'streetAddress' => $this->client->address1,
|
||||
'postalCode' => $this->client->postal_code,
|
||||
'firstName' => $this->client->present()->name(),
|
||||
'streetAddress' => $this->client->address1 ?: '',
|
||||
'postalCode' => $this->client->postal_code ?: '',
|
||||
'countryCodeAlpha2' => $this->client->country ? $this->client->country->iso_3166_2 : '',
|
||||
]);
|
||||
|
||||
|
@ -261,7 +261,7 @@ class GoCardlessPaymentDriver extends BaseDriver
|
||||
//finalize payments on invoices here.
|
||||
}
|
||||
|
||||
if ($event['action'] === 'failed') {
|
||||
if ($event['action'] === 'failed' && array_key_exists('payment', $event['links'])) {
|
||||
$payment = Payment::query()
|
||||
->where('transaction_reference', $event['links']['payment'])
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
|
@ -81,7 +81,7 @@ class CreditCard implements MethodInterface
|
||||
$client->addressLines = [$this->square_driver->client->address1 ?: '', $this->square_driver->client->address2 ?: ''];
|
||||
$client->givenName = $this->square_driver->client->present()->first_name();
|
||||
$client->familyName = $this->square_driver->client->present()->last_name();
|
||||
$client->email = $this->square_driver->client->present()->email;
|
||||
$client->email = $this->square_driver->client->present()->email();
|
||||
$client->phone = $this->square_driver->client->phone;
|
||||
$client->city = $this->square_driver->client->city;
|
||||
$client->region = $this->square_driver->client->state;
|
||||
|
@ -74,6 +74,9 @@ class UpdatePaymentMethods
|
||||
{
|
||||
$sources = $customer->sources;
|
||||
|
||||
if(!property_exists($sources, 'data'))
|
||||
return;
|
||||
|
||||
foreach ($sources->data as $method) {
|
||||
$token_exists = ClientGatewayToken::where([
|
||||
'gateway_customer_reference' => $customer->id,
|
||||
|
@ -28,10 +28,21 @@ class ClientContactRepository extends BaseRepository
|
||||
|
||||
public function save(array $data, Client $client) : void
|
||||
{
|
||||
if (isset($data['contacts'])) {
|
||||
//06-09-2022 sometimes users pass a contact object instead of a nested array, this sequence handles this scenario
|
||||
if (isset($data['contacts']) && (count($data['contacts']) !== count($data['contacts'], COUNT_RECURSIVE))) {
|
||||
|
||||
$contacts = collect($data['contacts']);
|
||||
} else {
|
||||
|
||||
} elseif(isset($data['contacts'])){
|
||||
|
||||
$temp_array[] = $data['contacts'];
|
||||
$contacts = collect($temp_array);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
$contacts = collect();
|
||||
|
||||
}
|
||||
|
||||
$client->contacts->pluck('id')->diff($contacts->pluck('id'))->each(function ($contact) {
|
||||
@ -45,6 +56,7 @@ class ClientContactRepository extends BaseRepository
|
||||
|
||||
/* Set first record to primary - always */
|
||||
$contacts = $contacts->sortByDesc('is_primary')->map(function ($contact) {
|
||||
|
||||
$contact['is_primary'] = $this->is_primary;
|
||||
$this->is_primary = false;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
@ -16,6 +17,7 @@ use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||
use App\Models\Expense;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Database\QueryException;
|
||||
|
||||
/**
|
||||
* ExpenseRepository.
|
||||
@ -24,6 +26,8 @@ class ExpenseRepository extends BaseRepository
|
||||
{
|
||||
use GeneratesCounter;
|
||||
|
||||
private $completed = true;
|
||||
|
||||
/**
|
||||
* Saves the expense and its contacts.
|
||||
*
|
||||
@ -32,15 +36,17 @@ class ExpenseRepository extends BaseRepository
|
||||
*
|
||||
* @return \App\Models\Expense|null expense Object
|
||||
*/
|
||||
public function save(array $data, Expense $expense) : ?Expense
|
||||
public function save(array $data, Expense $expense): ?Expense
|
||||
{
|
||||
$expense->fill($data);
|
||||
|
||||
if (! $expense->id) {
|
||||
if (!$expense->id) {
|
||||
$expense = $this->processExchangeRates($data, $expense);
|
||||
}
|
||||
|
||||
$expense->number = empty($expense->number) ? $this->getNextExpenseNumber($expense) : $expense->number;
|
||||
if (empty($expense->number))
|
||||
$expense = $this->findAndSaveNumber($expense);
|
||||
|
||||
$expense->save();
|
||||
|
||||
if (array_key_exists('documents', $data)) {
|
||||
@ -54,6 +60,7 @@ class ExpenseRepository extends BaseRepository
|
||||
* Store expenses in bulk.
|
||||
*
|
||||
* @param array $expense
|
||||
*
|
||||
* @return \App\Models\Expense|null
|
||||
*/
|
||||
public function create($expense): ?Expense
|
||||
@ -64,7 +71,7 @@ class ExpenseRepository extends BaseRepository
|
||||
);
|
||||
}
|
||||
|
||||
public function processExchangeRates($data, $expense)
|
||||
public function processExchangeRates($data, $expense): Expense
|
||||
{
|
||||
if (array_key_exists('exchange_rate', $data) && isset($data['exchange_rate']) && $data['exchange_rate'] != 1) {
|
||||
return $expense;
|
||||
@ -83,4 +90,35 @@ class ExpenseRepository extends BaseRepository
|
||||
|
||||
return $expense;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle race conditions when creating expense numbers
|
||||
*
|
||||
* @param Expense $expense
|
||||
* @return \App\Models\Expense
|
||||
*/
|
||||
private function findAndSaveNumber($expense): Expense
|
||||
{
|
||||
|
||||
$x = 1;
|
||||
|
||||
do {
|
||||
|
||||
try {
|
||||
|
||||
$expense->number = $this->getNextExpenseNumber($expense);
|
||||
$expense->saveQuietly();
|
||||
|
||||
$this->completed = false;
|
||||
} catch (QueryException $e) {
|
||||
|
||||
$x++;
|
||||
|
||||
if ($x > 50)
|
||||
$this->completed = false;
|
||||
}
|
||||
} while ($this->completed);
|
||||
|
||||
return $expense;
|
||||
}
|
||||
}
|
||||
|
@ -84,23 +84,23 @@ class PaymentRepository extends BaseRepository {
|
||||
$data['amount'] = array_sum(array_column($data['invoices'], 'amount'));
|
||||
}
|
||||
|
||||
// $client->service()->updatePaidToDate($data['amount'])->save();
|
||||
$client->paid_to_date += $data['amount'];
|
||||
$client->service()->updatePaidToDate($data['amount'])->save();
|
||||
// $client->paid_to_date += $data['amount'];
|
||||
$client->save();
|
||||
}
|
||||
|
||||
else{
|
||||
//this fixes an edge case with unapplied payments
|
||||
// $client->service()->updatePaidToDate($data['amount'])->save();
|
||||
$client->paid_to_date += $data['amount'];
|
||||
$client->service()->updatePaidToDate($data['amount'])->save();
|
||||
// $client->paid_to_date += $data['amount'];
|
||||
$client->save();
|
||||
}
|
||||
|
||||
if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) {
|
||||
$_credit_totals = array_sum(array_column($data['credits'], 'amount'));
|
||||
|
||||
// $client->service()->updatePaidToDate($_credit_totals)->save();
|
||||
$client->paid_to_date += $_credit_totals;
|
||||
$client->service()->updatePaidToDate($_credit_totals)->save();
|
||||
// $client->paid_to_date += $_credit_totals;
|
||||
$client->save();
|
||||
}
|
||||
|
||||
@ -131,7 +131,8 @@ class PaymentRepository extends BaseRepository {
|
||||
|
||||
/*Ensure payment number generated*/
|
||||
if (! $payment->number || strlen($payment->number) == 0) {
|
||||
$payment->number = $payment->client->getNextPaymentNumber($payment->client, $payment);
|
||||
// $payment->number = $payment->client->getNextPaymentNumber($payment->client, $payment);
|
||||
$payment->service()->applyNumber();
|
||||
}
|
||||
|
||||
/*Set local total variables*/
|
||||
|
@ -62,7 +62,9 @@ class UserRepository extends BaseRepository
|
||||
// $account->num_users++;
|
||||
// $account->save();
|
||||
// }
|
||||
|
||||
if(array_key_exists('oauth_provider_id', $details))
|
||||
unset($details['oauth_provider_id']);
|
||||
|
||||
$user->fill($details);
|
||||
|
||||
//allow users to change only their passwords - not others!
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Services\Client;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Credit;
|
||||
use App\Services\Client\Merge;
|
||||
use App\Services\Client\PaymentMethod;
|
||||
use App\Utils\Number;
|
||||
@ -28,14 +29,47 @@ class ClientService
|
||||
|
||||
public function updateBalance(float $amount)
|
||||
{
|
||||
$this->client->balance += $amount;
|
||||
// $this->client->balance += $amount;
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () use($amount) {
|
||||
|
||||
$this->client = Client::where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$this->client->balance += $amount;
|
||||
$this->client->save();
|
||||
|
||||
}, 2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function updateBalanceAndPaidToDate(float $balance, float $paid_to_date)
|
||||
{
|
||||
// $this->client->balance += $amount;
|
||||
// $this->client->paid_to_date += $amount;
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () use($balance, $paid_to_date) {
|
||||
|
||||
$this->client = Client::where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$this->client->balance += $balance;
|
||||
$this->client->paid_to_date += $paid_to_date;
|
||||
$this->client->save();
|
||||
|
||||
}, 2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function updatePaidToDate(float $amount)
|
||||
{
|
||||
$this->client->paid_to_date += $amount;
|
||||
// $this->client->paid_to_date += $amount;
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () use($amount) {
|
||||
|
||||
$this->client = Client::where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$this->client->paid_to_date += $amount;
|
||||
$this->client->save();
|
||||
|
||||
}, 2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -49,7 +83,7 @@ class ClientService
|
||||
|
||||
public function getCreditBalance() :float
|
||||
{
|
||||
$credits = $this->client->credits()
|
||||
$credits = Credit::where('client_id', $this->client->id)
|
||||
->where('is_deleted', false)
|
||||
->where('balance', '>', 0)
|
||||
->where(function ($query) {
|
||||
@ -63,7 +97,7 @@ class ClientService
|
||||
|
||||
public function getCredits()
|
||||
{
|
||||
return $this->client->credits()
|
||||
return Credit::where('client_id', $this->client->id)
|
||||
->where('is_deleted', false)
|
||||
->where('balance', '>', 0)
|
||||
->where(function ($query) {
|
||||
|
@ -59,7 +59,7 @@ class ApplyNumber extends AbstractService
|
||||
} catch (QueryException $e) {
|
||||
$x++;
|
||||
|
||||
if ($x > 10) {
|
||||
if ($x > 50) {
|
||||
$this->completed = false;
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ class ApplyNumber extends AbstractService
|
||||
|
||||
$x++;
|
||||
|
||||
if($x>10)
|
||||
if($x>50)
|
||||
$this->completed = false;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ use App\Factory\PaymentFactory;
|
||||
use App\Jobs\Invoice\InvoiceWorkflowSettings;
|
||||
use App\Jobs\Payment\EmailPayment;
|
||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Services\AbstractService;
|
||||
@ -42,7 +43,7 @@ class ApplyPaymentAmount extends AbstractService
|
||||
public function run()
|
||||
{
|
||||
if ($this->invoice->status_id == Invoice::STATUS_DRAFT) {
|
||||
$this->invoice->service()->markSent()->save();
|
||||
$this->invoice = $this->invoice->service()->markSent()->save();
|
||||
}
|
||||
|
||||
/*Don't double pay*/
|
||||
@ -88,10 +89,10 @@ class ApplyPaymentAmount extends AbstractService
|
||||
$this->invoice
|
||||
->client
|
||||
->service()
|
||||
->updateBalance($payment->amount * -1)
|
||||
->updatePaidToDate($payment->amount)
|
||||
->updateBalanceAndPaidToDate($payment->amount * -1, $payment->amount)
|
||||
->save();
|
||||
|
||||
|
||||
if ($this->invoice->client->getSetting('client_manual_payment_notification')) {
|
||||
$payment->service()->sendEmail();
|
||||
}
|
||||
|
@ -35,6 +35,9 @@ class AutoBillInvoice extends AbstractService
|
||||
|
||||
protected $db;
|
||||
|
||||
/*Specific variable for partial payments */
|
||||
private bool $is_partial_amount = false;
|
||||
|
||||
public function __construct(Invoice $invoice, $db)
|
||||
{
|
||||
$this->invoice = $invoice;
|
||||
@ -46,7 +49,8 @@ class AutoBillInvoice extends AbstractService
|
||||
{
|
||||
MultiDB::setDb($this->db);
|
||||
|
||||
$this->client = $this->invoice->client->fresh();
|
||||
/* Harvest Client*/
|
||||
$this->client = $this->invoice->client;
|
||||
|
||||
$is_partial = false;
|
||||
|
||||
@ -68,6 +72,10 @@ class AutoBillInvoice extends AbstractService
|
||||
$this->applyCreditPayment();
|
||||
}
|
||||
|
||||
//If this returns true, it means a partial invoice amount was paid as a credit and there is no further balance payable
|
||||
if($this->is_partial_amount && $this->invoice->partial == 0)
|
||||
return;
|
||||
|
||||
$amount = 0;
|
||||
|
||||
/* Determine $amount */
|
||||
@ -169,9 +177,9 @@ class AutoBillInvoice extends AbstractService
|
||||
$payment->invoices()->attach($this->invoice->id, ['amount' => $amount]);
|
||||
|
||||
$this->invoice
|
||||
->service()
|
||||
->setStatus(Invoice::STATUS_PAID)
|
||||
->save();
|
||||
->service()
|
||||
->setCalculatedStatus()
|
||||
->save();
|
||||
|
||||
foreach ($this->used_credit as $credit) {
|
||||
$current_credit = Credit::find($credit['credit_id']);
|
||||
@ -191,18 +199,18 @@ class AutoBillInvoice extends AbstractService
|
||||
->updatePaymentBalance($amount * -1)
|
||||
->save();
|
||||
|
||||
$client = $this->invoice->client->fresh();
|
||||
|
||||
$client->service()
|
||||
->updateBalance($amount * -1)
|
||||
->updatePaidToDate($amount)
|
||||
->adjustCreditBalance($amount * -1)
|
||||
->save();
|
||||
$this->invoice
|
||||
->client
|
||||
->service()
|
||||
->updateBalanceAndPaidToDate($amount * -1, $amount)
|
||||
// ->updateBalance($amount * -1)
|
||||
// ->updatePaidToDate($amount)
|
||||
->adjustCreditBalance($amount * -1)
|
||||
->save();
|
||||
|
||||
$this->invoice->ledger() //09-03-2022
|
||||
// ->updateInvoiceBalance($amount * -1, "Invoice {$this->invoice->number} payment using Credit {$current_credit->number}")
|
||||
->updateCreditBalance($amount * -1, "Credit {$current_credit->number} used to pay down Invoice {$this->invoice->number}")
|
||||
->save();
|
||||
->updateCreditBalance($amount * -1, "Credit {$current_credit->number} used to pay down Invoice {$this->invoice->number}")
|
||||
->save();
|
||||
|
||||
event('eloquent.created: App\Models\Payment', $payment);
|
||||
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars()));
|
||||
@ -221,11 +229,11 @@ class AutoBillInvoice extends AbstractService
|
||||
*/
|
||||
private function applyCreditPayment()
|
||||
{
|
||||
$available_credits = $this->client
|
||||
->credits
|
||||
$available_credits = Credit::where('client_id', $this->client->id)
|
||||
->where('is_deleted', false)
|
||||
->where('balance', '>', 0)
|
||||
->sortBy('created_at');
|
||||
->orderBy('created_at')
|
||||
->get();
|
||||
|
||||
$available_credit_balance = $available_credits->sum('balance');
|
||||
|
||||
@ -235,16 +243,14 @@ class AutoBillInvoice extends AbstractService
|
||||
return;
|
||||
}
|
||||
|
||||
$is_partial_amount = false;
|
||||
|
||||
if ($this->invoice->partial > 0) {
|
||||
$is_partial_amount = true;
|
||||
$this->is_partial_amount = true;
|
||||
}
|
||||
|
||||
$this->used_credit = [];
|
||||
|
||||
foreach ($available_credits as $key => $credit) {
|
||||
if ($is_partial_amount) {
|
||||
if ($this->is_partial_amount) {
|
||||
|
||||
//more credit than needed
|
||||
if ($credit->balance > $this->invoice->partial) {
|
||||
@ -260,6 +266,7 @@ class AutoBillInvoice extends AbstractService
|
||||
$this->invoice->partial -= $credit->balance;
|
||||
$this->invoice->balance -= $credit->balance;
|
||||
$this->invoice->paid_to_date += $credit->balance;
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -276,6 +283,7 @@ class AutoBillInvoice extends AbstractService
|
||||
$this->used_credit[$key]['amount'] = $credit->balance;
|
||||
$this->invoice->balance -= $credit->balance;
|
||||
$this->invoice->paid_to_date += $credit->balance;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +45,7 @@ class MarkInvoiceDeleted extends AbstractService
|
||||
->setAdjustmentAmount()
|
||||
->deletePaymentables()
|
||||
->adjustPayments()
|
||||
->adjustPaidToDate()
|
||||
->adjustBalance()
|
||||
->adjustPaidToDateAndBalance()
|
||||
->adjustLedger();
|
||||
|
||||
$transaction = [
|
||||
@ -70,21 +69,29 @@ class MarkInvoiceDeleted extends AbstractService
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function adjustPaidToDate()
|
||||
private function adjustPaidToDateAndBalance()
|
||||
{
|
||||
$client = $this->invoice->client->fresh();
|
||||
$client->paid_to_date += $this->adjustment_amount * -1;
|
||||
$client->save();
|
||||
// $this->invoice->client->service()->updatePaidToDate($this->adjustment_amount * -1)->save(); //reduces the paid to date by the payment totals
|
||||
// $client = $this->invoice->client->fresh();
|
||||
// $client->paid_to_date += $this->adjustment_amount * -1;
|
||||
// $client->balance += $this->balance_adjustment * -1;
|
||||
// $client->save();
|
||||
|
||||
// 06-09-2022
|
||||
$this->invoice
|
||||
->client
|
||||
->service()
|
||||
->updateBalanceAndPaidToDate($this->balance_adjustment * -1, $this->adjustment_amount * -1)
|
||||
->save(); //reduces the paid to date by the payment totals
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// @deprecated
|
||||
private function adjustBalance()
|
||||
{
|
||||
$client = $this->invoice->client->fresh();
|
||||
$client->balance += $this->balance_adjustment * -1;
|
||||
$client->save();
|
||||
// $client = $this->invoice->client->fresh();
|
||||
// $client->balance += $this->balance_adjustment * -1;
|
||||
// $client->save();
|
||||
|
||||
// $this->invoice->client->service()->updateBalance($this->balance_adjustment * -1)->save(); //reduces the client balance by the invoice amount.
|
||||
|
||||
|
@ -34,6 +34,8 @@ class MarkPaid extends AbstractService
|
||||
|
||||
private $invoice;
|
||||
|
||||
private $payable_balance;
|
||||
|
||||
public function __construct(Invoice $invoice)
|
||||
{
|
||||
$this->invoice = $invoice;
|
||||
@ -41,20 +43,37 @@ class MarkPaid extends AbstractService
|
||||
|
||||
public function run()
|
||||
{
|
||||
if ($this->invoice->status_id == Invoice::STATUS_DRAFT) {
|
||||
$this->invoice->service()->markSent()->save();
|
||||
}
|
||||
|
||||
/*Don't double pay*/
|
||||
if ($this->invoice->status_id == Invoice::STATUS_PAID) {
|
||||
return $this->invoice;
|
||||
}
|
||||
|
||||
if ($this->invoice->status_id == Invoice::STATUS_DRAFT) {
|
||||
$this->invoice = $this->invoice->service()->markSent()->save();
|
||||
}
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () {
|
||||
|
||||
$this->invoice = Invoice::where('id', $this->invoice->id)->lockForUpdate()->first();
|
||||
|
||||
$this->payable_balance = $this->invoice->balance;
|
||||
|
||||
$this->invoice
|
||||
->service()
|
||||
->setExchangeRate()
|
||||
->updateBalance($this->payable_balance * -1)
|
||||
->updatePaidToDate($this->payable_balance)
|
||||
->setStatus(Invoice::STATUS_PAID)
|
||||
->save();
|
||||
|
||||
}, 1);
|
||||
|
||||
/* Create Payment */
|
||||
$payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||
|
||||
$payment->amount = $this->invoice->balance;
|
||||
$payment->applied = $this->invoice->balance;
|
||||
$payment->amount = $this->payable_balance;
|
||||
$payment->applied = $this->payable_balance;
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->client_id = $this->invoice->client_id;
|
||||
$payment->transaction_reference = ctrans('texts.manual_entry');
|
||||
@ -75,25 +94,20 @@ class MarkPaid extends AbstractService
|
||||
|
||||
$payment->service()->applyNumber()->save();
|
||||
|
||||
if($payment->company->getSetting('send_email_on_mark_paid'))
|
||||
$payment->service()->sendEmail();
|
||||
|
||||
$this->setExchangeRate($payment);
|
||||
|
||||
/* Create a payment relationship to the invoice entity */
|
||||
$payment->invoices()->attach($this->invoice->id, [
|
||||
'amount' => $payment->amount,
|
||||
'amount' => $this->payable_balance,
|
||||
]);
|
||||
|
||||
event('eloquent.created: App\Models\Payment', $payment);
|
||||
|
||||
$this->invoice->next_send_date = null;
|
||||
|
||||
$this->invoice
|
||||
->service()
|
||||
->setExchangeRate()
|
||||
->updateBalance($payment->amount * -1)
|
||||
->updatePaidToDate($payment->amount)
|
||||
->setStatus(Invoice::STATUS_PAID)
|
||||
->save();
|
||||
|
||||
$this->invoice
|
||||
->service()
|
||||
->applyNumber()
|
||||
@ -101,16 +115,14 @@ class MarkPaid extends AbstractService
|
||||
->save();
|
||||
|
||||
$payment->ledger()
|
||||
->updatePaymentBalance($payment->amount * -1);
|
||||
->updatePaymentBalance($this->payable_balance * -1);
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () use ($payment) {
|
||||
|
||||
/* Get the last record for the client and set the current balance*/
|
||||
$client = Client::withTrashed()->where('id', $this->invoice->client_id)->lockForUpdate()->first();
|
||||
$client->paid_to_date += $payment->amount;
|
||||
$client->balance -= $payment->amount;
|
||||
$client->save();
|
||||
}, 1);
|
||||
//06-09-2022
|
||||
$this->invoice
|
||||
->client
|
||||
->service()
|
||||
->updateBalanceAndPaidToDate($payment->amount*-1, $payment->amount)
|
||||
->save();
|
||||
|
||||
$this->invoice = $this->invoice
|
||||
->service()
|
||||
|
@ -62,14 +62,7 @@ class MarkSent extends AbstractService
|
||||
->save();
|
||||
|
||||
/*Adjust client balance*/
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () use ($adjustment) {
|
||||
|
||||
/* Get the last record for the client and set the current balance*/
|
||||
$client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first();
|
||||
$client->balance += $adjustment;
|
||||
$client->save();
|
||||
}, 1);
|
||||
$this->invoice->client->service()->updateBalance($adjustment)->save();
|
||||
|
||||
$this->invoice->markInvitationsSent();
|
||||
|
||||
|
@ -56,7 +56,7 @@ class ApplyNumber extends AbstractService
|
||||
} catch (QueryException $e) {
|
||||
$x++;
|
||||
|
||||
if ($x > 10) {
|
||||
if ($x > 50) {
|
||||
$this->completed = false;
|
||||
}
|
||||
}
|
||||
|
@ -90,9 +90,12 @@ class DeletePayment
|
||||
->updateInvoiceBalance($net_deletable, "Adjusting invoice {$paymentable_invoice->number} due to deletion of Payment {$this->payment->number}")
|
||||
->save();
|
||||
|
||||
$client = $client->service()
|
||||
->updateBalance($net_deletable)
|
||||
->save();
|
||||
$client = $this->payment
|
||||
->client
|
||||
->fresh()
|
||||
->service()
|
||||
->updateBalance($net_deletable)
|
||||
->save();
|
||||
|
||||
if ($paymentable_invoice->balance == $paymentable_invoice->amount) {
|
||||
$paymentable_invoice->service()->setStatus(Invoice::STATUS_SENT)->save();
|
||||
|
@ -55,15 +55,12 @@ class RefundPayment
|
||||
public function run()
|
||||
{
|
||||
$this->payment = $this->calculateTotalRefund() //sets amount for the refund (needed if we are refunding multiple invoices in one payment)
|
||||
->setStatus() //sets status of payment
|
||||
//->reversePayment()
|
||||
//->buildCreditNote() //generate the credit note
|
||||
//->buildCreditLineItems() //generate the credit note items
|
||||
->updateCreditables() //return the credits first
|
||||
->updatePaymentables() //update the paymentable items
|
||||
->adjustInvoices()
|
||||
->processGatewayRefund() //process the gateway refund if needed
|
||||
->save();
|
||||
->setStatus() //sets status of payment
|
||||
->updateCreditables() //return the credits first
|
||||
->updatePaymentables() //update the paymentable items
|
||||
->adjustInvoices()
|
||||
->processGatewayRefund() //process the gateway refund if needed
|
||||
->save();
|
||||
|
||||
if (array_key_exists('email_receipt', $this->refund_data) && $this->refund_data['email_receipt'] == 'true') {
|
||||
$contact = $this->payment->client->contacts()->whereNotNull('email')->first();
|
||||
@ -251,7 +248,6 @@ class RefundPayment
|
||||
*/
|
||||
private function adjustInvoices()
|
||||
{
|
||||
$adjustment_amount = 0;
|
||||
|
||||
if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) {
|
||||
foreach ($this->refund_data['invoices'] as $refunded_invoice) {
|
||||
@ -266,7 +262,9 @@ class RefundPayment
|
||||
->updatePaidToDate($refunded_invoice['amount'] * -1)
|
||||
->save();
|
||||
|
||||
$invoice->ledger()->updateInvoiceBalance($refunded_invoice['amount'], "Refund of payment # {$this->payment->number}")->save();
|
||||
$invoice->ledger()
|
||||
->updateInvoiceBalance($refunded_invoice['amount'], "Refund of payment # {$this->payment->number}")
|
||||
->save();
|
||||
|
||||
if ($invoice->amount == $invoice->balance) {
|
||||
$invoice->service()->setStatus(Invoice::STATUS_SENT);
|
||||
@ -276,10 +274,15 @@ class RefundPayment
|
||||
|
||||
$invoice->saveQuietly();
|
||||
|
||||
$client = $invoice->client;
|
||||
$adjustment_amount += $refunded_invoice['amount'];
|
||||
$client->balance += $refunded_invoice['amount'];
|
||||
$client->save();
|
||||
//06-09-2022
|
||||
$client = $invoice->client
|
||||
->service()
|
||||
->updateBalance($refunded_invoice['amount'])
|
||||
->save();
|
||||
|
||||
// $client = $invoice->client;
|
||||
// $client->balance += $refunded_invoice['amount'];
|
||||
// $client->save();
|
||||
|
||||
$transaction = [
|
||||
'invoice' => $invoice->transaction_event(),
|
||||
@ -350,75 +353,4 @@ class RefundPayment
|
||||
return $this->payment;
|
||||
}
|
||||
|
||||
// public function updateCreditNoteBalance()
|
||||
// {
|
||||
// $this->credit_note->balance -= $this->total_refund;
|
||||
// $this->credit_note->status_id = Credit::STATUS_APPLIED;
|
||||
|
||||
// $this->credit_note->balance === 0
|
||||
// ? $this->credit_note->status_id = Credit::STATUS_APPLIED
|
||||
// : $this->credit_note->status_id = Credit::STATUS_PARTIAL;
|
||||
|
||||
// $this->credit_note->save();
|
||||
|
||||
// return $this;
|
||||
// }
|
||||
|
||||
// private function buildCreditNote()
|
||||
// {
|
||||
// $this->credit_note = CreditFactory::create($this->payment->company_id, $this->payment->user_id);
|
||||
// $this->credit_note->assigned_user_id = isset($this->payment->assigned_user_id) ?: null;
|
||||
// $this->credit_note->date = $this->refund_data['date'];
|
||||
// $this->credit_note->status_id = Credit::STATUS_SENT;
|
||||
// $this->credit_note->client_id = $this->payment->client->id;
|
||||
// $this->credit_note->amount = $this->total_refund;
|
||||
// $this->credit_note->balance = $this->total_refund;
|
||||
|
||||
// $this->credit_note->save();
|
||||
// $this->credit_note->number = $this->payment->client->getNextCreditNumber($this->payment->client);
|
||||
// $this->credit_note->save();
|
||||
|
||||
// return $this;
|
||||
// }
|
||||
|
||||
// private function buildCreditLineItems()
|
||||
// {
|
||||
// $ledger_string = '';
|
||||
|
||||
// if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) {
|
||||
// foreach ($this->refund_data['invoices'] as $invoice) {
|
||||
|
||||
// $inv = Invoice::find($invoice['invoice_id']);
|
||||
|
||||
// $credit_line_item = InvoiceItemFactory::create();
|
||||
// $credit_line_item->quantity = 1;
|
||||
// $credit_line_item->cost = $invoice['amount'];
|
||||
// $credit_line_item->product_key = ctrans('texts.invoice');
|
||||
// $credit_line_item->notes = ctrans('texts.refund_body', ['amount' => $invoice['amount'], 'invoice_number' => $inv->number]);
|
||||
// $credit_line_item->line_total = $invoice['amount'];
|
||||
// $credit_line_item->date = $this->refund_data['date'];
|
||||
|
||||
// $ledger_string .= $credit_line_item->notes . ' ';
|
||||
|
||||
// $line_items[] = $credit_line_item;
|
||||
// }
|
||||
// } else {
|
||||
|
||||
// $credit_line_item = InvoiceItemFactory::create();
|
||||
// $credit_line_item->quantity = 1;
|
||||
// $credit_line_item->cost = $this->refund_data['amount'];
|
||||
// $credit_line_item->product_key = ctrans('texts.credit');
|
||||
// $credit_line_item->notes = ctrans('texts.credit_created_by', ['transaction_reference' => $this->payment->number]);
|
||||
// $credit_line_item->line_total = $this->refund_data['amount'];
|
||||
// $credit_line_item->date = $this->refund_data['date'];
|
||||
|
||||
// $line_items = [];
|
||||
// $line_items[] = $credit_line_item;
|
||||
// }
|
||||
|
||||
// $this->credit_note->line_items = $line_items;
|
||||
// $this->credit_note->save();
|
||||
|
||||
// return $this;
|
||||
// }
|
||||
}
|
||||
|
@ -62,15 +62,17 @@ class UpdateInvoicePayment
|
||||
$paid_amount = $paid_invoice->amount;
|
||||
}
|
||||
|
||||
\DB::connection(config('database.default'))->transaction(function () use($client, $paid_amount){
|
||||
$client->service()->updateBalanceAndPaidToDate($paid_amount*-1, $paid_amount);
|
||||
|
||||
// \DB::connection(config('database.default'))->transaction(function () use($client, $paid_amount){
|
||||
|
||||
$update_client = Client::withTrashed()->where('id', $client->id)->lockForUpdate()->first();
|
||||
// $update_client = Client::withTrashed()->where('id', $client->id)->lockForUpdate()->first();
|
||||
|
||||
$update_client->paid_to_date += $paid_amount;
|
||||
$update_client->balance -= $paid_amount;
|
||||
$update_client->save();
|
||||
// $update_client->paid_to_date += $paid_amount;
|
||||
// $update_client->balance -= $paid_amount;
|
||||
// $update_client->save();
|
||||
|
||||
}, 1);
|
||||
// }, 1);
|
||||
|
||||
/* Need to determine here is we have an OVER payment - if YES only apply the max invoice amount */
|
||||
if($paid_amount > $invoice->partial && $paid_amount > $invoice->balance)
|
||||
|
@ -104,7 +104,7 @@ class PurchaseOrderService
|
||||
if($force){
|
||||
|
||||
$this->purchase_order->invitations->each(function ($invitation) {
|
||||
CreatePurchaseOrderPdf::dispatchNow($invitation);
|
||||
(new CreatePurchaseOrderPdf($invitation))->handle();
|
||||
});
|
||||
|
||||
return $this;
|
||||
|
@ -67,7 +67,7 @@ class ApplyNumber
|
||||
} catch (QueryException $e) {
|
||||
$x++;
|
||||
|
||||
if ($x > 10) {
|
||||
if ($x > 50) {
|
||||
$this->completed = false;
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ class RecurringService
|
||||
|
||||
$this->recurring_entity->invitations->each(function ($invitation){
|
||||
|
||||
UnlinkFile::dispatchNow(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath($invitation) . $this->recurring_entity->numberFormatter().'.pdf');
|
||||
(new UnlinkFile(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath($invitation) . $this->recurring_entity->numberFormatter().'.pdf'))->handle();
|
||||
|
||||
});
|
||||
|
||||
|
@ -477,10 +477,14 @@ class SubscriptionService
|
||||
|
||||
nlog($response);
|
||||
|
||||
if($credit)
|
||||
return $this->handleRedirect('/client/credits/'.$credit->hashed_id);
|
||||
else
|
||||
return $this->handleRedirect('/client/credits');
|
||||
if($credit){
|
||||
// return $this->handleRedirect('/client/credits/'.$credit->hashed_id);
|
||||
return '/client/credits/'.$credit->hashed_id;
|
||||
}
|
||||
else{
|
||||
// return $this->handleRedirect('/client/credits');
|
||||
return '/client/credits';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -42,9 +42,9 @@ class ZeroCostProduct extends AbstractService
|
||||
|
||||
$invoice = $this->subscription->service()->createInvoice($this->data);
|
||||
|
||||
$invoice->service()
|
||||
->markPaid()
|
||||
->save();
|
||||
$invoice = $invoice->service()
|
||||
->markPaid()
|
||||
->save();
|
||||
|
||||
$redirect_url = "/client/invoices/{$invoice->hashed_id}";
|
||||
|
||||
|
@ -179,6 +179,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'enable_applying_payments' => (bool) $company->enable_applying_payments,
|
||||
'enabled_expense_tax_rates' => (int) $company->enabled_expense_tax_rates,
|
||||
'invoice_task_project' => (bool) $company->invoice_task_project,
|
||||
'report_include_deleted' => (bool) $company->report_include_deleted,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,7 @@ class HtmlEngine
|
||||
$data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()) ?: ' ', 'label' => ctrans('texts.invoice_date')];
|
||||
|
||||
if($this->entity->project) {
|
||||
$data['$project.name'] = ['value' => $this->entity->project->name, 'label' => ctrans('texts.project_name')];
|
||||
$data['$project.name'] = ['value' => $this->entity->project->name, 'label' => ctrans('texts.project')];
|
||||
$data['$invoice.project'] = &$data['$project.name'];
|
||||
}
|
||||
|
||||
@ -522,6 +522,9 @@ class HtmlEngine
|
||||
$data['$font_name'] = ['value' => Helpers::resolveFont($this->settings->primary_font)['name'], 'label' => ''];
|
||||
$data['$font_url'] = ['value' => Helpers::resolveFont($this->settings->primary_font)['url'], 'label' => ''];
|
||||
|
||||
$data['$secondary_font_name'] = ['value' => Helpers::resolveFont($this->settings->secondary_font)['name'], 'label' => ''];
|
||||
$data['$secondary_font_url'] = ['value' => Helpers::resolveFont($this->settings->secondary_font)['url'], 'label' => ''];
|
||||
|
||||
$data['$invoiceninja.whitelabel'] = ['value' => 'https://raw.githubusercontent.com/invoiceninja/invoiceninja/v5-develop/public/images/new_logo.png', 'label' => ''];
|
||||
|
||||
$data['$primary_color'] = ['value' => $this->settings->primary_color, 'label' => ''];
|
||||
|
@ -184,6 +184,9 @@ class SystemHealth
|
||||
|
||||
private static function checkPhpCli()
|
||||
{
|
||||
if(!function_exists('exec'))
|
||||
return "Unable to check CLI version";
|
||||
|
||||
try {
|
||||
exec('php -v', $foo, $exitCode);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
@ -87,16 +88,16 @@ class TemplateEngine
|
||||
{
|
||||
|
||||
return $this->setEntity()
|
||||
->setSettingsObject()
|
||||
->setTemplates()
|
||||
->replaceValues()
|
||||
->renderTemplate();
|
||||
->setSettingsObject()
|
||||
->setTemplates()
|
||||
->replaceValues()
|
||||
->renderTemplate();
|
||||
}
|
||||
|
||||
private function setEntity()
|
||||
{
|
||||
if (strlen($this->entity) > 1 && strlen($this->entity_id) > 1) {
|
||||
$class = 'App\Models\\'.ucfirst(Str::camel($this->entity));
|
||||
$class = 'App\Models\\' . ucfirst(Str::camel($this->entity));
|
||||
$this->entity_obj = $class::withTrashed()->where('id', $this->decodePrimaryKey($this->entity_id))->company()->first();
|
||||
} else {
|
||||
$this->mockEntity();
|
||||
@ -107,11 +108,10 @@ class TemplateEngine
|
||||
|
||||
private function setSettingsObject()
|
||||
{
|
||||
if($this->entity == 'purchaseOrder'){
|
||||
if ($this->entity == 'purchaseOrder' || $this->entity == 'purchase_order') {
|
||||
$this->settings_entity = auth()->user()->company();
|
||||
$this->settings = $this->settings_entity->settings;
|
||||
}
|
||||
elseif ($this->entity_obj->client()->exists()) {
|
||||
} elseif ($this->entity_obj->client()->exists()) {
|
||||
$this->settings_entity = $this->entity_obj->client;
|
||||
$this->settings = $this->settings_entity->getMergedSettings();
|
||||
} else {
|
||||
@ -155,13 +155,11 @@ class TemplateEngine
|
||||
$this->raw_body = $this->body;
|
||||
$this->raw_subject = $this->subject;
|
||||
|
||||
if($this->entity == 'purchaseOrder'){
|
||||
$this->fakerValues();
|
||||
}
|
||||
elseif ($this->entity_obj->client()->exists()) {
|
||||
if ($this->entity_obj->client()->exists()) {
|
||||
$this->entityValues($this->entity_obj->client->primary_contact()->first());
|
||||
}
|
||||
else {
|
||||
} elseif ($this->entity_obj->vendor()->exists()) {
|
||||
$this->entityValues($this->entity_obj->vendor->primary_contact()->first());
|
||||
} else {
|
||||
$this->fakerValues();
|
||||
}
|
||||
|
||||
@ -184,16 +182,16 @@ class TemplateEngine
|
||||
]);
|
||||
|
||||
$this->body = $converter->convert($this->body)->getContent();
|
||||
|
||||
}
|
||||
|
||||
private function entityValues($contact)
|
||||
{
|
||||
if($this->entity == 'purchaseOrder')
|
||||
if (in_array($this->entity, ['purchaseOrder', 'purchase_order']))
|
||||
$this->labels_and_values = (new VendorHtmlEngine($this->entity_obj->invitations->first()))->generateLabelsAndValues();
|
||||
else
|
||||
$this->labels_and_values = (new HtmlEngine($this->entity_obj->invitations->first()))->generateLabelsAndValues();
|
||||
|
||||
|
||||
$this->body = strtr($this->body, $this->labels_and_values['labels']);
|
||||
$this->body = strtr($this->body, $this->labels_and_values['values']);
|
||||
|
||||
@ -217,16 +215,15 @@ class TemplateEngine
|
||||
$data['footer'] = '';
|
||||
$data['logo'] = auth()->user()->company()->present()->logo();
|
||||
|
||||
if($this->entity_obj->client()->exists())
|
||||
if ($this->entity_obj->client()->exists())
|
||||
$data = array_merge($data, Helpers::sharedEmailVariables($this->entity_obj->client));
|
||||
else{
|
||||
else {
|
||||
|
||||
$data['signature'] = $this->settings->email_signature;
|
||||
$data['settings'] = $this->settings;
|
||||
$data['whitelabel'] = $this->entity_obj ? $this->entity_obj->company->account->isPaid() : true;
|
||||
$data['company'] = $this->entity_obj ? $this->entity_obj->company : '';
|
||||
$data['settings'] = $this->settings;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -235,7 +232,6 @@ class TemplateEngine
|
||||
|
||||
// In order to parse variables such as $signature in the body,
|
||||
// we need to replace strings with the values from HTMLEngine.
|
||||
|
||||
$wrapper = strtr($wrapper, $this->labels_and_values['values']);
|
||||
|
||||
/*If no custom design exists, send back a blank!*/
|
||||
@ -269,7 +265,7 @@ class TemplateEngine
|
||||
|
||||
private function mockEntity()
|
||||
{
|
||||
if(!$this->entity && $this->template && str_contains($this->template, 'purchase_order'))
|
||||
if (!$this->entity && $this->template && str_contains($this->template, 'purchase_order'))
|
||||
$this->entity = 'purchaseOrder';
|
||||
|
||||
DB::connection(config('database.default'))->beginTransaction();
|
||||
@ -289,7 +285,7 @@ class TemplateEngine
|
||||
'send_email' => true,
|
||||
]);
|
||||
|
||||
if (! $this->entity || $this->entity == 'invoice') {
|
||||
if (!$this->entity || $this->entity == 'invoice') {
|
||||
$this->entity_obj = Invoice::factory()->create([
|
||||
'user_id' => auth()->user()->id,
|
||||
'company_id' => auth()->user()->company()->id,
|
||||
@ -321,40 +317,37 @@ class TemplateEngine
|
||||
|
||||
|
||||
|
||||
if($this->entity == 'purchaseOrder')
|
||||
{
|
||||
if ($this->entity == 'purchaseOrder') {
|
||||
|
||||
$vendor = Vendor::factory()->create([
|
||||
'user_id' => auth()->user()->id,
|
||||
'company_id' => auth()->user()->company()->id,
|
||||
]);
|
||||
'user_id' => auth()->user()->id,
|
||||
'company_id' => auth()->user()->company()->id,
|
||||
]);
|
||||
|
||||
$contact = VendorContact::factory()->create([
|
||||
'user_id' => auth()->user()->id,
|
||||
'company_id' => auth()->user()->company()->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'is_primary' => 1,
|
||||
'send_email' => true,
|
||||
]);
|
||||
'user_id' => auth()->user()->id,
|
||||
'company_id' => auth()->user()->company()->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
'is_primary' => 1,
|
||||
'send_email' => true,
|
||||
]);
|
||||
|
||||
|
||||
$this->entity_obj = PurchaseOrder::factory()->create([
|
||||
'user_id' => auth()->user()->id,
|
||||
'company_id' => auth()->user()->company()->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
]);
|
||||
|
||||
$invitation = PurchaseOrderInvitation::factory()->create([
|
||||
'user_id' => auth()->user()->id,
|
||||
'company_id' => auth()->user()->company()->id,
|
||||
'purchase_order_id' => $this->entity_obj->id,
|
||||
'vendor_contact_id' => $contact->id,
|
||||
'user_id' => auth()->user()->id,
|
||||
'company_id' => auth()->user()->company()->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
]);
|
||||
|
||||
$invitation = PurchaseOrderInvitation::factory()->create([
|
||||
'user_id' => auth()->user()->id,
|
||||
'company_id' => auth()->user()->company()->id,
|
||||
'purchase_order_id' => $this->entity_obj->id,
|
||||
'vendor_contact_id' => $contact->id,
|
||||
]);
|
||||
}
|
||||
|
||||
if($vendor)
|
||||
{
|
||||
if ($vendor) {
|
||||
|
||||
$this->entity_obj->setRelation('invitations', $invitation);
|
||||
$this->entity_obj->setRelation('vendor', $vendor);
|
||||
@ -362,10 +355,7 @@ class TemplateEngine
|
||||
$this->entity_obj->load('vendor');
|
||||
$vendor->setRelation('company', auth()->user()->company());
|
||||
$vendor->load('company');
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$this->entity_obj->setRelation('invitations', $invitation);
|
||||
$this->entity_obj->setRelation('client', $client);
|
||||
$this->entity_obj->setRelation('company', auth()->user()->company());
|
||||
|
@ -28,6 +28,7 @@ trait Refundable
|
||||
/**
|
||||
* Entry point for processing of refunds.
|
||||
* @param array $data
|
||||
* @deprecated ???? 06-09-2022
|
||||
* @return Refundable
|
||||
* @throws PaymentRefundFailed
|
||||
*/
|
||||
|
@ -33,6 +33,9 @@ trait SavesDocuments
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!is_array($document_array))
|
||||
return;
|
||||
|
||||
foreach ($document_array as $document) {
|
||||
$document = (new UploadFile(
|
||||
$document,
|
||||
|
@ -408,6 +408,68 @@ class VendorHtmlEngine
|
||||
|
||||
$data['$payments'] = ['value' => '', 'label' => ctrans('texts.payments')];
|
||||
|
||||
if($this->entity->client()->exists())
|
||||
{
|
||||
|
||||
$data['$client1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client1', $this->entity->client->custom_value1, $this->entity->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client1')];
|
||||
$data['$client2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client2', $this->entity->client->custom_value2, $this->entity->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client2')];
|
||||
$data['$client3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client3', $this->entity->client->custom_value3, $this->entity->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client3')];
|
||||
$data['$client4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'client4', $this->entity->client->custom_value4, $this->entity->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'client4')];
|
||||
$data['$client.custom1'] = &$data['$client1'];
|
||||
$data['$client.custom2'] = &$data['$client2'];
|
||||
$data['$client.custom3'] = &$data['$client3'];
|
||||
$data['$client.custom4'] = &$data['$client4'];
|
||||
$data['$client.number'] = ['value' => $this->entity->client->number ?: ' ', 'label' => ctrans('texts.number')];
|
||||
|
||||
$data['$client_name'] = ['value' => $this->entity->client->present()->name() ?: ' ', 'label' => ctrans('texts.client_name')];
|
||||
$data['$client.name'] = &$data['$client_name'];
|
||||
$data['$client'] = &$data['$client_name'];
|
||||
|
||||
$data['$client.address1'] = &$data['$address1'];
|
||||
$data['$client.address2'] = &$data['$address2'];
|
||||
$data['$client_address'] = ['value' => $this->entity->client->present()->address() ?: ' ', 'label' => ctrans('texts.address')];
|
||||
$data['$client.address'] = &$data['$client_address'];
|
||||
$data['$client.postal_code'] = ['value' => $this->entity->client->postal_code ?: ' ', 'label' => ctrans('texts.postal_code')];
|
||||
$data['$client.public_notes'] = ['value' => $this->entity->client->public_notes ?: ' ', 'label' => ctrans('texts.notes')];
|
||||
$data['$client.city'] = ['value' => $this->entity->client->city ?: ' ', 'label' => ctrans('texts.city')];
|
||||
$data['$client.state'] = ['value' => $this->entity->client->state ?: ' ', 'label' => ctrans('texts.state')];
|
||||
$data['$client.id_number'] = &$data['$id_number'];
|
||||
$data['$client.vat_number'] = &$data['$vat_number'];
|
||||
$data['$client.website'] = &$data['$website'];
|
||||
$data['$client.phone'] = &$data['$phone'];
|
||||
$data['$city_state_postal'] = ['value' => $this->entity->client->present()->cityStateZip($this->entity->client->city, $this->entity->client->state, $this->entity->client->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
|
||||
$data['$client.city_state_postal'] = &$data['$city_state_postal'];
|
||||
$data['$postal_city_state'] = ['value' => $this->entity->client->present()->cityStateZip($this->entity->client->city, $this->entity->client->state, $this->entity->client->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
|
||||
$data['$client.postal_city_state'] = &$data['$postal_city_state'];
|
||||
$data['$client.country'] = &$data['$country'];
|
||||
$data['$client.email'] = &$data['$email'];
|
||||
|
||||
$data['$client.billing_address'] = &$data['$client_address'];
|
||||
$data['$client.billing_address1'] = &$data['$client.address1'];
|
||||
$data['$client.billing_address2'] = &$data['$client.address2'];
|
||||
$data['$client.billing_city'] = &$data['$client.city'];
|
||||
$data['$client.billing_state'] = &$data['$client.state'];
|
||||
$data['$client.billing_postal_code'] = &$data['$client.postal_code'];
|
||||
$data['$client.billing_country'] = &$data['$client.country'];
|
||||
|
||||
$data['$client.shipping_address'] = ['value' => $this->entity->client->present()->shipping_address() ?: ' ', 'label' => ctrans('texts.shipping_address')];
|
||||
$data['$client.shipping_address1'] = ['value' => $this->entity->client->shipping_address1 ?: ' ', 'label' => ctrans('texts.shipping_address1')];
|
||||
$data['$client.shipping_address2'] = ['value' => $this->entity->client->shipping_address2 ?: ' ', 'label' => ctrans('texts.shipping_address2')];
|
||||
$data['$client.shipping_city'] = ['value' => $this->entity->client->shipping_city ?: ' ', 'label' => ctrans('texts.shipping_city')];
|
||||
$data['$client.shipping_state'] = ['value' => $this->entity->client->shipping_state ?: ' ', 'label' => ctrans('texts.shipping_state')];
|
||||
$data['$client.shipping_postal_code'] = ['value' => $this->entity->client->shipping_postal_code ?: ' ', 'label' => ctrans('texts.shipping_postal_code')];
|
||||
$data['$client.shipping_country'] = ['value' => isset($this->entity->client->shipping_country->name) ? ctrans('texts.country_' . $this->entity->client->shipping_country->name) : '', 'label' => ctrans('texts.shipping_country')];
|
||||
|
||||
$data['$client.currency'] = ['value' => $this->entity->client->currency()->code, 'label' => ''];
|
||||
|
||||
$data['$client.lang_2'] = ['value' => optional($this->entity->client->language())->locale, 'label' => ''];
|
||||
|
||||
$data['$client.balance'] = ['value' => Number::formatMoney($this->entity->client->balance, $this->entity->client), 'label' => ctrans('texts.account_balance')];
|
||||
$data['$client_balance'] = ['value' => Number::formatMoney($this->entity->client->balance, $this->entity->client), 'label' => ctrans('texts.account_balance')];
|
||||
|
||||
}
|
||||
|
||||
|
||||
$arrKeysLength = array_map('strlen', array_keys($data));
|
||||
array_multisort($arrKeysLength, SORT_DESC, $data);
|
||||
|
||||
|
@ -88,13 +88,13 @@
|
||||
"symfony/mailgun-mailer": "^6.1",
|
||||
"symfony/postmark-mailer": "^6.1",
|
||||
"tijsverkoyen/css-to-inline-styles": "^2.2",
|
||||
"turbo124/beacon": "^1.2",
|
||||
"turbo124/beacon": "^1.3",
|
||||
"twilio/sdk": "^6.40",
|
||||
"webpatser/laravel-countries": "dev-master#75992ad",
|
||||
"wepay/php-sdk": "^0.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"php": "^7.4|^8.0",
|
||||
"php": "^8.1",
|
||||
"barryvdh/laravel-debugbar": "^3.6",
|
||||
"beyondcode/laravel-query-detector": "^1.6",
|
||||
"brianium/paratest": "^6.1",
|
||||
|
899
composer.lock
generated
899
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.5.17',
|
||||
'app_tag' => '5.5.17',
|
||||
'app_version' => '5.5.21',
|
||||
'app_tag' => '5.5.21',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user