Merge pull request #5 from invoiceninja/v5-develop

V5 develop
This commit is contained in:
Kendall Arneaud 2024-07-12 11:04:21 -04:00 committed by GitHub
commit 1d32a7c017
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
150 changed files with 343663 additions and 341065 deletions

View File

@ -63,3 +63,5 @@ APPLE_REDIRECT_URI=
NORDIGEN_SECRET_ID= NORDIGEN_SECRET_ID=
NORDIGEN_SECRET_KEY= NORDIGEN_SECRET_KEY=
OPENEXCHANGE_APP_ID=

View File

@ -44,11 +44,12 @@ jobs:
git clone https://${{secrets.commit_secret}}@github.com/invoiceninja/ui.git git clone https://${{secrets.commit_secret}}@github.com/invoiceninja/ui.git
cd ui cd ui
git checkout develop git checkout develop
cp .env.example .env
cp ../vite.config.ts.react ./vite.config.js cp ../vite.config.ts.react ./vite.config.js
npm i npm i
npm run build npm run build
cp -r dist/* ../public/ cp -r dist/* ../public/
cp dist/index.html ../resources/views/react/index.blade.php mv dist/index.html ../resources/views/react/index.blade.php
- name: Prepare JS/CSS assets - name: Prepare JS/CSS assets
run: | run: |

View File

@ -54,7 +54,6 @@ We offer a $30 per year white-label license to remove the Invoice Ninja branding
git clone --single-branch --branch v5-stable https://github.com/invoiceninja/invoiceninja.git git clone --single-branch --branch v5-stable https://github.com/invoiceninja/invoiceninja.git
cp .env.example .env cp .env.example .env
composer i -o --no-dev composer i -o --no-dev
php artisan key:generate
``` ```
Please Note: Please Note:

View File

@ -1 +1 @@
5.10.3 5.10.8

View File

@ -12,37 +12,38 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App; use App;
use App\Models\User;
use App\Utils\Ninja;
use App\Models\Quote;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Vendor;
use App\Models\Account;
use App\Models\Company;
use App\Models\Contact;
use App\Models\Expense;
use App\Models\Invoice;
use App\Models\Payment;
use App\Libraries\MultiDB;
use App\Models\CompanyUser;
use Illuminate\Support\Str;
use App\Models\CompanyToken;
use App\Models\ClientContact;
use App\Models\CompanyLedger;
use App\Models\PurchaseOrder;
use App\Models\VendorContact;
use App\Models\BankTransaction;
use App\Models\QuoteInvitation;
use Illuminate\Console\Command;
use App\Models\CreditInvitation;
use App\Models\RecurringInvoice;
use App\Models\InvoiceInvitation;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use App\Factory\ClientContactFactory; use App\Factory\ClientContactFactory;
use App\Factory\VendorContactFactory; use App\Factory\VendorContactFactory;
use App\Jobs\Company\CreateCompanyToken; use App\Jobs\Company\CreateCompanyToken;
use App\Libraries\MultiDB;
use App\Models\Account;
use App\Models\BankTransaction;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyLedger;
use App\Models\CompanyToken;
use App\Models\CompanyUser;
use App\Models\Contact;
use App\Models\Credit;
use App\Models\CreditInvitation;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\Payment;
use App\Models\PurchaseOrder;
use App\Models\Quote;
use App\Models\QuoteInvitation;
use App\Models\RecurringInvoice;
use App\Models\RecurringInvoiceInvitation; use App\Models\RecurringInvoiceInvitation;
use App\Models\User;
use App\Models\Vendor;
use App\Models\VendorContact;
use App\Utils\Ninja;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
/* /*
@ -130,6 +131,7 @@ class CheckData extends Command
$this->checkContactEmailAndSendEmailStatus(); $this->checkContactEmailAndSendEmailStatus();
$this->checkPaymentCurrency(); $this->checkPaymentCurrency();
$this->checkSubdomainsSet(); $this->checkSubdomainsSet();
$this->checkExpenseCurrency();
if (Ninja::isHosted()) { if (Ninja::isHosted()) {
$this->checkAccountStatuses(); $this->checkAccountStatuses();
@ -1158,7 +1160,21 @@ class CheckData extends Command
}); });
} }
}
public function checkExpenseCurrency()
{
Expense::with('company')
->withTrashed()
->whereNull('exchange_rate')
->orWhere('exchange_rate', 0)
->cursor()
->each(function ($expense){
$expense->exchange_rate = 1;
$expense->saveQuietly();
$this->logMessage("Fixing - exchange rate for expense :: {$expense->id}");
});
} }
} }

View File

@ -54,13 +54,6 @@ class PostUpdate extends Command
info('finished migrating'); info('finished migrating');
$output = [];
// exec('vendor/bin/composer install --no-dev -o', $output);
info(print_r($output, 1));
info('finished running composer install ');
try { try {
// Artisan::call('optimize'); // Artisan::call('optimize');
Artisan::call('config:clear'); Artisan::call('config:clear');

View File

@ -62,10 +62,10 @@ class SendRemindersCron extends Command
public function handle() public function handle()
{ {
Invoice::where('next_send_date', '<=', now()->toDateTimeString()) Invoice::where('next_send_date', '<=', now()->toDateTimeString())
->whereNull('deleted_at') ->whereNull('invoices.deleted_at')
->where('is_deleted', 0) ->where('invoices.is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) ->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0) ->where('invoices.balance', '>', 0)
->whereHas('client', function ($query) { ->whereHas('client', function ($query) {
$query->where('is_deleted', 0) $query->where('is_deleted', 0)
->where('deleted_at', null); ->where('deleted_at', null);
@ -73,6 +73,7 @@ class SendRemindersCron extends Command
->whereHas('company', function ($query) { ->whereHas('company', function ($query) {
$query->where('is_disabled', 0); $query->where('is_disabled', 0);
}) })
->with('invitations')->cursor()->each(function ($invoice) { ->with('invitations')->cursor()->each(function ($invoice) {
if ($invoice->isPayable()) { if ($invoice->isPayable()) {
$reminder_template = $invoice->calculateTemplate('invoice'); $reminder_template = $invoice->calculateTemplate('invoice');

View File

@ -54,8 +54,8 @@ class AccountPlatform extends GenericMixedMetric
public function __construct($string_metric5, $string_metric6, $string_metric7) public function __construct($string_metric5, $string_metric6, $string_metric7)
{ {
$this->string_metric5 = $string_metric5; $this->string_metric5 = mb_convert_encoding($string_metric5, 'UTF-8');
$this->string_metric6 = $string_metric6; $this->string_metric6 = mb_convert_encoding($string_metric6, 'UTF-8');
$this->string_metric7 = $string_metric7; $this->string_metric7 = $string_metric7;
} }
} }

View File

@ -73,7 +73,7 @@ class DbQuery extends GenericMixedMetric
$this->string_metric6 = $string_metric6; $this->string_metric6 = $string_metric6;
$this->double_metric2 = $double_metric2; $this->double_metric2 = $double_metric2;
$this->string_metric7 = $string_metric7; $this->string_metric7 = $string_metric7;
$this->string_metric8 = $string_metric8; $this->string_metric8 = mb_convert_encoding($string_metric8, "UTF-8");
$this->string_metric9 = $string_metric9; $this->string_metric9 = mb_convert_encoding($string_metric9, "UTF-8");
} }
} }

View File

@ -207,6 +207,14 @@ class Rule extends BaseRule implements RuleInterface
*/ */
public function override($item): self public function override($item): self
{ {
$this->tax_rate1 = $item->tax_rate1;
$this->tax_name1 = $item->tax_name1;
$this->tax_rate2 = $item->tax_rate2;
$this->tax_name2 = $item->tax_name2;
$this->tax_rate3 = $item->tax_rate3;
$this->tax_name3 = $item->tax_name3;
return $this; return $this;
} }

View File

@ -18,6 +18,7 @@ use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException; use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
use Illuminate\Database\Eloquent\RelationNotFoundException; use Illuminate\Database\Eloquent\RelationNotFoundException;
use Illuminate\Encryption\MissingAppKeyException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exceptions\ThrottleRequestsException; use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@ -94,11 +95,6 @@ class Handler extends ExceptionHandler
*/ */
public function report(Throwable $exception) public function report(Throwable $exception)
{ {
if (! Schema::hasTable('accounts')) {
info('account table not found');
return;
}
if (Ninja::isHosted()) { if (Ninja::isHosted()) {
// if($exception instanceof ThrottleRequestsException && class_exists(\Modules\Admin\Events\ThrottledExceptionRaised::class)) { // if($exception instanceof ThrottleRequestsException && class_exists(\Modules\Admin\Events\ThrottledExceptionRaised::class)) {
@ -154,6 +150,10 @@ class Handler extends ExceptionHandler
} }
parent::report($exception); parent::report($exception);
if (Ninja::isSelfHost() && $exception instanceof MissingAppKeyException) {
info('To setup the app run: cp .env.example .env');
}
} }
private function validException($exception) private function validException($exception)

View File

@ -241,7 +241,7 @@ class CreditExport extends BaseExport
} }
if (in_array('credit.user_id', $this->input['report_keys'])) { if (in_array('credit.user_id', $this->input['report_keys'])) {
$entity['credit.user_id'] = $credit->user ? $credit->user->present()->name() : ''; $entity['credit.user_id'] = $credit->user ? $credit->user->present()->name() : ''; //@phpstan-ignore-line
} }
return $entity; return $entity;

View File

@ -85,7 +85,7 @@ class ExpenseExport extends BaseExport
->where('company_id', $this->company->id); ->where('company_id', $this->company->id);
if(!$this->input['include_deleted'] ?? false) { if(!$this->input['include_deleted'] ?? false) { // @phpstan-ignore-line
$query->where('is_deleted', 0); $query->where('is_deleted', 0);
} }
@ -259,10 +259,17 @@ class ExpenseExport extends BaseExport
{ {
$precision = $expense->currency->precision ?? 2; $precision = $expense->currency->precision ?? 2;
$entity['expense.net_amount'] = round($expense->amount, $precision);
if($expense->calculate_tax_by_amount) { if($expense->calculate_tax_by_amount) {
$total_tax_amount = round($expense->tax_amount1 + $expense->tax_amount2 + $expense->tax_amount3, $precision); $total_tax_amount = round($expense->tax_amount1 + $expense->tax_amount2 + $expense->tax_amount3, $precision);
if($expense->uses_inclusive_taxes) {
$entity['expense.net_amount'] = round($expense->amount, $precision) - $total_tax_amount;
}
else {
$entity['expense.net_amount'] = round($expense->amount, $precision);
}
} else { } else {
if($expense->uses_inclusive_taxes) { if($expense->uses_inclusive_taxes) {

View File

@ -63,7 +63,7 @@ class InvoiceExport extends BaseExport
->where('company_id', $this->company->id); ->where('company_id', $this->company->id);
if(!$this->input['include_deleted'] ?? false) { if(!$this->input['include_deleted'] ?? false) {// @phpstan-ignore-line
$query->where('is_deleted', 0); $query->where('is_deleted', 0);
} }
@ -166,7 +166,8 @@ class InvoiceExport extends BaseExport
} }
if (in_array('invoice.user_id', $this->input['report_keys'])) { if (in_array('invoice.user_id', $this->input['report_keys'])) {
$entity['invoice.user_id'] = $invoice->user ? $invoice->user->present()->name() : ''; $entity['invoice.user_id'] = $invoice->user ? $invoice->user->present()->name() : ''; // @phpstan-ignore-line
} }
return $entity; return $entity;

View File

@ -75,7 +75,7 @@ class InvoiceItemExport extends BaseExport
}) })
->where('company_id', $this->company->id); ->where('company_id', $this->company->id);
if(!$this->input['include_deleted'] ?? false) { if(!$this->input['include_deleted'] ?? false) {// @phpstan-ignore-line
$query->where('is_deleted', 0); $query->where('is_deleted', 0);
} }
@ -258,7 +258,7 @@ class InvoiceItemExport extends BaseExport
} }
if (in_array('invoice.user_id', $this->input['report_keys'])) { if (in_array('invoice.user_id', $this->input['report_keys'])) {
$entity['invoice.user_id'] = $invoice->user ? $invoice->user->present()->name() : ''; $entity['invoice.user_id'] = $invoice->user ? $invoice->user->present()->name() : '';// @phpstan-ignore-line
} }
return $entity; return $entity;

View File

@ -63,7 +63,7 @@ class PurchaseOrderExport extends BaseExport
}) })
->where('company_id', $this->company->id); ->where('company_id', $this->company->id);
if(!$this->input['include_deleted'] ?? false) { if(!$this->input['include_deleted'] ?? false) { // @phpstan-ignore-line
$query->where('is_deleted', 0); $query->where('is_deleted', 0);
} }
@ -167,7 +167,8 @@ class PurchaseOrderExport extends BaseExport
} }
if (in_array('purchase_order.user_id', $this->input['report_keys'])) { if (in_array('purchase_order.user_id', $this->input['report_keys'])) {
$entity['purchase_order.user_id'] = $purchase_order->user ? $purchase_order->user->present()->name() : ''; $entity['purchase_order.user_id'] = $purchase_order->user ? $purchase_order->user->present()->name() : ''; // @phpstan-ignore-line
} }
if (in_array('purchase_order.assigned_user_id', $this->input['report_keys'])) { if (in_array('purchase_order.assigned_user_id', $this->input['report_keys'])) {

View File

@ -152,22 +152,22 @@ class InvoiceFilters extends QueryFilters
{ {
return $this->builder->where(function ($query) { return $this->builder->where(function ($query) {
$query->whereIn('status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT]) $query->whereIn('invoices.status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
->where('is_deleted', 0) ->where('invoices.is_deleted', 0)
->where('balance', '>', 0) ->where('invoices.balance', '>', 0)
->where(function ($query) { ->orWhere(function ($query) {
$query->whereNull('due_date') $query->whereNull('invoices.due_date')
->orWhere(function ($q) { ->orWhere(function ($q) {
$q->where('due_date', '>=', now()->startOfDay()->subSecond())->where('partial', 0); $q->where('invoices.due_date', '>=', now()->startOfDay()->subSecond())->where('invoices.partial', 0);
}) })
->orWhere(function ($q) { ->orWhere(function ($q) {
$q->where('partial_due_date', '>=', now()->startOfDay()->subSecond())->where('partial', '>', 0); $q->where('invoices.partial_due_date', '>=', now()->startOfDay()->subSecond())->where('invoices.partial', '>', 0);
}); });
}) })
->orderByRaw('ISNULL(due_date), due_date ' . 'desc') ->orderByRaw('ISNULL(invoices.due_date), invoices.due_date ' . 'desc')
->orderByRaw('ISNULL(partial_due_date), partial_due_date ' . 'desc'); ->orderByRaw('ISNULL(invoices.partial_due_date), invoices.partial_due_date ' . 'desc');
}); });
} }
@ -320,7 +320,7 @@ class InvoiceFilters extends QueryFilters
{ {
$sort_col = explode('|', $sort); $sort_col = explode('|', $sort);
if (!is_array($sort_col) || count($sort_col) != 2) { if (!is_array($sort_col) || count($sort_col) != 2 || in_array($sort_col[0], ['documents'])) {
return $this->builder; return $this->builder;
} }
@ -337,10 +337,10 @@ class InvoiceFilters extends QueryFilters
// return $this->builder->orderByRaw('CAST(number AS UNSIGNED), number ' . $dir); // return $this->builder->orderByRaw('CAST(number AS UNSIGNED), number ' . $dir);
// return $this->builder->orderByRaw("number REGEXP '^[A-Za-z]+$',CAST(number as SIGNED INTEGER),CAST(REPLACE(number,'-','')AS SIGNED INTEGER) ,number"); // return $this->builder->orderByRaw("number REGEXP '^[A-Za-z]+$',CAST(number as SIGNED INTEGER),CAST(REPLACE(number,'-','')AS SIGNED INTEGER) ,number");
// return $this->builder->orderByRaw('ABS(number) ' . $dir); // return $this->builder->orderByRaw('ABS(number) ' . $dir);
return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir); return $this->builder->orderByRaw("REGEXP_REPLACE(invoices.number,'[^0-9]+','')+0 " . $dir);
} }
return $this->builder->orderBy($sort_col[0], $dir); return $this->builder->orderBy("{$this->builder->getQuery()->from}.".$sort_col[0], $dir);
} }
/** /**

View File

@ -107,16 +107,16 @@ class InvoiceSumInclusive
private function calculateCustomValues() private function calculateCustomValues()
{ {
$this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge1, $this->invoice->custom_surcharge_tax1); // $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge1, $this->invoice->custom_surcharge_tax1);
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge1); $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge1);
$this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge2, $this->invoice->custom_surcharge_tax2); // $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge2, $this->invoice->custom_surcharge_tax2);
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge2); $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge2);
$this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge3, $this->invoice->custom_surcharge_tax3); // $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge3, $this->invoice->custom_surcharge_tax3);
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge3); $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge3);
$this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge4, $this->invoice->custom_surcharge_tax4); // $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge4, $this->invoice->custom_surcharge_tax4);
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge4); $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge4);
$this->total += $this->total_custom_values; $this->total += $this->total_custom_values;
@ -137,21 +137,21 @@ class InvoiceSumInclusive
} }
//Handles cases where the surcharge is not taxed //Handles cases where the surcharge is not taxed
// if(is_numeric($this->invoice->custom_surcharge1) && $this->invoice->custom_surcharge1 > 0 && !$this->invoice->custom_surcharge_tax1) { if(is_numeric($this->invoice->custom_surcharge1) && $this->invoice->custom_surcharge1 > 0 && $this->invoice->custom_surcharge_tax1) {
// $amount += $this->invoice->custom_surcharge1; $amount += $this->invoice->custom_surcharge1;
// } }
// if(is_numeric($this->invoice->custom_surcharge2) && $this->invoice->custom_surcharge2 > 0 && !$this->invoice->custom_surcharge_tax2) { if(is_numeric($this->invoice->custom_surcharge2) && $this->invoice->custom_surcharge2 > 0 && $this->invoice->custom_surcharge_tax2) {
// $amount += $this->invoice->custom_surcharge2; $amount += $this->invoice->custom_surcharge2;
// } }
// if(is_numeric($this->invoice->custom_surcharge3) && $this->invoice->custom_surcharge3 > 0 && !$this->invoice->custom_surcharge_tax3) { if(is_numeric($this->invoice->custom_surcharge3) && $this->invoice->custom_surcharge3 > 0 && $this->invoice->custom_surcharge_tax3) {
// $amount += $this->invoice->custom_surcharge3; $amount += $this->invoice->custom_surcharge3;
// } }
// if(is_numeric($this->invoice->custom_surcharge4) && $this->invoice->custom_surcharge4 > 0 && !$this->invoice->custom_surcharge_tax4) { if(is_numeric($this->invoice->custom_surcharge4) && $this->invoice->custom_surcharge4 > 0 && $this->invoice->custom_surcharge_tax4) {
// $amount += $this->invoice->custom_surcharge4; $amount += $this->invoice->custom_surcharge4;
// } }
if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) { if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) {
$tax = $this->calcInclusiveLineTax($this->invoice->tax_rate1, $amount); $tax = $this->calcInclusiveLineTax($this->invoice->tax_rate1, $amount);

View File

@ -115,13 +115,8 @@ class AccountController extends BaseController
public function update(UpdateAccountRequest $request, Account $account) public function update(UpdateAccountRequest $request, Account $account)
{ {
$fi = new \FilesystemIterator(public_path('react'), \FilesystemIterator::SKIP_DOTS);
if (iterator_count($fi) < 30) { $account->set_react_as_default_ap = $request->input('set_react_as_default_ap');
return response()->json(['message' => 'React App Not Installed, Please install the React app before attempting to switch.'], 400);
}
$account->fill($request->all());
$account->save(); $account->save();
$this->entity_type = Account::class; $this->entity_type = Account::class;

View File

@ -35,6 +35,7 @@ use League\Fractal\Resource\Item;
use App\Models\BankTransactionRule; use App\Models\BankTransactionRule;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use App\Transformers\ArraySerializer; use App\Transformers\ArraySerializer;
use Illuminate\Support\Facades\Schema as DbSchema;
use App\Transformers\EntityTransformer; use App\Transformers\EntityTransformer;
use League\Fractal\Resource\Collection; use League\Fractal\Resource\Collection;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
@ -937,7 +938,9 @@ class BaseController extends Controller
} elseif (in_array($this->entity_type, [Design::class, GroupSetting::class, PaymentTerm::class, TaskStatus::class])) { } elseif (in_array($this->entity_type, [Design::class, GroupSetting::class, PaymentTerm::class, TaskStatus::class])) {
// nlog($this->entity_type); // nlog($this->entity_type);
} else { } else {
$query->where('user_id', '=', $user->id)->orWhere('assigned_user_id', $user->id); $query->where(function ($q) use ($user){ //grouping these together improves query performance significantly)
$q->where('user_id', '=', $user->id)->orWhere('assigned_user_id', $user->id);
});
} }
} }
@ -1104,7 +1107,7 @@ class BaseController extends Controller
public function flutterRoute() public function flutterRoute()
{ {
if ((bool) $this->checkAppSetup() !== false && $account = Account::first()) { if ((bool) $this->checkAppSetup() !== false && DbSchema::hasTable('accounts') && $account = Account::first()) {
/** @var \App\Models\Account $account */ /** @var \App\Models\Account $account */

View File

@ -11,8 +11,9 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Http\Requests\Chart\ShowChartRequest;
use App\Services\Chart\ChartService; use App\Services\Chart\ChartService;
use App\Http\Requests\Chart\ShowChartRequest;
use App\Http\Requests\Chart\ShowCalculatedFieldRequest;
class ChartController extends BaseController class ChartController extends BaseController
{ {
@ -65,5 +66,15 @@ class ChartController extends BaseController
return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200); return response()->json($cs->chart_summary($request->input('start_date'), $request->input('end_date')), 200);
} }
public function calculatedField(ShowCalculatedFieldRequest $request)
{
/** @var \App\Models\User auth()->user() */
$user = auth()->user();
$cs = new ChartService($user->company(), $user, $user->isAdmin());
$result = $cs->getCalculatedField($request->all());
return response()->json($result, 200);
} }
}

View File

@ -429,7 +429,7 @@ class ClientController extends BaseController
/** @var ?\Postmark\Models\DynamicResponseModel $response */ /** @var ?\Postmark\Models\DynamicResponseModel $response */
$response = $postmark->activateBounce((int)$bounce_id); $response = $postmark->activateBounce((int)$bounce_id);
if($response && $response?->Message == 'OK' && !$response->Bounce->Inactive && $response->Bounce->Email) { if($response && $response?->Message == 'OK' && !$response->Bounce->Inactive && $response->Bounce->Email) { // @phpstan-ignore-line
$email = $response->Bounce->Email; $email = $response->Bounce->Email;
//remove email from quarantine. //@TODO //remove email from quarantine. //@TODO

View File

@ -57,8 +57,9 @@ class ContactHashLoginController extends Controller
return render('generic.error', [ return render('generic.error', [
'title' => session()->get('title'), 'title' => session()->get('title'),
'notification' => session()->get('notification'), 'notification' => session()->get('notification'),
'account' => auth()->guard('contact')?->user()?->user?->account, 'account' => auth()->guard('contact')?->user()?->user?->account,// @phpstan-ignore-line
'company' => auth()->guard('contact')?->user()?->user?->company 'company' => auth()->guard('contact')?->user()?->user?->company // @phpstan-ignore-line
]); ]);
} }

View File

@ -20,7 +20,6 @@ use Illuminate\Http\Request;
*/ */
class MailgunWebhookController extends BaseController class MailgunWebhookController extends BaseController
{ {
private $invitation;
public function __construct() public function __construct()
{ {

View File

@ -81,4 +81,34 @@ class PingController extends BaseController
return response()->json(SystemHealth::check(), 200); return response()->json(SystemHealth::check(), 200);
} }
/**
* Get the last error from storage/logs/laravel.log
*
* @return Response| \Illuminate\Http\JsonResponse
*
* @OA\Get(
* path="/api/v1/last_error",
* operationId="getLastError",
* tags={"last_error"},
* summary="Get the last error from storage/logs/laravel.log",
* description="Get the last error from storage/logs/laravel.log",
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Response(
* response=200,
* description="The last error from the logs",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* )
* )
*/
public function lastError()
{
if (Ninja::isNinja() || ! auth()->user()->isAdmin()) {
return response()->json(['message' => ctrans('texts.route_not_available'), 'errors' => []], 403);
}
return response()->json(['last_error' => SystemHealth::lastError()], 200);
}
} }

View File

@ -36,8 +36,6 @@ class ReportExportController extends BaseController
return response()->json(['message' => 'Still working.....'], 409); return response()->json(['message' => 'Still working.....'], 409);
} }
if($report) {
Cache::forget($hash); Cache::forget($hash);
$headers = [ $headers = [
@ -50,7 +48,4 @@ class ReportExportController extends BaseController
}, $this->filename, $headers); }, $this->filename, $headers);
} }
}
} }

View File

@ -34,12 +34,9 @@ class ReportPreviewController extends BaseController
return response()->json(['message' => 'Still working.....'], 409); return response()->json(['message' => 'Still working.....'], 409);
} }
if($report) {
Cache::forget($hash); Cache::forget($hash);
return response()->json($report, 200); return response()->json($report, 200);
}
} }

View File

@ -81,14 +81,20 @@ class SearchController extends Controller
$invoices = Invoice::query() $invoices = Invoice::query()
->company() ->company()
->with('client') ->with('client')
->where('is_deleted', 0) ->where('invoices.is_deleted', 0)
->whereHas('client', function ($q) { // ->whereHas('client', function ($q) {
$q->where('is_deleted', 0); // $q->where('is_deleted', 0);
// })
->leftJoin('clients', function ($join) {
$join->on('invoices.client_id', '=', 'clients.id')
->where('clients.is_deleted', 0);
}) })
->when(!$user->hasPermission('view_all') || !$user->hasPermission('view_invoice'), function ($query) use ($user) { ->when(!$user->hasPermission('view_all') || !$user->hasPermission('view_invoice'), function ($query) use ($user) {
$query->where('user_id', $user->id); $query->where('invoices.user_id', $user->id);
}) })
->orderBy('id', 'desc') ->orderBy('invoices.id', 'desc')
->take(3000) ->take(3000)
->get(); ->get();

View File

@ -35,6 +35,7 @@ class SelfUpdateController extends BaseController
'bootstrap/cache/services.php', 'bootstrap/cache/services.php',
'bootstrap/cache/routes-v7.php', 'bootstrap/cache/routes-v7.php',
'bootstrap/cache/livewire-components.php', 'bootstrap/cache/livewire-components.php',
'public/index.html',
]; ];
public function __construct() public function __construct()
@ -114,33 +115,33 @@ class SelfUpdateController extends BaseController
Artisan::call('config:clear'); Artisan::call('config:clear');
Artisan::call('cache:clear'); Artisan::call('cache:clear');
$this->runModelChecks(); // $this->runModelChecks();
nlog('Called Artisan commands'); nlog('Called Artisan commands');
return response()->json(['message' => 'Update completed'], 200); return response()->json(['message' => 'Update completed'], 200);
} }
private function runModelChecks() // private function runModelChecks()
{ // {
Company::query() // Company::query()
->cursor() // ->cursor()
->each(function ($company) { // ->each(function ($company) {
$settings = $company->settings; // $settings = $company->settings;
if(property_exists($settings->pdf_variables, 'purchase_order_details')) { // if(property_exists($settings->pdf_variables, 'purchase_order_details')) {
return; // return;
} // }
$pdf_variables = $settings->pdf_variables; // $pdf_variables = $settings->pdf_variables;
$pdf_variables->purchase_order_details = []; // $pdf_variables->purchase_order_details = [];
$settings->pdf_variables = $pdf_variables; // $settings->pdf_variables = $pdf_variables;
$company->settings = $settings; // $company->settings = $settings;
$company->save(); // $company->save();
}); // });
} // }
private function clearCacheDir() private function clearCacheDir()
{ {
@ -159,7 +160,7 @@ class SelfUpdateController extends BaseController
$directoryIterator = new \RecursiveDirectoryIterator(base_path(), \RecursiveDirectoryIterator::SKIP_DOTS); $directoryIterator = new \RecursiveDirectoryIterator(base_path(), \RecursiveDirectoryIterator::SKIP_DOTS);
foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) { foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {
if (strpos($file->getPathname(), '.git') !== false) { if (strpos($file->getPathname(), '.git') !== false || strpos($file->getPathname(), 'vendor/') !== false) {
continue; continue;
} }
@ -180,6 +181,9 @@ class SelfUpdateController extends BaseController
public function checkVersion() public function checkVersion()
{ {
if(Ninja::isHosted())
return '5.10.SaaS';
return trim(file_get_contents(config('ninja.version_url'))); return trim(file_get_contents(config('ninja.version_url')));
} }

View File

@ -43,7 +43,7 @@ class SetupController extends Controller
public function index() public function index()
{ {
$check = SystemHealth::check(false); $check = SystemHealth::check(false, false);
if ($check['system_health'] == true && $check['simple_db_check'] && Schema::hasTable('accounts') && $account = Account::first()) { if ($check['system_health'] == true && $check['simple_db_check'] && Schema::hasTable('accounts') && $account = Account::first()) {
return redirect('/'); return redirect('/');
@ -59,7 +59,7 @@ class SetupController extends Controller
public function doSetup(StoreSetupRequest $request) public function doSetup(StoreSetupRequest $request)
{ {
try { try {
$check = SystemHealth::check(false); $check = SystemHealth::check(false, false);
} catch (Exception $e) { } catch (Exception $e) {
nlog(['message' => $e->getMessage(), 'action' => 'SetupController::doSetup()']); nlog(['message' => $e->getMessage(), 'action' => 'SetupController::doSetup()']);
@ -146,6 +146,7 @@ class SetupController extends Controller
Artisan::call('config:clear'); Artisan::call('config:clear');
Artisan::call('key:generate', ['--force' => true]);
Artisan::call('migrate', ['--force' => true]); Artisan::call('migrate', ['--force' => true]);
Artisan::call('db:seed', ['--force' => true]); Artisan::call('db:seed', ['--force' => true]);
Artisan::call('config:clear'); Artisan::call('config:clear');

View File

@ -164,7 +164,7 @@ class TwilioController extends BaseController
return response()->json(['message' => 'Please update your first and/or last name in the User Details before verifying your number.'], 400); return response()->json(['message' => 'Please update your first and/or last name in the User Details before verifying your number.'], 400);
} }
if (!$user->phone || $user->phone == '') { if (!$user->phone || empty($user->phone)) {
return response()->json(['message' => 'User found, but no valid phone number on file, please contact support.'], 400); return response()->json(['message' => 'User found, but no valid phone number on file, please contact support.'], 400);
} }

View File

@ -56,7 +56,8 @@ class SetDomainNameDb
return response()->json($error, 403); return response()->json($error, 403);
} else { } else {
MultiDB::setDb('db-ninja-01'); MultiDB::setDb('db-ninja-01');
nlog('I could not set the DB - defaulting to DB1'); nlog('SetDomainNameDb:: I could not set the DB - defaulting to DB1');
$request->session()->invalidate();
//abort(400, 'Domain not found'); //abort(400, 'Domain not found');
} }
} }
@ -73,7 +74,8 @@ class SetDomainNameDb
return response()->json($error, 403); return response()->json($error, 403);
} else { } else {
MultiDB::setDb('db-ninja-01'); MultiDB::setDb('db-ninja-01');
nlog('I could not set the DB - defaulting to DB1'); nlog('SetDomainNameDb:: I could not set the DB - defaulting to DB1');
$request->session()->invalidate();
} }
} }
} }

View File

@ -0,0 +1,78 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\Chart;
use App\Http\Requests\Request;
use App\Utils\Traits\MakesDates;
class ShowCalculatedFieldRequest extends Request
{
use MakesDates;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
/**@var \App\Models\User auth()->user */
$user = auth()->user();
return $user->isAdmin() || $user->hasPermission('view_dashboard');
}
public function rules()
{
return [
'date_range' => 'bail|sometimes|string|in:last7_days,last30_days,last365_days,this_month,last_month,this_quarter,last_quarter,this_year,last_year,all_time,custom',
'start_date' => 'bail|sometimes|date',
'end_date' => 'bail|sometimes|date',
'field' => 'required|bail|in:active_invoices, outstanding_invoices, completed_payments, refunded_payments, active_quotes, unapproved_quotes, logged_tasks, invoiced_tasks, paid_tasks, logged_expenses, pending_expenses, invoiced_expenses, invoice_paid_expenses',
'calculation' => 'required|bail|in:sum,avg,count',
'period' => 'required|bail|in:current,previous,total',
'format' => 'sometimes|bail|in:time,money',
];
}
public function prepareForValidation()
{
/**@var \App\Models\User auth()->user */
$user = auth()->user();
$input = $this->all();
if(isset($input['date_range'])) {
$dates = $this->calculateStartAndEndDates($input, $user->company());
$input['start_date'] = $dates[0];
$input['end_date'] = $dates[1];
}
if (! isset($input['start_date'])) {
$input['start_date'] = now()->subDays(20)->format('Y-m-d');
}
if (! isset($input['end_date'])) {
$input['end_date'] = now()->format('Y-m-d');
}
if(isset($input['period']) && $input['period'] == 'previous')
{
$dates = $this->calculatePreviousPeriodStartAndEndDates($input, $user->company());
$input['start_date'] = $dates[0];
$input['end_date'] = $dates[1];
}
$this->replace($input);
}
}

View File

@ -170,7 +170,7 @@ class UpdateClientRequest extends Request
* down to the free plan setting properties which * down to the free plan setting properties which
* are saveable * are saveable
* *
* @param \stdClass $settings * @param mixed $settings
* @return \stdClass $settings * @return \stdClass $settings
*/ */
private function filterSaveableSettings($settings) private function filterSaveableSettings($settings)

View File

@ -15,10 +15,8 @@ use App\Utils\Ninja;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\DataMapper\CompanySettings; use App\DataMapper\CompanySettings;
use InvoiceNinja\EInvoice\EInvoice;
use App\Http\ValidationRules\ValidSettingsRule; use App\Http\ValidationRules\ValidSettingsRule;
use InvoiceNinja\EInvoice\Models\Peppol\Invoice; use App\Http\ValidationRules\EInvoice\ValidCompanyScheme;
use App\Http\ValidationRules\EInvoice\ValidScheme;
use App\Http\ValidationRules\Company\ValidSubdomain; use App\Http\ValidationRules\Company\ValidSubdomain;
class UpdateCompanyRequest extends Request class UpdateCompanyRequest extends Request
@ -67,7 +65,7 @@ class UpdateCompanyRequest extends Request
$rules['smtp_local_domain'] = 'sometimes|string|nullable'; $rules['smtp_local_domain'] = 'sometimes|string|nullable';
// $rules['smtp_verify_peer'] = 'sometimes|string'; // $rules['smtp_verify_peer'] = 'sometimes|string';
// $rules['e_invoice'] = ['sometimes','nullable', new ValidScheme()]; $rules['e_invoice'] = ['sometimes','nullable', new ValidCompanyScheme()];
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) { if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
$rules['portal_domain'] = 'bail|nullable|sometimes|url'; $rules['portal_domain'] = 'bail|nullable|sometimes|url';

View File

@ -18,7 +18,6 @@ class ClientEmailHistoryRequest extends Request
{ {
use MakesHash; use MakesHash;
private string $error_message = '';
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *

View File

@ -20,7 +20,6 @@ class EntityEmailHistoryRequest extends Request
{ {
use MakesHash; use MakesHash;
private string $error_message = '';
private string $entity_plural = ''; private string $entity_plural = '';
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.

View File

@ -63,7 +63,7 @@ class SendEmailRequest extends Request
$user = auth()->user(); $user = auth()->user();
return [ return [
'template' => 'bail|required|in:'.implode(',', $this->templates), 'template' => 'bail|required|string|in:'.implode(',', $this->templates),
'entity' => 'bail|required|in:App\Models\Invoice,App\Models\Quote,App\Models\Credit,App\Models\RecurringInvoice,App\Models\PurchaseOrder,App\Models\Payment', 'entity' => 'bail|required|in:App\Models\Invoice,App\Models\Quote,App\Models\Credit,App\Models\RecurringInvoice,App\Models\PurchaseOrder,App\Models\Payment',
'entity_id' => ['bail', 'required', Rule::exists($this->entity_plural, 'id')->where('company_id', $user->company()->id)], 'entity_id' => ['bail', 'required', Rule::exists($this->entity_plural, 'id')->where('company_id', $user->company()->id)],
'cc_email.*' => 'bail|sometimes|email', 'cc_email.*' => 'bail|sometimes|email',

View File

@ -78,7 +78,9 @@ class StoreInvoiceRequest extends Request
$rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['exchange_rate'] = 'bail|sometimes|numeric'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0'; $rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0';
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date']; $rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date ?? '') > 1), 'date'];
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999']; $rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
// $rules['amount'] = ['sometimes', 'bail', 'max:99999999999999']; // $rules['amount'] = ['sometimes', 'bail', 'max:99999999999999'];

View File

@ -82,8 +82,8 @@ class UpdateInvoiceRequest extends Request
$rules['date'] = 'bail|sometimes|date:Y-m-d'; $rules['date'] = 'bail|sometimes|date:Y-m-d';
// $rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date']; $rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
// $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date']; $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
return $rules; return $rules;
} }

View File

@ -80,7 +80,7 @@ class StoreProjectRequest extends Request
$input['budgeted_hours'] = 0; $input['budgeted_hours'] = 0;
} }
$input['task_rate'] = isset($input['task_rate']) ? $input['task_rate'] : 0; $input['task_rate'] = (isset($input['task_rate']) && floatval($input['task_rate']) >= 0) ? $input['task_rate'] : 0;
$this->replace($input); $this->replace($input);
} }

View File

@ -45,7 +45,8 @@ class UpdateProjectRequest extends Request
$rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id)->ignore($this->project->id); $rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id)->ignore($this->project->id);
} }
$rules['budgeted_hours'] = 'sometimes|numeric'; $rules['budgeted_hours'] = 'sometimes|bail|numeric';
$rules['task_rate'] = 'sometimes|bail|numeric';
if ($this->file('documents') && is_array($this->file('documents'))) { if ($this->file('documents') && is_array($this->file('documents'))) {
$rules['documents.*'] = $this->fileValidation(); $rules['documents.*'] = $this->fileValidation();

View File

@ -66,8 +66,8 @@ class StoreQuoteRequest extends Request
$rules['exchange_rate'] = 'bail|sometimes|numeric'; $rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['line_items'] = 'array'; $rules['line_items'] = 'array';
$rules['date'] = 'bail|sometimes|date:Y-m-d'; $rules['date'] = 'bail|sometimes|date:Y-m-d';
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date', 'after_or_equal:date']; $rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date']; $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date ?? '') > 1), 'date'];
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999']; $rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
return $rules; return $rules;

View File

@ -65,7 +65,7 @@ class UpdateQuoteRequest extends Request
$rules['date'] = 'bail|sometimes|date:Y-m-d'; $rules['date'] = 'bail|sometimes|date:Y-m-d';
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date']; $rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date']; $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999']; $rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];

View File

@ -126,7 +126,7 @@ class UpdateRecurringInvoiceRequest extends Request
} }
if (isset($input['line_items'])) { if (isset($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; $input['line_items'] = $this->cleanItems($input['line_items']);
$input['amount'] = $this->entityTotalAmount($input['line_items']); $input['amount'] = $this->entityTotalAmount($input['line_items']);
} }

View File

@ -17,7 +17,6 @@ use Illuminate\Auth\Access\AuthorizationException;
class GenericReportRequest extends Request class GenericReportRequest extends Request
{ {
private string $error_message = '';
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.

View File

@ -77,7 +77,7 @@ class UpdateUserRequest extends Request
unset($input['oauth_user_token']); unset($input['oauth_user_token']);
} }
if(isset($input['password']) && strlen($input['password'] ?? '') > 1) if(isset($input['password']) && is_string($input['password']))
{ {
$input['password'] = trim($input['password']); $input['password'] = trim($input['password']);
} }

View File

@ -11,6 +11,7 @@
namespace App\Http\ValidationRules\EInvoice; namespace App\Http\ValidationRules\EInvoice;
use App\Services\EDocument\Standards\Validation\Peppol\CompanyLevel;
use Closure; use Closure;
use InvoiceNinja\EInvoice\EInvoice; use InvoiceNinja\EInvoice\EInvoice;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
@ -19,9 +20,9 @@ use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Contracts\Validation\ValidatorAwareRule; use Illuminate\Contracts\Validation\ValidatorAwareRule;
/** /**
* Class BlackListRule. * Class ValidScheme.
*/ */
class ValidScheme implements ValidationRule, ValidatorAwareRule class ValidCompanyScheme implements ValidationRule, ValidatorAwareRule
{ {
/** /**
@ -35,7 +36,7 @@ class ValidScheme implements ValidationRule, ValidatorAwareRule
{ {
$r = new EInvoice(); $r = new EInvoice();
$errors = $r->validateRequest($value['Invoice'], Invoice::class); $errors = $r->validateRequest($value['Invoice'], CompanyLevel::class);
foreach ($errors as $key => $msg) { foreach ($errors as $key => $msg) {

View File

@ -51,7 +51,7 @@ class HasValidPhoneNumber implements Rule
$twilio = new \Twilio\Rest\Client($sid, $token); $twilio = new \Twilio\Rest\Client($sid, $token);
$country = auth()->user()->account?->companies()?->first()?->country(); $country = auth()->user()->account?->companies()?->first()?->country(); //@phpstan-ignore-line
if (!$country || strlen(auth()->user()->phone) < 2) { if (!$country || strlen(auth()->user()->phone) < 2) {
return true; return true;

View File

@ -47,7 +47,7 @@ class InvoiceTransformer extends BaseTransformer
'due_date' => isset($invoice_data['Due Date']) ? $this->parseDate($invoice_data['Due Date']) : null, 'due_date' => isset($invoice_data['Due Date']) ? $this->parseDate($invoice_data['Due Date']) : null,
'po_number' => $this->getString($invoice_data, 'PurchaseOrder'), 'po_number' => $this->getString($invoice_data, 'PurchaseOrder'),
'public_notes' => $this->getString($invoice_data, 'Notes'), 'public_notes' => $this->getString($invoice_data, 'Notes'),
'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'), // 'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'),
'amount' => $this->getFloat($invoice_data, 'Total'), 'amount' => $this->getFloat($invoice_data, 'Total'),
'balance' => $this->getFloat($invoice_data, 'Balance'), 'balance' => $this->getFloat($invoice_data, 'Balance'),
'status_id' => $invoiceStatusMap[$status = 'status_id' => $invoiceStatusMap[$status =

View File

@ -11,16 +11,17 @@
namespace App\Jobs\Cron; namespace App\Jobs\Cron;
use App\Events\Expense\ExpenseWasCreated; use App\Utils\Ninja;
use App\Factory\RecurringExpenseToExpenseFactory;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use Illuminate\Support\Carbon;
use App\Models\RecurringExpense; use App\Models\RecurringExpense;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use App\Utils\Traits\GeneratesCounter;
use App\Events\Expense\ExpenseWasCreated;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Factory\RecurringExpenseToExpenseFactory;
use App\Libraries\Currency\Conversion\CurrencyApi;
class RecurringExpensesCron class RecurringExpensesCron
{ {
@ -109,6 +110,15 @@ class RecurringExpensesCron
$expense->payment_date = now()->format('Y-m-d'); $expense->payment_date = now()->format('Y-m-d');
} }
if ((int)$expense->company->settings->currency_id != $expense->currency_id) {
$exchange_rate = new CurrencyApi();
$expense->exchange_rate = $exchange_rate->exchangeRate($expense->currency_id, (int)$expense->company->settings->currency_id, Carbon::parse($expense->date));
}
else {
$expense->exchange_rate = 1;
}
$expense->number = $this->getNextExpenseNumber($expense); $expense->number = $this->getNextExpenseNumber($expense);
$expense->saveQuietly(); $expense->saveQuietly();

View File

@ -48,12 +48,12 @@ class RecurringInvoicesCron
Auth::logout(); Auth::logout();
if (! config('ninja.db.multi_db_enabled')) { if (! config('ninja.db.multi_db_enabled')) {
$recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE) $recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
->where('is_deleted', false) ->where('recurring_invoices.is_deleted', false)
->where('remaining_cycles', '!=', '0') ->where('recurring_invoices.remaining_cycles', '!=', '0')
->whereNotNull('next_send_date') ->whereNotNull('recurring_invoices.next_send_date')
->whereNull('deleted_at') ->whereNull('recurring_invoices.deleted_at')
->where('next_send_date', '<=', now()->toDateTimeString()) ->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) { ->whereHas('client', function ($query) {
$query->where('is_deleted', 0) $query->where('is_deleted', 0)
->where('deleted_at', null); ->where('deleted_at', null);
@ -87,18 +87,27 @@ class RecurringInvoicesCron
foreach (MultiDB::$dbs as $db) { foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db); MultiDB::setDB($db);
$recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE) $recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
->where('is_deleted', false) ->where('recurring_invoices.is_deleted', false)
->where('remaining_cycles', '!=', '0') ->where('recurring_invoices.remaining_cycles', '!=', '0')
->whereNull('deleted_at') ->whereNull('recurring_invoices.deleted_at')
->whereNotNull('next_send_date') ->whereNotNull('recurring_invoices.next_send_date')
->where('next_send_date', '<=', now()->toDateTimeString()) ->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) { // ->whereHas('client', function ($query) {
$query->where('is_deleted', 0) // $query->where('is_deleted', 0)
->where('deleted_at', null); // ->where('deleted_at', null);
// })
// ->whereHas('company', function ($query) {
// $query->where('is_disabled', 0);
// })
->leftJoin('clients', function ($join) {
$join->on('recurring_invoices.client_id', '=', 'clients.id')
->where('clients.is_deleted', 0)
->whereNull('clients.deleted_at');
}) })
->whereHas('company', function ($query) { ->leftJoin('companies', function ($join) {
$query->where('is_disabled', 0); $join->on('recurring_invoices.company_id', '=', 'companies.id')
->where('companies.is_disabled', 0);
}) })
->with('company') ->with('company')
->cursor(); ->cursor();

View File

@ -86,6 +86,9 @@ class UpdateCalculatedFields
foreach(json_decode($task->time_log) as $log) { foreach(json_decode($task->time_log) as $log) {
if(!is_array($log))
continue;
$start_time = $log[0]; $start_time = $log[0];
$end_time = $log[1] == 0 ? time() : $log[1]; $end_time = $log[1] == 0 ? time() : $log[1];

View File

@ -51,20 +51,29 @@ class InvoiceCheckLateWebhook implements ShouldQueue
->pluck('company_id'); ->pluck('company_id');
Invoice::query() Invoice::query()
->where('is_deleted', false) ->where('invoices.is_deleted', false)
->whereNull('deleted_at') ->whereNull('invoices.deleted_at')
->whereNotNull('due_date') ->whereNotNull('invoices.due_date')
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) ->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0) ->where('invoices.balance', '>', 0)
->whereIn('company_id', $company_ids) ->whereIn('invoices.company_id', $company_ids)
->whereHas('client', function ($query) { // ->whereHas('client', function ($query) {
$query->where('is_deleted', 0) // $query->where('is_deleted', 0)
->where('deleted_at', null); // ->where('deleted_at', null);
// })
// ->whereHas('company', function ($query) {
// $query->where('is_disabled', 0);
// })
->leftJoin('clients', function ($join) {
$join->on('invoices.client_id', '=', 'clients.id')
->where('clients.is_deleted', 0)
->whereNull('clients.deleted_at');
}) })
->whereHas('company', function ($query) { ->leftJoin('companies', function ($join) {
$query->where('is_disabled', 0); $join->on('invoices.company_id', '=', 'companies.id')
->where('companies.is_disabled', 0);
}) })
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()]) ->whereBetween('invoices.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor() ->cursor()
->each(function ($invoice) { ->each(function ($invoice) {
(new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle(); (new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle();
@ -78,20 +87,29 @@ class InvoiceCheckLateWebhook implements ShouldQueue
->pluck('company_id'); ->pluck('company_id');
Invoice::query() Invoice::query()
->where('is_deleted', false) ->where('invoices.is_deleted', false)
->whereNull('deleted_at') ->whereNull('invoices.deleted_at')
->whereNotNull('due_date') ->whereNotNull('invoices.due_date')
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) ->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0) ->where('invoices.balance', '>', 0)
->whereIn('company_id', $company_ids) ->whereIn('invoices.company_id', $company_ids)
->whereHas('client', function ($query) { // ->whereHas('client', function ($query) {
$query->where('is_deleted', 0) // $query->where('is_deleted', 0)
->where('deleted_at', null); // ->where('deleted_at', null);
// })
// ->whereHas('company', function ($query) {
// $query->where('is_disabled', 0);
// })
->leftJoin('clients', function ($join) {
$join->on('invoices.client_id', '=', 'clients.id')
->where('clients.is_deleted', 0)
->whereNull('clients.deleted_at');
}) })
->whereHas('company', function ($query) { ->leftJoin('companies', function ($join) {
$query->where('is_disabled', 0); $join->on('invoices.company_id', '=', 'companies.id')
->where('companies.is_disabled', 0);
}) })
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()]) ->whereBetween('invoices.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor() ->cursor()
->each(function ($invoice) { ->each(function ($invoice) {
(new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle(); (new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle();

View File

@ -49,10 +49,10 @@ class QuoteCheckExpired implements ShouldQueue
{ {
if (! config('ninja.db.multi_db_enabled')) { if (! config('ninja.db.multi_db_enabled')) {
Quote::query() Quote::query()
->where('status_id', Quote::STATUS_SENT) ->where('quotes.status_id', Quote::STATUS_SENT)
->where('is_deleted', false) ->where('quotes.is_deleted', false)
->whereNull('deleted_at') ->whereNull('quotes.deleted_at')
->whereNotNull('due_date') ->whereNotNull('quotes.due_date')
->whereHas('client', function ($query) { ->whereHas('client', function ($query) {
$query->where('is_deleted', 0) $query->where('is_deleted', 0)
->where('deleted_at', null); ->where('deleted_at', null);
@ -60,8 +60,8 @@ class QuoteCheckExpired implements ShouldQueue
->whereHas('company', function ($query) { ->whereHas('company', function ($query) {
$query->where('is_disabled', 0); $query->where('is_disabled', 0);
}) })
// ->where('due_date', '<='. now()->toDateTimeString())
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()]) ->whereBetween('quotes.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor() ->cursor()
->each(function ($quote) { ->each(function ($quote) {
$this->queueExpiredQuoteNotification($quote); $this->queueExpiredQuoteNotification($quote);
@ -71,10 +71,10 @@ class QuoteCheckExpired implements ShouldQueue
MultiDB::setDB($db); MultiDB::setDB($db);
Quote::query() Quote::query()
->where('status_id', Quote::STATUS_SENT) ->where('quotes.status_id', Quote::STATUS_SENT)
->where('is_deleted', false) ->where('quotes.is_deleted', false)
->whereNull('deleted_at') ->whereNull('quotes.deleted_at')
->whereNotNull('due_date') ->whereNotNull('quotes.due_date')
->whereHas('client', function ($query) { ->whereHas('client', function ($query) {
$query->where('is_deleted', 0) $query->where('is_deleted', 0)
->where('deleted_at', null); ->where('deleted_at', null);
@ -82,8 +82,8 @@ class QuoteCheckExpired implements ShouldQueue
->whereHas('company', function ($query) { ->whereHas('company', function ($query) {
$query->where('is_disabled', 0); $query->where('is_disabled', 0);
}) })
// ->where('due_date', '<='. now()->toDateTimeString())
->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()]) ->whereBetween('quotes.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
->cursor() ->cursor()
->each(function ($quote) { ->each(function ($quote) {
$this->queueExpiredQuoteNotification($quote); $this->queueExpiredQuoteNotification($quote);

View File

@ -13,14 +13,15 @@ namespace App\Jobs\Task;
use App\Models\Task; use App\Models\Task;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Models\CompanyUser;
use App\Services\Email\Email; use App\Services\Email\Email;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use App\Services\Email\EmailObject; use App\Services\Email\EmailObject;
use Illuminate\Mail\Mailables\Address; use Illuminate\Mail\Mailables\Address;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use App\Utils\Traits\Notifications\UserNotifies; use App\Utils\Traits\Notifications\UserNotifies;
@ -47,7 +48,7 @@ class TaskAssigned implements ShouldQueue
$company_user = $this->task->assignedCompanyUser(); $company_user = $this->task->assignedCompanyUser();
if($this->findEntityAssignedNotification($company_user, 'task')) if(($company_user instanceof CompanyUser) && $this->findEntityAssignedNotification($company_user, 'task'))
{ {
$mo = new EmailObject(); $mo = new EmailObject();
$mo->subject = ctrans('texts.task_assigned_subject', ['task' => $this->task->number, 'date' => now()->setTimeZone($this->task->company->timezone()->name)->format($this->task->company->date_format()) ]); $mo->subject = ctrans('texts.task_assigned_subject', ['task' => $this->task->number, 'date' => now()->setTimeZone($this->task->company->timezone()->name)->format($this->task->company->date_format()) ]);

View File

@ -61,10 +61,10 @@ class QuoteReminderJob implements ShouldQueue
nrlog("Sending quote reminders on ".now()->format('Y-m-d h:i:s')); nrlog("Sending quote reminders on ".now()->format('Y-m-d h:i:s'));
Quote::query() Quote::query()
->where('is_deleted', 0) ->where('quotes.is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT]) ->whereIn('quotes.status_id', [Invoice::STATUS_SENT])
->whereNull('deleted_at') ->whereNull('quotes.deleted_at')
->where('next_send_date', '<=', now()->toDateTimeString()) ->where('quotes.next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) { ->whereHas('client', function ($query) {
$query->where('is_deleted', 0) $query->where('is_deleted', 0)
->where('deleted_at', null); ->where('deleted_at', null);
@ -88,10 +88,10 @@ class QuoteReminderJob implements ShouldQueue
nrlog("Sending quote reminders on db {$db} ".now()->format('Y-m-d h:i:s')); nrlog("Sending quote reminders on db {$db} ".now()->format('Y-m-d h:i:s'));
Quote::query() Quote::query()
->where('is_deleted', 0) ->where('quotes.is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT]) ->whereIn('quotes.status_id', [Invoice::STATUS_SENT])
->whereNull('deleted_at') ->whereNull('quotes.deleted_at')
->where('next_send_date', '<=', now()->toDateTimeString()) ->where('quotes.next_send_date', '<=', now()->toDateTimeString())
->whereHas('client', function ($query) { ->whereHas('client', function ($query) {
$query->where('is_deleted', 0) $query->where('is_deleted', 0)
->where('deleted_at', null); ->where('deleted_at', null);
@ -99,6 +99,7 @@ class QuoteReminderJob implements ShouldQueue
->whereHas('company', function ($query) { ->whereHas('company', function ($query) {
$query->where('is_disabled', 0); $query->where('is_disabled', 0);
}) })
->with('invitations')->chunk(50, function ($quotes) { ->with('invitations')->chunk(50, function ($quotes) {
foreach ($quotes as $quote) { foreach ($quotes as $quote) {

View File

@ -71,7 +71,7 @@ class ReminderJob implements ShouldQueue
->whereHas('company', function ($query) { ->whereHas('company', function ($query) {
$query->where('is_disabled', 0); $query->where('is_disabled', 0);
}) })
->with('invitations')->chunk(50, function ($invoices) { ->with('invitations')->chunk(200, function ($invoices) {
foreach ($invoices as $invoice) { foreach ($invoices as $invoice) {
$this->sendReminderForInvoice($invoice); $this->sendReminderForInvoice($invoice);
} }
@ -99,7 +99,7 @@ class ReminderJob implements ShouldQueue
->whereHas('company', function ($query) { ->whereHas('company', function ($query) {
$query->where('is_disabled', 0); $query->where('is_disabled', 0);
}) })
->with('invitations')->chunk(50, function ($invoices) { ->with('invitations')->chunk(200, function ($invoices) {
foreach ($invoices as $invoice) { foreach ($invoices as $invoice) {
$this->sendReminderForInvoice($invoice); $this->sendReminderForInvoice($invoice);

View File

@ -129,7 +129,7 @@ class BaseModel extends Model
/** @var \App\Models\User $user */ /** @var \App\Models\User $user */
$user = auth()->user(); $user = auth()->user();
$query->where('company_id', $user->companyId()); $query->where("{$query->getQuery()->from}.company_id", $user->companyId());
return $query; return $query;
} }

View File

@ -112,12 +112,12 @@ use Laracasts\Presenter\PresentableTrait;
* @property int $notify_vendor_when_paid * @property int $notify_vendor_when_paid
* @property int $invoice_task_hours * @property int $invoice_task_hours
* @property int $deleted_at * @property int $deleted_at
* @property string $smtp_username * @property string|null $smtp_username
* @property string $smtp_password * @property string|null $smtp_password
* @property string $smtp_host * @property string|null $smtp_host
* @property string $smtp_port * @property string|null $smtp_port
* @property string $smtp_encryption * @property string|null $smtp_encryption
* @property string $smtp_local_domain * @property string|null $smtp_local_domain
* @property boolean $smtp_verify_peer * @property boolean $smtp_verify_peer
* @property-read \App\Models\Account $account * @property-read \App\Models\Account $account
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities

View File

@ -107,7 +107,6 @@ use Laracasts\Presenter\PresentableTrait;
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Backup> $history * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Backup> $history
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\QuoteInvitation> $invitations * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\QuoteInvitation> $invitations
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel company()
* @mixin \Eloquent * @mixin \Eloquent
* @mixin \Illuminate\Database\Eloquent\Builder * @mixin \Illuminate\Database\Eloquent\Builder
*/ */

View File

@ -186,7 +186,7 @@ class Task extends BaseModel
} }
if($this->status) { if($this->status) {
return '<h5><span class="badge badge-primary">' . $this->status?->name ?? ''; return '<h5><span class="badge badge-primary">' . $this->status?->name ?? ''; //@phpstan-ignore-line
} }
return ''; return '';

View File

@ -136,16 +136,16 @@ class EntityViewedNotification extends Notification
// return $data; // return $data;
// } // }
private function buildSubject() // private function buildSubject()
{ // {
$subject = ctrans( // $subject = ctrans(
"texts.notification_{$this->entity_name}_viewed_subject", // "texts.notification_{$this->entity_name}_viewed_subject",
[ // [
'client' => $this->contact->present()->name(), // 'client' => $this->contact->present()->name(),
$this->entity_name => $this->entity->number, // $this->entity_name => $this->entity->number,
] // ]
); // );
return $subject; // return $subject;
} // }
} }

View File

@ -131,9 +131,11 @@ class BTCPayPaymentDriver extends BaseDriver
$this->payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$btcpayRep->metadata->InvoiceNinjaPaymentHash])->firstOrFail(); $this->payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$btcpayRep->metadata->InvoiceNinjaPaymentHash])->firstOrFail();
$StatusId = Payment::STATUS_PENDING; $StatusId = Payment::STATUS_PENDING;
if ($this->payment_hash->payment_id == null) { if ($this->payment_hash->payment_id == null) {
//$_invoice = collect($this->payment_hash->data->invoices)->first();
$_invoice = Invoice::query()->where('number', $btcpayRep->metadata->orderId)->first(); $_invoice = Invoice::with('client')->withTrashed()->find($this->payment_hash->fee_invoice_id);
$this->client = Client::find($_invoice->client_id);
$this->client = $_invoice->client;
$dataPayment = [ $dataPayment = [
'payment_method' => $this->payment_method, 'payment_method' => $this->payment_method,
'payment_type' => PaymentType::CRYPTO, 'payment_type' => PaymentType::CRYPTO,
@ -144,7 +146,7 @@ class BTCPayPaymentDriver extends BaseDriver
$payment = $this->createPayment($dataPayment, $StatusId); $payment = $this->createPayment($dataPayment, $StatusId);
} else { } else {
/** @var \App\Models\Payment $payment */ /** @var \App\Models\Payment $payment */
$payment = Payment::find($this->payment_hash->payment_id); $payment = Payment::withTrashed()->find($this->payment_hash->payment_id);
$StatusId = $payment->status_id; $StatusId = $payment->status_id;
} }
switch ($btcpayRep->type) { switch ($btcpayRep->type) {

View File

@ -559,7 +559,7 @@ class BaseDriver extends AbstractPaymentDriver
$error = 'Payment Aborted'; $error = 'Payment Aborted';
} }
if (! is_null($this->payment_hash)) { if (! is_null($this->payment_hash)) { //@phpstan-ignore-line
$this->unWindGatewayFees($this->payment_hash); $this->unWindGatewayFees($this->payment_hash);
} }
@ -830,7 +830,7 @@ class BaseDriver extends AbstractPaymentDriver
} }
$invoices_string = \implode(', ', collect($this->payment_hash->invoices())->pluck('invoice_number')->toArray()) ?: null; $invoices_string = \implode(', ', collect($this->payment_hash->invoices())->pluck('invoice_number')->toArray()) ?: null;
$amount = Number::formatMoney($this->payment_hash?->amount_with_fee() ?? 0, $this->client); $amount = Number::formatMoney($this->payment_hash?->amount_with_fee() ?? 0, $this->client); // @phpstan-ignore-line
if($abbreviated && $invoices_string) { if($abbreviated && $invoices_string) {
return $invoices_string; return $invoices_string;

View File

@ -40,7 +40,7 @@ class Webhook
$error_details = $e->error_details; $error_details = $e->error_details;
nlog($error_details); nlog($error_details);
$http_status_code = isset($e->http_metadata) ? $e->http_metadata->getStatusCode() : null; $http_status_code = isset($e->http_metadata) ? $e->http_metadata->getStatusCode() : null; //@phpstan-ignore-line
} catch (CheckoutAuthorizationException $e) { } catch (CheckoutAuthorizationException $e) {
// Bad Invalid authorization // Bad Invalid authorization
} }

View File

@ -78,7 +78,7 @@ class CreditCard
$this->logResponse($response); $this->logResponse($response);
throw new PaymentFailed($response_status['message'] ?? 'Unknown response from gateway, please contact you merchant.', 400); throw new PaymentFailed($response_status['message'] ?? 'Unknown response from gateway, please contact you merchant.', 400); //@phpstan-ignore-line
} }
//success //success
@ -135,7 +135,7 @@ class CreditCard
$invoice_numbers = ''; $invoice_numbers = '';
if ($this->eway_driver->payment_hash->data) { if ($this->eway_driver->payment_hash->data) {
$invoice_numbers = collect($this->eway_driver->payment_hash->data->invoices)->pluck('invoice_number')->implode(','); $invoice_numbers = collect($this->eway_driver->payment_hash->data->invoices)->pluck('invoice_number')->implode(','); //@phpstan-ignore-line
} }
$amount = array_sum(array_column($this->eway_driver->payment_hash->invoices(), 'amount')) + $this->eway_driver->payment_hash->fee_total; $amount = array_sum(array_column($this->eway_driver->payment_hash->invoices(), 'amount')) + $this->eway_driver->payment_hash->fee_total;

View File

@ -173,7 +173,7 @@ class ACH implements MethodInterface
$description = "Amount {$request->amount} from client {$this->go_cardless->client->present()->name()}"; $description = "Amount {$request->amount} from client {$this->go_cardless->client->present()->name()}";
} }
$amount = $this->go_cardless->convertToGoCardlessAmount($this->go_cardless->payment_hash?->amount_with_fee(), $this->go_cardless->client->currency()->precision); $amount = $this->go_cardless->convertToGoCardlessAmount($this->go_cardless->payment_hash?->amount_with_fee(), $this->go_cardless->client->currency()->precision); //@phpstan-ignore-line
try { try {
$payment = $this->go_cardless->gateway->payments()->create([ $payment = $this->go_cardless->gateway->payments()->create([

View File

@ -156,7 +156,7 @@ class MolliePaymentDriver extends BaseDriver
return [ return [
'transaction_reference' => $refund->id, 'transaction_reference' => $refund->id,
'transaction_response' => json_encode($refund), 'transaction_response' => json_encode($refund),
'success' => $refund->status === 'refunded' ? true : false, 'success' => $refund->status === 'refunded' ? true : false, //@phpstan-ignore-line
'description' => $refund->description, 'description' => $refund->description,
'code' => 200, 'code' => 200,
]; ];

View File

@ -489,7 +489,7 @@ class StripePaymentDriver extends BaseDriver
{ {
$customer = Customer::retrieve($customer_id, $this->stripe_connect_auth); $customer = Customer::retrieve($customer_id, $this->stripe_connect_auth);
return $customer ?? null; return $customer ?? null; // @phpstan-ignore-line
} }
/** /**

View File

@ -46,12 +46,11 @@ class ExpenseRepository extends BaseRepository
/** @var \App\Models\User $user */ /** @var \App\Models\User $user */
$user = auth()->user(); $user = auth()->user();
$payment_date = &$data['payment_date']; $payment_date = isset($data['payment_date']) ? $data['payment_date'] : false;
$vendor_id = &$data['vendor_id'];
if($payment_date && $payment_date == $expense->payment_date) { if($payment_date && $payment_date == $expense->payment_date) {
//do nothing //do nothing
} elseif($payment_date && strlen($payment_date) > 1 && $user->company()->notify_vendor_when_paid && ($vendor_id || $expense->vendor_id)) { } elseif($payment_date && strlen($payment_date) > 1 && $user->company()->notify_vendor_when_paid && (isset($data['vendor_id']) || $expense->vendor_id)) {
$this->notify_vendor = true; $this->notify_vendor = true;
} }

View File

@ -117,10 +117,15 @@ class TaskRepository extends BaseRepository
} }
$key_values = array_column($time_log, 0); $key_values = array_column($time_log, 0);
if(count($key_values) > 0)
array_multisort($key_values, SORT_ASC, $time_log); array_multisort($key_values, SORT_ASC, $time_log);
foreach($time_log as $key => $value) { foreach($time_log as $key => $value) {
if(is_array($time_log[$key]) && count($time_log[$key]) >=2)
$time_log[$key][1] = $this->roundTimeLog($time_log[$key][0], $time_log[$key][1]); $time_log[$key][1] = $this->roundTimeLog($time_log[$key][0], $time_log[$key][1]);
} }
if (isset($data['action'])) { if (isset($data['action'])) {
@ -261,7 +266,7 @@ class TaskRepository extends BaseRepository
public function roundTimeLog(int $start_time, int $end_time): int public function roundTimeLog(int $start_time, int $end_time): int
{ {
if($this->task_round_to_nearest == 1 || $end_time == 0) { if(in_array($this->task_round_to_nearest, [0,1]) || $end_time == 0) {
return $end_time; return $end_time;
} }

View File

@ -53,11 +53,7 @@ class VendorRepository extends BaseRepository
$vendor->saveQuietly(); $vendor->saveQuietly();
if ($vendor->number == '' || ! $vendor->number) { $vendor->service()->applyNumber();
$vendor->number = $this->getNextVendorNumber($vendor);
}
$vendor->saveQuietly();
if (isset($data['contacts']) || $vendor->contacts()->count() == 0) { if (isset($data['contacts']) || $vendor->contacts()->count() == 0) {
$this->contact_repo->save($data, $vendor); $this->contact_repo->save($data, $vendor);

View File

@ -0,0 +1,173 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\Chart;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Quote;
/**
* Class ChartCalculations.
*/
trait ChartCalculations
{
public function getActiveInvoices($data): int|float
{
$result = 0;
$q = Invoice::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->whereIn('status_id', [2,3,4]);
if(in_array($data['period'],['current,previous']))
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
match ($data['calculation']) {
'sum' => $result = $q->sum('amount'),
'avg' => $result = $q->avg('amount'),
'count' => $result = $q->count(),
default => $result = 0,
};
return $result;
}
public function getOutstandingInvoices($data): int|float
{
$result = 0;
$q = Invoice::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->whereIn('status_id', [2,3]);
if(in_array($data['period'],['current,previous']))
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
match ($data['calculation']) {
'sum' => $result = $q->sum('balance'),
'avg' => $result = $q->avg('balance'),
'count' => $result = $q->count(),
default => $result = 0,
};
return $result;
}
public function getCompletedPayments($data): int|float
{
$result = 0;
$q = Payment::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->where('status_id', 4);
if(in_array($data['period'],['current,previous']))
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
match ($data['calculation']) {
'sum' => $result = $q->sum('amount'),
'avg' => $result = $q->avg('amount'),
'count' => $result = $q->count(),
default => $result = 0,
};
return $result;
}
public function getRefundedPayments($data): int|float
{
$result = 0;
$q = Payment::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->whereIn('status_id', [5,6]);
if(in_array($data['period'],['current,previous']))
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
match ($data['calculation']) {
'sum' => $result = $q->sum('refunded'),
'avg' => $result = $q->avg('refunded'),
'count' => $result = $q->count(),
default => $result = 0,
};
return $result;
}
public function getActiveQuotes($data): int|float
{
$result = 0;
$q = Quote::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->whereIn('status_id', [2,3])
->where(function ($qq){
$qq->where('due_date', '>=', now()->toDateString())->orWhereNull('due_date');
});
if(in_array($data['period'],['current,previous']))
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
match ($data['calculation']) {
'sum' => $result = $q->sum('refunded'),
'avg' => $result = $q->avg('refunded'),
'count' => $result = $q->count(),
default => $result = 0,
};
return $result;
}
public function getUnapprovedQuotes($data): int|float
{
$result = 0;
$q = Quote::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->whereIn('status_id', [2])
->where(function ($qq){
$qq->where('due_date', '>=', now()->toDateString())->orWhereNull('due_date');
});
if(in_array($data['period'],['current,previous']))
$q->whereBetween('date', [$data['start_date'], $data['end_date']]);
match ($data['calculation']) {
'sum' => $result = $q->sum('refunded'),
'avg' => $result = $q->avg('refunded'),
'count' => $result = $q->count(),
default => $result = 0,
};
return $result;
}
}

View File

@ -14,12 +14,17 @@ namespace App\Services\Chart;
use App\Models\Client; use App\Models\Client;
use App\Models\Company; use App\Models\Company;
use App\Models\Expense; use App\Models\Expense;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Quote;
use App\Models\Task;
use App\Models\User; use App\Models\User;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
class ChartService class ChartService
{ {
use ChartQueries; use ChartQueries;
use ChartCalculations;
public function __construct(public Company $company, private User $user, private bool $is_admin) public function __construct(public Company $company, private User $user, private bool $is_admin)
{ {
@ -207,4 +212,44 @@ class ChartService
return ''; return '';
} }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* calculatedField
*
* @param array $data -
*
* field - list of fields for calculation
* period - current/previous
* calculation - sum/count/avg
*
* date_range - this_month
* or
* start_date - end_date
*/
public function getCalculatedField(array $data)
{
$results = 0;
match($data['field']){
'active_invoices' => $results = $this->getActiveInvoices($data),
'outstanding_invoices' => $results = 0,
'completed_payments' => $results = 0,
'refunded_payments' => $results = 0,
'active_quotes' => $results = 0,
'unapproved_quotes' => $results = 0,
'logged_tasks' => $results = 0,
'invoiced_tasks' => $results = 0,
'paid_tasks' => $results = 0,
'logged_expenses' => $results = 0,
'pending_expenses' => $results = 0,
'invoiced_expenses' => $results = 0,
'invoice_paid_expenses' => $results = 0,
default => $results = 0,
};
return $results;
}
} }

View File

@ -169,7 +169,7 @@ class ClientService
} catch (QueryException $e) { } catch (QueryException $e) {
$x++; $x++;
if ($x > 10) { if ($x > 50) {
$this->completed = false; $this->completed = false;
} }
} }

View File

@ -180,6 +180,21 @@ class PaymentMethod
} }
} }
if (($this->client->getSetting('use_credits_payment') == 'option' || $this->client->getSetting('use_credits_payment') == 'always') && $this->client->service()->getCreditBalance() > 0) {
// Show credits as only payment option if both statements are true.
if (
$this->client->service()->getCreditBalance() > $this->amount
&& $this->client->getSetting('use_credits_payment') == 'always') {
$payment_urls = [];
}
$this->payment_urls[] = [
'label' => ctrans('texts.apply_credit'),
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
'gateway_type_id' => GatewayType::CREDIT,
];
}
return $this; return $this;
} }

View File

@ -41,13 +41,15 @@ class ZugferdEDocument extends AbstractService {
*/ */
public function run(): Expense public function run(): Expense
{ {
/** @var \App\Models\User $user */
$user = auth()->user(); $user = auth()->user();
$this->document = ZugferdDocumentReader::readAndGuessFromContent($this->tempdocument); $this->document = ZugferdDocumentReader::readAndGuessFromContent($this->tempdocument);
$this->document->getDocumentInformation($documentno, $documenttypecode, $documentdate, $invoiceCurrency, $taxCurrency, $documentname, $documentlanguage, $effectiveSpecifiedPeriod); $this->document->getDocumentInformation($documentno, $documenttypecode, $documentdate, $invoiceCurrency, $taxCurrency, $documentname, $documentlanguage, $effectiveSpecifiedPeriod);
$this->document->getDocumentSummation($grandTotalAmount, $duePayableAmount, $lineTotalAmount, $chargeTotalAmount, $allowanceTotalAmount, $taxBasisTotalAmount, $taxTotalAmount, $roundingAmount, $totalPrepaidAmount); $this->document->getDocumentSummation($grandTotalAmount, $duePayableAmount, $lineTotalAmount, $chargeTotalAmount, $allowanceTotalAmount, $taxBasisTotalAmount, $taxTotalAmount, $roundingAmount, $totalPrepaidAmount);
$expense = Expense::where('amount', $grandTotalAmount)->where("transaction_reference", $documentno)->whereDate("date", $documentdate)->first(); /** @var \App\Models\Expense $expense */
if (empty($expense)) { $expense = Expense::where("company_id", $user->company()->id)->where('amount', $grandTotalAmount)->where("transaction_reference", $documentno)->whereDate("date", $documentdate)->first();
if (!$expense) {
// The document does not exist as an expense // The document does not exist as an expense
// Handle accordingly // Handle accordingly
$visualizer = new ZugferdVisualizer($this->document); $visualizer = new ZugferdVisualizer($this->document);
@ -59,10 +61,8 @@ class ZugferdEDocument extends AbstractService {
$expense = ExpenseFactory::create($user->company()->id, $user->id); $expense = ExpenseFactory::create($user->company()->id, $user->id);
$expense->date = $documentdate; $expense->date = $documentdate;
$expense->user_id = $user->id;
$expense->company_id = $user->company->id;
$expense->public_notes = $documentno; $expense->public_notes = $documentno;
$expense->currency_id = Currency::whereCode($invoiceCurrency)->first()->id; $expense->currency_id = Currency::whereCode($invoiceCurrency)->first()->id ?? $user->company()->settings->currency_id;
$expense->save(); $expense->save();
$origin_file = TempFile::UploadedFileFromRaw($this->tempdocument, $this->documentname, "application/xml"); $origin_file = TempFile::UploadedFileFromRaw($this->tempdocument, $this->documentname, "application/xml");
@ -103,13 +103,13 @@ class ZugferdEDocument extends AbstractService {
if ($taxid != null) { if ($taxid != null) {
$vendor->vat_number = $taxid; $vendor->vat_number = $taxid;
} }
$vendor->currency_id = Currency::whereCode($invoiceCurrency)->first()->id; $vendor->currency_id = Currency::query()->where('code', $invoiceCurrency)->first()->id;
$vendor->phone = $contact_phone; $vendor->phone = $contact_phone;
$vendor->address1 = $address_1; $vendor->address1 = $address_1;
$vendor->address2 = $address_2; $vendor->address2 = $address_2;
$vendor->city = $city; $vendor->city = $city;
$vendor->postal_code = $postcode; $vendor->postal_code = $postcode;
$vendor->country_id = Country::where('iso_3166_2', $country)->first()->id; $vendor->country_id = Country::query()->where('iso_3166_2', $country)->first()->id;
$vendor->save(); $vendor->save();
$expense->vendor_id = $vendor->id; $expense->vendor_id = $vendor->id;

View File

@ -44,7 +44,7 @@ use CleverIt\UBL\Invoice\FatturaPA\common\FatturaElettronicaHeader;
*/ */
class FatturaPA extends AbstractService class FatturaPA extends AbstractService
{ {
private $xml; // private $xml;
//urn:cen.eu:en16931:2017#compliant#urn:fatturapa.gov.it:CIUS-IT:2.0.0 //urn:cen.eu:en16931:2017#compliant#urn:fatturapa.gov.it:CIUS-IT:2.0.0
//<cbc:EndpointID schemeID=" 0201 ">UFF001</cbc:EndpointID> //<cbc:EndpointID schemeID=" 0201 ">UFF001</cbc:EndpointID>

View File

@ -0,0 +1,303 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\EDocument\Standards\Validation\Peppol;
use DateTime;
use InvoiceNinja\EInvoice\Models\Peppol\AllowanceChargeType\AllowanceCharge;
use InvoiceNinja\EInvoice\Models\Peppol\BillingReferenceType\BillingReference;
use InvoiceNinja\EInvoice\Models\Peppol\CodeType\AccountingCostCode;
use InvoiceNinja\EInvoice\Models\Peppol\CodeType\DocumentCurrencyCode;
use InvoiceNinja\EInvoice\Models\Peppol\CodeType\InvoiceTypeCode;
use InvoiceNinja\EInvoice\Models\Peppol\CodeType\PaymentAlternativeCurrencyCode;
use InvoiceNinja\EInvoice\Models\Peppol\CodeType\PaymentCurrencyCode;
use InvoiceNinja\EInvoice\Models\Peppol\CodeType\PricingCurrencyCode;
use InvoiceNinja\EInvoice\Models\Peppol\CodeType\TaxCurrencyCode;
use InvoiceNinja\EInvoice\Models\Peppol\CustomerPartyType\AccountingCustomerParty;
use InvoiceNinja\EInvoice\Models\Peppol\CustomerPartyType\BuyerCustomerParty;
use InvoiceNinja\EInvoice\Models\Peppol\DeliveryTermsType\DeliveryTerms;
use InvoiceNinja\EInvoice\Models\Peppol\DeliveryType\Delivery;
use InvoiceNinja\EInvoice\Models\Peppol\DocumentReferenceType\AdditionalDocumentReference;
use InvoiceNinja\EInvoice\Models\Peppol\DocumentReferenceType\ContractDocumentReference;
use InvoiceNinja\EInvoice\Models\Peppol\DocumentReferenceType\DespatchDocumentReference;
use InvoiceNinja\EInvoice\Models\Peppol\DocumentReferenceType\OriginatorDocumentReference;
use InvoiceNinja\EInvoice\Models\Peppol\DocumentReferenceType\ReceiptDocumentReference;
use InvoiceNinja\EInvoice\Models\Peppol\DocumentReferenceType\StatementDocumentReference;
use InvoiceNinja\EInvoice\Models\Peppol\ExchangeRateType\PaymentAlternativeExchangeRate;
use InvoiceNinja\EInvoice\Models\Peppol\ExchangeRateType\PaymentExchangeRate;
use InvoiceNinja\EInvoice\Models\Peppol\ExchangeRateType\PricingExchangeRate;
use InvoiceNinja\EInvoice\Models\Peppol\ExchangeRateType\TaxExchangeRate;
use InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\CustomizationID;
use InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\ID;
use InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\ProfileExecutionID;
use InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\ProfileID;
use InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\UBLVersionID;
use InvoiceNinja\EInvoice\Models\Peppol\IdentifierType\UUID;
use InvoiceNinja\EInvoice\Models\Peppol\InvoiceLineType\InvoiceLine;
use InvoiceNinja\EInvoice\Models\Peppol\MonetaryTotalType\LegalMonetaryTotal;
use InvoiceNinja\EInvoice\Models\Peppol\NumericType\LineCountNumeric;
use InvoiceNinja\EInvoice\Models\Peppol\OrderReferenceType\OrderReference;
use InvoiceNinja\EInvoice\Models\Peppol\PartyType\PayeeParty;
use InvoiceNinja\EInvoice\Models\Peppol\PartyType\TaxRepresentativeParty;
use InvoiceNinja\EInvoice\Models\Peppol\PaymentMeansType\PaymentMeans;
use InvoiceNinja\EInvoice\Models\Peppol\PaymentTermsType\PaymentTerms;
use InvoiceNinja\EInvoice\Models\Peppol\PaymentType\PrepaidPayment;
use InvoiceNinja\EInvoice\Models\Peppol\PeriodType\InvoicePeriod;
use InvoiceNinja\EInvoice\Models\Peppol\ProjectReferenceType\ProjectReference;
use InvoiceNinja\EInvoice\Models\Peppol\SignatureType\Signature;
use InvoiceNinja\EInvoice\Models\Peppol\SupplierPartyType\AccountingSupplierParty;
use InvoiceNinja\EInvoice\Models\Peppol\SupplierPartyType\SellerSupplierParty;
use InvoiceNinja\EInvoice\Models\Peppol\TaxTotalType\TaxTotal;
use InvoiceNinja\EInvoice\Models\Peppol\TaxTotalType\WithholdingTaxTotal;
use Symfony\Component\Serializer\Attribute\Context;
use Symfony\Component\Serializer\Attribute\SerializedName;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Validator\Constraints\Date;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\Constraints\Valid;
class CompanyLevel
{
// /** @var UBLVersionID */
// #[SerializedName('cbc:UBLVersionID')]
// public $UBLVersionID;
// /** @var CustomizationID */
// #[SerializedName('cbc:CustomizationID')]
// public $CustomizationID;
// /** @var ProfileID */
// #[SerializedName('cbc:ProfileID')]
// public $ProfileID;
// /** @var ProfileExecutionID */
// #[SerializedName('cbc:ProfileExecutionID')]
// public $ProfileExecutionID;
// /** @var ID */
// #[NotNull]
// #[NotBlank]
// #[Valid]
// #[SerializedName('cbc:ID')]
// public $ID;
/** @var bool */
#[SerializedName('cbc:CopyIndicator')]
public bool $CopyIndicator;
/** @var UUID */
#[SerializedName('cbc:UUID')]
public $UUID;
// /** @var ?\DateTime */
// #[NotNull]
// #[NotBlank]
// #[Valid]
// #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
// #[SerializedName('cbc:IssueDate')]
// public ?DateTime $IssueDate;
// /** @var ?\DateTime */
// #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d\TH:i:s.uP'])]
// #[SerializedName('cbc:IssueTime')]
// public ?DateTime $IssueTime;
// /** @var ?\DateTime */
// #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
// #[SerializedName('cbc:DueDate')]
// public ?DateTime $DueDate;
/** @var InvoiceTypeCode */
#[SerializedName('cbc:InvoiceTypeCode')]
public $InvoiceTypeCode;
/** @var string */
#[SerializedName('cbc:Note')]
public string $Note;
/** @var ?\DateTime */
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
#[SerializedName('cbc:TaxPointDate')]
public ?DateTime $TaxPointDate;
/** @var DocumentCurrencyCode */
#[SerializedName('cbc:DocumentCurrencyCode')]
public $DocumentCurrencyCode;
/** @var TaxCurrencyCode */
#[SerializedName('cbc:TaxCurrencyCode')]
public $TaxCurrencyCode;
/** @var PricingCurrencyCode */
#[SerializedName('cbc:PricingCurrencyCode')]
public $PricingCurrencyCode;
/** @var PaymentCurrencyCode */
#[SerializedName('cbc:PaymentCurrencyCode')]
public $PaymentCurrencyCode;
/** @var PaymentAlternativeCurrencyCode */
#[SerializedName('cbc:PaymentAlternativeCurrencyCode')]
public $PaymentAlternativeCurrencyCode;
// /** @var AccountingCostCode */
// #[SerializedName('cbc:AccountingCostCode')]
// public $AccountingCostCode;
// /** @var string */
// #[SerializedName('cbc:AccountingCost')]
// public string $AccountingCost;
// /** @var LineCountNumeric */
// #[SerializedName('cbc:LineCountNumeric')]
// public $LineCountNumeric;
// /** @var string */
// #[SerializedName('cbc:BuyerReference')]
// public string $BuyerReference;
// /** @var InvoicePeriod[] */
// #[SerializedName('cac:InvoicePeriod')]
// public array $InvoicePeriod;
// /** @var OrderReference */
// #[SerializedName('cac:OrderReference')]
// public $OrderReference;
// /** @var BillingReference[] */
// #[SerializedName('cac:BillingReference')]
// public array $BillingReference;
/** @var DespatchDocumentReference[] */
#[SerializedName('cac:DespatchDocumentReference')]
public array $DespatchDocumentReference;
/** @var ReceiptDocumentReference[] */
#[SerializedName('cac:ReceiptDocumentReference')]
public array $ReceiptDocumentReference;
/** @var StatementDocumentReference[] */
#[SerializedName('cac:StatementDocumentReference')]
public array $StatementDocumentReference;
/** @var OriginatorDocumentReference[] */
#[SerializedName('cac:OriginatorDocumentReference')]
public array $OriginatorDocumentReference;
/** @var ContractDocumentReference[] */
#[SerializedName('cac:ContractDocumentReference')]
public array $ContractDocumentReference;
/** @var AdditionalDocumentReference[] */
#[SerializedName('cac:AdditionalDocumentReference')]
public array $AdditionalDocumentReference;
/** @var ProjectReference[] */
#[SerializedName('cac:ProjectReference')]
public array $ProjectReference;
// /** @var Signature[] */
// #[SerializedName('cac:Signature')]
// public array $Signature;
// /** @var AccountingSupplierParty */
// #[NotNull]
// #[NotBlank]
// #[Valid]
// #[SerializedName('cac:AccountingSupplierParty')]
// public $AccountingSupplierParty;
// /** @var AccountingCustomerParty */
// #[NotNull]
// #[NotBlank]
// #[Valid]
// #[SerializedName('cac:AccountingCustomerParty')]
// public $AccountingCustomerParty;
/** @var PayeeParty */
#[SerializedName('cac:PayeeParty')]
public $PayeeParty;
// /** @var BuyerCustomerParty */
// #[SerializedName('cac:BuyerCustomerParty')]
// public $BuyerCustomerParty;
// /** @var SellerSupplierParty */
// #[SerializedName('cac:SellerSupplierParty')]
// public $SellerSupplierParty;
/** @var TaxRepresentativeParty */
#[SerializedName('cac:TaxRepresentativeParty')]
public $TaxRepresentativeParty;
/** @var Delivery[] */
#[SerializedName('cac:Delivery')]
public array $Delivery;
/** @var DeliveryTerms */
#[SerializedName('cac:DeliveryTerms')]
public $DeliveryTerms;
/** @var PaymentMeans[] */
#[SerializedName('cac:PaymentMeans')]
public array $PaymentMeans;
/** @var PaymentTerms[] */
#[SerializedName('cac:PaymentTerms')]
public array $PaymentTerms;
// /** @var PrepaidPayment[] */
// #[SerializedName('cac:PrepaidPayment')]
// public array $PrepaidPayment;
// /** @var AllowanceCharge[] */
// #[SerializedName('cac:AllowanceCharge')]
// public array $AllowanceCharge;
// /** @var TaxExchangeRate */
// #[SerializedName('cac:TaxExchangeRate')]
// public $TaxExchangeRate;
// /** @var PricingExchangeRate */
// #[SerializedName('cac:PricingExchangeRate')]
// public $PricingExchangeRate;
// /** @var PaymentExchangeRate */
// #[SerializedName('cac:PaymentExchangeRate')]
// public $PaymentExchangeRate;
// /** @var PaymentAlternativeExchangeRate */
// #[SerializedName('cac:PaymentAlternativeExchangeRate')]
// public $PaymentAlternativeExchangeRate;
// /** @var TaxTotal[] */
// #[SerializedName('cac:TaxTotal')]
// public array $TaxTotal;
// /** @var WithholdingTaxTotal[] */
// #[SerializedName('cac:WithholdingTaxTotal')]
// public array $WithholdingTaxTotal;
// /** @var LegalMonetaryTotal */
// #[NotNull]
// #[NotBlank]
// #[Valid]
// #[SerializedName('cac:LegalMonetaryTotal')]
// public $LegalMonetaryTotal;
// /** @var InvoiceLine[] */
// #[NotNull]
// #[NotBlank]
// #[Valid]
// #[SerializedName('cac:InvoiceLine')]
// public array $InvoiceLine;
}

View File

@ -205,7 +205,7 @@ class AdminEmail implements ShouldQueue
$this->entityEmailFailed($message); $this->entityEmailFailed($message);
/* Don't send postmark failures to Sentry */ /* Don't send postmark failures to Sentry */
if (Ninja::isHosted() && (!$e instanceof ClientException)) { if (Ninja::isHosted() && (!$e instanceof ClientException)) { //@phpstan-ignore-line
app('sentry')->captureException($e); app('sentry')->captureException($e);
} }
} }

View File

@ -33,9 +33,6 @@ class RefundPayment
public function __construct(public Payment $payment, public array $refund_data) public function __construct(public Payment $payment, public array $refund_data)
{ {
$this->gateway_refund_status = false;
$this->activity_repository = new ActivityRepository();
} }
public function run() public function run()

View File

@ -127,8 +127,6 @@ class UpdateInvoicePayment
return; return;
} }
if (strlen($invoice->number) > 1 && str_starts_with($invoice->number, "####")) { if (strlen($invoice->number) > 1 && str_starts_with($invoice->number, "####")) {
$invoice->number = ''; $invoice->number = '';
} }
@ -140,7 +138,6 @@ class UpdateInvoicePayment
->save(); ->save();
} }
/* Updates the company ledger */ /* Updates the company ledger */
$this->payment $this->payment
->ledger() ->ledger()

View File

@ -1665,7 +1665,7 @@ class PdfBuilder
if ($child['element'] !== 'script') { if ($child['element'] !== 'script') {
if ($this->service->company->markdown_enabled && array_key_exists('content', $child)) { if ($this->service->company->markdown_enabled && array_key_exists('content', $child)) {
$child['content'] = str_replace('<br>', "\r", ($child['content'] ?? '')); $child['content'] = str_replace('<br>', "\r", ($child['content'] ?? ''));
$child['content'] = $this->commonmark->convert($child['content'] ?? ''); $child['content'] = $this->commonmark->convert($child['content'] ?? ''); //@phpstan-ignore-line
} }
} }

View File

@ -469,7 +469,7 @@ class PdfMock
'$country_2' => 'AF', '$country_2' => 'AF',
'$firstName' => 'Benedict', '$firstName' => 'Benedict',
'$user.name' => 'Derrick Monahan DDS Erna Wunsch', '$user.name' => 'Derrick Monahan DDS Erna Wunsch',
'$font_name' => isset($this->settings?->primary_font) ? $this->settings?->primary_font : 'Roboto', '$font_name' => isset($this->settings?->primary_font) ? $this->settings?->primary_font : 'Roboto', //@phpstan-ignore-line
'$auto_bill' => 'This invoice will automatically be billed to your credit card on file on the due date.', '$auto_bill' => 'This invoice will automatically be billed to your credit card on file on the due date.',
'$payments' => '', '$payments' => '',
'$task.tax' => '', '$task.tax' => '',

View File

@ -287,7 +287,7 @@ class Design extends BaseDesign
{ {
$elements = []; $elements = [];
if (!$this->client) { if (!$this->client) {//@phpstan-ignore-line
return $elements; return $elements;
} }
@ -359,7 +359,7 @@ class Design extends BaseDesign
$variables = $this->context['pdf_variables']['credit_details']; $variables = $this->context['pdf_variables']['credit_details'];
} }
if ($this->vendor) { if ($this->vendor) { //@phpstan-ignore-line
$variables = $this->context['pdf_variables']['purchase_order_details']; $variables = $this->context['pdf_variables']['purchase_order_details'];
} }

View File

@ -64,9 +64,9 @@ trait DesignHelpers
$this->document(); $this->document();
$this->settings_object = $this->vendor ? $this->vendor->company : $this->client; $this->settings_object = $this->vendor ? $this->vendor->company : $this->client; //@phpstan-ignore-line
$this->company = $this->vendor ? $this->vendor->company : $this->client->company; $this->company = $this->vendor ? $this->vendor->company : $this->client->company; //@phpstan-ignore-line
return $this; return $this;
} }
@ -387,7 +387,7 @@ trait DesignHelpers
return ''; return '';
} }
if ($this->client->company->custom_fields && ! property_exists($this->client->company->custom_fields, $field)) { if ($this->client->company->custom_fields && ! property_exists($this->client->company->custom_fields, $field)) { //@phpstan-ignore-line
return ''; return '';
} }

View File

@ -27,16 +27,6 @@ class PdfMaker
public $document; public $document;
private $xpath;
private $filters = [
'<![CDATA[' => '',
'<![CDATA[<![CDATA[' => '',
']]]]><![CDATA[>]]>' => '',
']]>' => '',
'<?xml version="1.0" encoding="utf-8" standalone="yes"??>' => '',
];
private $options; private $options;
/** @var CommonMarkConverter */ /** @var CommonMarkConverter */

View File

@ -94,7 +94,7 @@ trait PdfMakerUtilities
if ($child['element'] !== 'script') { if ($child['element'] !== 'script') {
if (array_key_exists('process_markdown', $this->data) && array_key_exists('content', $child) && $this->data['process_markdown']) { if (array_key_exists('process_markdown', $this->data) && array_key_exists('content', $child) && $this->data['process_markdown']) {
$child['content'] = str_replace('<br>', "\r", ($child['content'] ?? '')); $child['content'] = str_replace('<br>', "\r", ($child['content'] ?? ''));
$child['content'] = $this->commonmark->convert($child['content'] ?? ''); $child['content'] = $this->commonmark->convert($child['content'] ?? ''); //@phpstan-ignore-line
} }
} }

View File

@ -41,7 +41,11 @@ class CreateInvitations extends AbstractService
public function run() public function run()
{ {
$contacts = $this->purchase_order?->vendor?->contacts()->get();
if(!$this->purchase_order->vendor)
return $this->purchase_order;
$contacts = $this->purchase_order->vendor->contacts()->get();
if ($contacts->count() == 0) { if ($contacts->count() == 0) {
$this->createBlankContact(); $this->createBlankContact();

View File

@ -11,19 +11,15 @@
namespace App\Services\PurchaseOrder; namespace App\Services\PurchaseOrder;
use App\Models\PurchaseOrder; use App\Models\Vendor;
use App\Models\Webhook; use App\Models\Webhook;
use App\Models\PurchaseOrder;
class MarkSent class MarkSent
{ {
private $vendor;
private $purchase_order; public function __construct(public Vendor $vendor, public PurchaseOrder $purchase_order)
public function __construct($vendor, $purchase_order)
{ {
$this->vendor = $vendor;
$this->purchase_order = $purchase_order;
} }
public function run() public function run()

View File

@ -1,40 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\Quote;
use App\Events\Quote\QuoteWasMarkedApproved;
use App\Models\Quote;
use App\Utils\Ninja;
class MarkApproved
{
private $client;
public function __construct($client)
{
$this->client = $client;
}
public function run($quote)
{
/* Return immediately if status is not draft */
if ($quote->status_id != Quote::STATUS_SENT) {
return $quote;
}
$quote->service()->setStatus(Quote::STATUS_APPROVED)->applyNumber()->save();
event(new QuoteWasMarkedApproved($quote, $quote->company, Ninja::eventVars()));
return $quote;
}
}

View File

@ -171,39 +171,39 @@ class UpdateReminder extends AbstractService
return $this->quote; return $this->quote;
} }
private function addTimeInterval($date, $endless_reminder_frequency_id): ?Carbon // private function addTimeInterval($date, $endless_reminder_frequency_id): ?Carbon
{ // {
if (! $date) { // if (! $date) {
return null; // return null;
} // }
switch ($endless_reminder_frequency_id) { // switch ($endless_reminder_frequency_id) {
case RecurringInvoice::FREQUENCY_DAILY: // case RecurringInvoice::FREQUENCY_DAILY:
return Carbon::parse($date)->addDay()->startOfDay(); // return Carbon::parse($date)->addDay()->startOfDay();
case RecurringInvoice::FREQUENCY_WEEKLY: // case RecurringInvoice::FREQUENCY_WEEKLY:
return Carbon::parse($date)->addWeek()->startOfDay(); // return Carbon::parse($date)->addWeek()->startOfDay();
case RecurringInvoice::FREQUENCY_TWO_WEEKS: // case RecurringInvoice::FREQUENCY_TWO_WEEKS:
return Carbon::parse($date)->addWeeks(2)->startOfDay(); // return Carbon::parse($date)->addWeeks(2)->startOfDay();
case RecurringInvoice::FREQUENCY_FOUR_WEEKS: // case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
return Carbon::parse($date)->addWeeks(4)->startOfDay(); // return Carbon::parse($date)->addWeeks(4)->startOfDay();
case RecurringInvoice::FREQUENCY_MONTHLY: // case RecurringInvoice::FREQUENCY_MONTHLY:
return Carbon::parse($date)->addMonthNoOverflow()->startOfDay(); // return Carbon::parse($date)->addMonthNoOverflow()->startOfDay();
case RecurringInvoice::FREQUENCY_TWO_MONTHS: // case RecurringInvoice::FREQUENCY_TWO_MONTHS:
return Carbon::parse($date)->addMonthsNoOverflow(2)->startOfDay(); // return Carbon::parse($date)->addMonthsNoOverflow(2)->startOfDay();
case RecurringInvoice::FREQUENCY_THREE_MONTHS: // case RecurringInvoice::FREQUENCY_THREE_MONTHS:
return Carbon::parse($date)->addMonthsNoOverflow(3)->startOfDay(); // return Carbon::parse($date)->addMonthsNoOverflow(3)->startOfDay();
case RecurringInvoice::FREQUENCY_FOUR_MONTHS: // case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
return Carbon::parse($date)->addMonthsNoOverflow(4)->startOfDay(); // return Carbon::parse($date)->addMonthsNoOverflow(4)->startOfDay();
case RecurringInvoice::FREQUENCY_SIX_MONTHS: // case RecurringInvoice::FREQUENCY_SIX_MONTHS:
return Carbon::parse($date)->addMonthsNoOverflow(6)->startOfDay(); // return Carbon::parse($date)->addMonthsNoOverflow(6)->startOfDay();
case RecurringInvoice::FREQUENCY_ANNUALLY: // case RecurringInvoice::FREQUENCY_ANNUALLY:
return Carbon::parse($date)->addYear()->startOfDay(); // return Carbon::parse($date)->addYear()->startOfDay();
case RecurringInvoice::FREQUENCY_TWO_YEARS: // case RecurringInvoice::FREQUENCY_TWO_YEARS:
return Carbon::parse($date)->addYears(2)->startOfDay(); // return Carbon::parse($date)->addYears(2)->startOfDay();
case RecurringInvoice::FREQUENCY_THREE_YEARS: // case RecurringInvoice::FREQUENCY_THREE_YEARS:
return Carbon::parse($date)->addYears(3)->startOfDay(); // return Carbon::parse($date)->addYears(3)->startOfDay();
default: // default:
return null; // return null;
} // }
} // }
} }

View File

@ -33,7 +33,7 @@ class UpdatePrice extends AbstractService
->where('is_deleted', 0) ->where('is_deleted', 0)
->first(); ->first();
if ($product) { if ($product) { //@phpstan-ignore-line
$line_items[$key]->cost = floatval($product->price); $line_items[$key]->cost = floatval($product->price);
} }
} }

View File

@ -90,15 +90,15 @@ class ARDetailReport extends BaseExport
$this->csv->insertOne($this->buildHeader()); $this->csv->insertOne($this->buildHeader());
$query = Invoice::query() $query = Invoice::query()
->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->withTrashed() ->withTrashed()
->whereHas('client', function ($query) { ->whereHas('client', function ($query) {
$query->where('is_deleted', 0); $query->where('is_deleted', 0);
}) })
->where('company_id', $this->company->id) ->where('invoices.company_id', $this->company->id)
->where('is_deleted', 0) ->where('invoices.is_deleted', 0)
->where('balance', '>', 0) ->where('invoices.balance', '>', 0)
->orderBy('due_date', 'ASC') ->orderBy('invoices.due_date', 'ASC');
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]);
$query = $this->addDateRange($query, 'invoices'); $query = $this->addDateRange($query, 'invoices');

View File

@ -183,17 +183,17 @@ class ProfitLoss
return $this; return $this;
} }
private function getForeignIncome(): array // private function getForeignIncome(): array
{ // {
return $this->foreign_income; // return $this->foreign_income;
} // }
private function filterPaymentIncome() // private function filterPaymentIncome()
{ // {
$payments = $this->paymentIncome(); // $payments = $this->paymentIncome();
return $this; // return $this;
} // }
/* /*
//returns an array of objects //returns an array of objects
@ -427,26 +427,26 @@ class ProfitLoss
+"payments_converted": "12260.870000000000", +"payments_converted": "12260.870000000000",
+"currency_id": 1, +"currency_id": 1,
*/ */
private function paymentIncome() // private function paymentIncome()
{ // {
return \DB::select(' // return \DB::select('
SELECT // SELECT
SUM(coalesce(payments.amount - payments.refunded,0)) as payments, // SUM(coalesce(payments.amount - payments.refunded,0)) as payments,
SUM(coalesce(payments.amount - payments.refunded,0)) * IFNULL(payments.exchange_rate ,1) as payments_converted, // SUM(coalesce(payments.amount - payments.refunded,0)) * IFNULL(payments.exchange_rate ,1) as payments_converted,
payments.currency_id as currency_id // payments.currency_id as currency_id
FROM clients // FROM clients
INNER JOIN // INNER JOIN
payments ON // payments ON
clients.id=payments.client_id // clients.id=payments.client_id
WHERE payments.status_id IN (1,4,5,6) // WHERE payments.status_id IN (1,4,5,6)
AND clients.is_deleted = false // AND clients.is_deleted = false
AND payments.is_deleted = false // AND payments.is_deleted = false
AND payments.company_id = :company_id // AND payments.company_id = :company_id
AND (payments.date BETWEEN :start_date AND :end_date) // AND (payments.date BETWEEN :start_date AND :end_date)
GROUP BY currency_id // GROUP BY currency_id
ORDER BY currency_id; // ORDER BY currency_id;
', ['company_id' => $this->company->id, 'start_date' => $this->start_date, 'end_date' => $this->end_date]); // ', ['company_id' => $this->company->id, 'start_date' => $this->start_date, 'end_date' => $this->end_date]);
} // }
private function expenseData() private function expenseData()
{ {
@ -544,18 +544,18 @@ class ProfitLoss
return round(($amount * $exchange_rate), 2); return round(($amount * $exchange_rate), 2);
} }
private function expenseCalcWithTax() // private function expenseCalcWithTax()
{ // {
return \DB::select(' // return \DB::select('
SELECT sum(expenses.amount) as amount, // SELECT sum(expenses.amount) as amount,
IFNULL(expenses.currency_id, :company_currency) as currency_id // IFNULL(expenses.currency_id, :company_currency) as currency_id
FROM expenses // FROM expenses
WHERE expenses.is_deleted = 0 // WHERE expenses.is_deleted = 0
AND expenses.company_id = :company_id // AND expenses.company_id = :company_id
AND (expenses.date BETWEEN :start_date AND :end_date) // AND (expenses.date BETWEEN :start_date AND :end_date)
GROUP BY currency_id // GROUP BY currency_id
', ['company_currency' => $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $this->start_date, 'end_date' => $this->end_date]); // ', ['company_currency' => $this->company->settings->currency_id, 'company_id' => $this->company->id, 'start_date' => $this->start_date, 'end_date' => $this->end_date]);
} // }
private function setBillingReportType() private function setBillingReportType()
{ {

View File

@ -23,8 +23,6 @@ class EmailStatementService
use MakesHash; use MakesHash;
use MakesDates; use MakesDates;
private Client $client;
public function __construct(public Scheduler $scheduler) public function __construct(public Scheduler $scheduler)
{ {
} }
@ -45,9 +43,6 @@ class EmailStatementService
$query->cursor() $query->cursor()
->each(function ($_client) { ->each(function ($_client) {
/**@var \App\Models\Client $_client */
$this->client = $_client;
//work out the date range //work out the date range
$statement_properties = $this->calculateStatementProperties($_client); $statement_properties = $this->calculateStatementProperties($_client);

View File

@ -102,7 +102,7 @@ class SubscriptionCalculator
$line_item->quantity = (float) $item['quantity']; $line_item->quantity = (float) $item['quantity'];
$line_item->cost = (float) $item['product']['price']; $line_item->cost = (float) $item['product']['price'];
$line_item->notes = $item['product']['notes']; $line_item->notes = $item['product']['notes'];
$line_item->tax_id = (string)$item['product']['tax_id'] ?? '1'; $line_item->tax_id = (string)$item['product']['tax_id'] ?? '1'; //@phpstan-ignore-line
$items[] = $line_item; $items[] = $line_item;
} }

View File

@ -145,7 +145,7 @@ class SubscriptionService
/* 06-04-2022 */ /* 06-04-2022 */
/* We may not be in a state where the user is present */ /* We may not be in a state where the user is present */
if (auth()->guard('contact')) { if (auth()->guard('contact')->user()) {
return $this->handleRedirect('/client/invoices/'.$this->encodePrimaryKey($payment_hash->fee_invoice_id)); return $this->handleRedirect('/client/invoices/'.$this->encodePrimaryKey($payment_hash->fee_invoice_id));
} }
} }
@ -200,7 +200,7 @@ class SubscriptionService
$license->first_name = $contact ? $contact->first_name : ' '; $license->first_name = $contact ? $contact->first_name : ' ';
$license->last_name = $contact ? $contact->last_name : ' '; $license->last_name = $contact ? $contact->last_name : ' ';
$license->is_claimed = 1; $license->is_claimed = 1;
$license->transaction_reference = $payment_hash?->payment?->transaction_reference ?: ' '; $license->transaction_reference = $payment_hash?->payment?->transaction_reference ?: ' '; //@phpstan-ignore-line
$license->product_id = self::WHITE_LABEL; $license->product_id = self::WHITE_LABEL;
$license->recurring_invoice_id = $recurring_invoice->id; $license->recurring_invoice_id = $recurring_invoice->id;
@ -1118,7 +1118,7 @@ class SubscriptionService
*/ */
public function triggerWebhook($context) public function triggerWebhook($context)
{ {
if (empty($this->subscription->webhook_configuration['post_purchase_url']) || is_null($this->subscription->webhook_configuration['post_purchase_url']) || strlen($this->subscription->webhook_configuration['post_purchase_url']) < 1) { if (empty($this->subscription->webhook_configuration['post_purchase_url']) || is_null($this->subscription->webhook_configuration['post_purchase_url']) || strlen($this->subscription->webhook_configuration['post_purchase_url']) < 1) { //@phpstan-ignore-line
return ["message" => "Success", "status_code" => 200]; return ["message" => "Success", "status_code" => 200];
} }

View File

@ -174,7 +174,7 @@ class SubscriptionStatus extends AbstractService
*/ */
private function checkRefundable(): self private function checkRefundable(): self
{ {
if(!$this->recurring_invoice->subscription->refund_period || (int)$this->recurring_invoice->subscription->refund_period == 0) { if(!$this->recurring_invoice->subscription->refund_period || (int)$this->recurring_invoice->subscription->refund_period == 0) {//@phpstan-ignore-line
return $this->setRefundable(false); return $this->setRefundable(false);
} }

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