Merge branches 'feature-inbound-email-expenses' and 'feature-inbound-email-expenses' of https://github.com/paulwer/invoiceninja; branch 'v5-develop' of https://github.com/invoiceninja/invoiceninja into feature-inbound-email-expenses

This commit is contained in:
paulwer 2024-07-02 20:20:04 +02:00
commit 36745bfabc
79 changed files with 280678 additions and 279416 deletions

View File

@ -48,7 +48,7 @@ jobs:
npm i
npm run build
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
run: |

View File

@ -207,6 +207,14 @@ class Rule extends BaseRule implements RuleInterface
*/
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;
}

View File

@ -241,7 +241,7 @@ class CreditExport extends BaseExport
}
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;

View File

@ -85,7 +85,7 @@ class ExpenseExport extends BaseExport
->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);
}
@ -259,10 +259,17 @@ class ExpenseExport extends BaseExport
{
$precision = $expense->currency->precision ?? 2;
$entity['expense.net_amount'] = round($expense->amount, $precision);
if($expense->calculate_tax_by_amount) {
$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 {
if($expense->uses_inclusive_taxes) {

View File

@ -63,7 +63,7 @@ class InvoiceExport extends BaseExport
->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);
}
@ -166,7 +166,8 @@ class InvoiceExport extends BaseExport
}
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;

View File

@ -75,7 +75,7 @@ class InvoiceItemExport extends BaseExport
})
->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);
}
@ -258,7 +258,7 @@ class InvoiceItemExport extends BaseExport
}
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;

View File

@ -63,7 +63,7 @@ class PurchaseOrderExport extends BaseExport
})
->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);
}
@ -167,7 +167,8 @@ class PurchaseOrderExport extends BaseExport
}
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'])) {

View File

@ -152,22 +152,22 @@ class InvoiceFilters extends QueryFilters
{
return $this->builder->where(function ($query) {
$query->whereIn('status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
->where('is_deleted', 0)
->where('balance', '>', 0)
->where(function ($query) {
$query->whereIn('invoices.status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
->where('invoices.is_deleted', 0)
->where('invoices.balance', '>', 0)
->orWhere(function ($query) {
$query->whereNull('due_date')
$query->whereNull('invoices.due_date')
->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) {
$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(partial_due_date), partial_due_date ' . 'desc');
->orderByRaw('ISNULL(invoices.due_date), invoices.due_date ' . 'desc')
->orderByRaw('ISNULL(invoices.partial_due_date), invoices.partial_due_date ' . 'desc');
});
}
@ -337,10 +337,10 @@ class InvoiceFilters extends QueryFilters
// 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('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

@ -937,7 +937,9 @@ class BaseController extends Controller
} elseif (in_array($this->entity_type, [Design::class, GroupSetting::class, PaymentTerm::class, TaskStatus::class])) {
// nlog($this->entity_type);
} 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);
});
}
}

View File

@ -11,8 +11,9 @@
namespace App\Http\Controllers;
use App\Http\Requests\Chart\ShowChartRequest;
use App\Services\Chart\ChartService;
use App\Http\Requests\Chart\ShowChartRequest;
use App\Http\Requests\Chart\ShowCalculatedFieldRequest;
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);
}
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 */
$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;
//remove email from quarantine. //@TODO

View File

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

View File

@ -36,21 +36,16 @@ class ReportExportController extends BaseController
return response()->json(['message' => 'Still working.....'], 409);
}
if($report) {
Cache::forget($hash);
Cache::forget($hash);
$headers = [
'Content-Disposition' => 'attachment',
'Content-Type' => 'text/csv',
];
return response()->streamDownload(function () use ($report) {
echo $report;
}, $this->filename, $headers);
}
$headers = [
'Content-Disposition' => 'attachment',
'Content-Type' => 'text/csv',
];
return response()->streamDownload(function () use ($report) {
echo $report;
}, $this->filename, $headers);
}
}

View File

@ -34,12 +34,9 @@ class ReportPreviewController extends BaseController
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

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

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);
}
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);
}

View File

@ -56,7 +56,8 @@ class SetDomainNameDb
return response()->json($error, 403);
} else {
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');
}
}
@ -73,7 +74,8 @@ class SetDomainNameDb
return response()->json($error, 403);
} else {
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
* are saveable
*
* @param \stdClass $settings
* @param mixed $settings
* @return \stdClass $settings
*/
private function filterSaveableSettings($settings)

View File

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

View File

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

View File

@ -126,7 +126,7 @@ class UpdateRecurringInvoiceRequest extends Request
}
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']);
}

View File

@ -17,7 +17,6 @@ use Illuminate\Auth\Access\AuthorizationException;
class GenericReportRequest extends Request
{
private string $error_message = '';
/**
* 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']);
}
if(isset($input['password']) && strlen($input['password'] ?? '') > 1)
if(isset($input['password']) && is_string($input['password']))
{
$input['password'] = trim($input['password']);
}

View File

@ -51,7 +51,7 @@ class HasValidPhoneNumber implements Rule
$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) {
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,
'po_number' => $this->getString($invoice_data, 'PurchaseOrder'),
'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'),
'balance' => $this->getFloat($invoice_data, 'Balance'),
'status_id' => $invoiceStatusMap[$status =

View File

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

View File

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

View File

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

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\Backup> $history
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\QuoteInvitation> $invitations
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel company()
* @mixin \Eloquent
* @mixin \Illuminate\Database\Eloquent\Builder
*/

View File

@ -186,7 +186,7 @@ class Task extends BaseModel
}
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 '';

View File

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

View File

@ -559,7 +559,7 @@ class BaseDriver extends AbstractPaymentDriver
$error = 'Payment Aborted';
}
if (! is_null($this->payment_hash)) {
if (! is_null($this->payment_hash)) { //@phpstan-ignore-line
$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;
$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) {
return $invoices_string;

View File

@ -40,7 +40,7 @@ class Webhook
$error_details = $e->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) {
// Bad Invalid authorization
}

View File

@ -78,7 +78,7 @@ class CreditCard
$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
@ -135,7 +135,7 @@ class CreditCard
$invoice_numbers = '';
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;

View File

@ -173,7 +173,7 @@ class ACH implements MethodInterface
$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 {
$payment = $this->go_cardless->gateway->payments()->create([

View File

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

View File

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

View File

@ -261,7 +261,7 @@ class TaskRepository extends BaseRepository
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;
}

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\Company;
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 Illuminate\Support\Facades\Cache;
class ChartService
{
use ChartQueries;
use ChartCalculations;
public function __construct(public Company $company, private User $user, private bool $is_admin)
{
@ -71,7 +76,7 @@ class ChartService
return $final_currencies;
}
/* Chart Data */
public function chart_summary($start_date, $end_date): array
{
@ -207,4 +212,44 @@ class ChartService
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

@ -52,8 +52,9 @@ class ZugferdEDocument extends AbstractService
$this->document->getDocumentInformation($documentno, $documenttypecode, $documentdate, $invoiceCurrency, $taxCurrency, $documentname, $documentlanguage, $effectiveSpecifiedPeriod);
$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();
if (empty($expense)) {
/** @var \App\Models\Expense $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
// Handle accordingly
$visualizer = new ZugferdVisualizer($this->document);
@ -65,8 +66,6 @@ class ZugferdEDocument extends AbstractService
$expense = ExpenseFactory::create($user->company()->id, $user->id);
$expense->date = $documentdate;
$expense->user_id = $user->id;
$expense->company_id = $user->company->id;
$expense->public_notes = $documentno;
$expense->currency_id = Currency::whereCode($invoiceCurrency)->first()?->id || $user->company->settings->currency_id;
$expense->save();
@ -75,7 +74,7 @@ class ZugferdEDocument extends AbstractService
if ($this->file->getExtension() == "xml")
array_push($documents, TempFile::UploadedFileFromRaw($visualizer->renderPdf(), $documentno . "_visualiser.pdf", "application/pdf"));
$this->saveDocuments($documents, $expense);
$expense->saveQuietly();
$expense->save();
if ($taxCurrency && $taxCurrency != $invoiceCurrency) {
$expense->private_notes = ctrans("texts.tax_currency_mismatch");

View File

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

View File

@ -205,7 +205,7 @@ class AdminEmail implements ShouldQueue
$this->entityEmailFailed($message);
/* 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);
}
}

View File

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

View File

@ -1665,7 +1665,7 @@ class PdfBuilder
if ($child['element'] !== 'script') {
if ($this->service->company->markdown_enabled && array_key_exists('content', $child)) {
$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',
'$firstName' => 'Benedict',
'$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.',
'$payments' => '',
'$task.tax' => '',

View File

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

View File

@ -64,9 +64,9 @@ trait DesignHelpers
$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;
}
@ -387,7 +387,7 @@ trait DesignHelpers
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 '';
}

View File

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

View File

@ -94,7 +94,7 @@ trait PdfMakerUtilities
if ($child['element'] !== 'script') {
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'] = $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()
{
$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) {
$this->createBlankContact();

View File

@ -11,19 +11,15 @@
namespace App\Services\PurchaseOrder;
use App\Models\PurchaseOrder;
use App\Models\Vendor;
use App\Models\Webhook;
use App\Models\PurchaseOrder;
class MarkSent
{
private $vendor;
private $purchase_order;
public function __construct($vendor, $purchase_order)
public function __construct(public Vendor $vendor, public PurchaseOrder $purchase_order)
{
$this->vendor = $vendor;
$this->purchase_order = $purchase_order;
}
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;
}
private function addTimeInterval($date, $endless_reminder_frequency_id): ?Carbon
{
if (! $date) {
return null;
}
// private function addTimeInterval($date, $endless_reminder_frequency_id): ?Carbon
// {
// if (! $date) {
// return null;
// }
switch ($endless_reminder_frequency_id) {
case RecurringInvoice::FREQUENCY_DAILY:
return Carbon::parse($date)->addDay()->startOfDay();
case RecurringInvoice::FREQUENCY_WEEKLY:
return Carbon::parse($date)->addWeek()->startOfDay();
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
return Carbon::parse($date)->addWeeks(2)->startOfDay();
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
return Carbon::parse($date)->addWeeks(4)->startOfDay();
case RecurringInvoice::FREQUENCY_MONTHLY:
return Carbon::parse($date)->addMonthNoOverflow()->startOfDay();
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
return Carbon::parse($date)->addMonthsNoOverflow(2)->startOfDay();
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
return Carbon::parse($date)->addMonthsNoOverflow(3)->startOfDay();
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
return Carbon::parse($date)->addMonthsNoOverflow(4)->startOfDay();
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
return Carbon::parse($date)->addMonthsNoOverflow(6)->startOfDay();
case RecurringInvoice::FREQUENCY_ANNUALLY:
return Carbon::parse($date)->addYear()->startOfDay();
case RecurringInvoice::FREQUENCY_TWO_YEARS:
return Carbon::parse($date)->addYears(2)->startOfDay();
case RecurringInvoice::FREQUENCY_THREE_YEARS:
return Carbon::parse($date)->addYears(3)->startOfDay();
default:
return null;
}
}
// switch ($endless_reminder_frequency_id) {
// case RecurringInvoice::FREQUENCY_DAILY:
// return Carbon::parse($date)->addDay()->startOfDay();
// case RecurringInvoice::FREQUENCY_WEEKLY:
// return Carbon::parse($date)->addWeek()->startOfDay();
// case RecurringInvoice::FREQUENCY_TWO_WEEKS:
// return Carbon::parse($date)->addWeeks(2)->startOfDay();
// case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
// return Carbon::parse($date)->addWeeks(4)->startOfDay();
// case RecurringInvoice::FREQUENCY_MONTHLY:
// return Carbon::parse($date)->addMonthNoOverflow()->startOfDay();
// case RecurringInvoice::FREQUENCY_TWO_MONTHS:
// return Carbon::parse($date)->addMonthsNoOverflow(2)->startOfDay();
// case RecurringInvoice::FREQUENCY_THREE_MONTHS:
// return Carbon::parse($date)->addMonthsNoOverflow(3)->startOfDay();
// case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
// return Carbon::parse($date)->addMonthsNoOverflow(4)->startOfDay();
// case RecurringInvoice::FREQUENCY_SIX_MONTHS:
// return Carbon::parse($date)->addMonthsNoOverflow(6)->startOfDay();
// case RecurringInvoice::FREQUENCY_ANNUALLY:
// return Carbon::parse($date)->addYear()->startOfDay();
// case RecurringInvoice::FREQUENCY_TWO_YEARS:
// return Carbon::parse($date)->addYears(2)->startOfDay();
// case RecurringInvoice::FREQUENCY_THREE_YEARS:
// return Carbon::parse($date)->addYears(3)->startOfDay();
// default:
// return null;
// }
// }
}

View File

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

View File

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

View File

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

View File

@ -102,7 +102,7 @@ class SubscriptionCalculator
$line_item->quantity = (float) $item['quantity'];
$line_item->cost = (float) $item['product']['price'];
$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;
}

View File

@ -145,7 +145,7 @@ class SubscriptionService
/* 06-04-2022 */
/* 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));
}
}
@ -200,7 +200,7 @@ class SubscriptionService
$license->first_name = $contact ? $contact->first_name : ' ';
$license->last_name = $contact ? $contact->last_name : ' ';
$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->recurring_invoice_id = $recurring_invoice->id;

View File

@ -1327,6 +1327,7 @@ class TemplateService
{
$entity_string = '';
//@phpstan-ignore-next-line
match($this->entity) {
($this->entity instanceof Invoice) => $entity_string = 'invoice',
($this->entity instanceof Quote) => $entity_string = 'quote',

View File

@ -85,6 +85,7 @@ class SystemHealth
'file_permissions' => (string) self::checkFileSystem(),
'exchange_rate_api_not_configured' => (bool)self::checkCurrencySanity(),
'api_version' => (string) config('ninja.app_version'),
'is_docker' => (bool) config('ninja.is_docker'),
];
}

View File

@ -146,7 +146,6 @@ trait MakesDates
}
return match ($data['date_range']) {
EmailStatement::LAST7 => [now()->startOfDay()->subDays(7)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
EmailStatement::LAST30 => [now()->startOfDay()->subDays(30)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
@ -162,4 +161,37 @@ trait MakesDates
};
}
public function calculatePreviousPeriodStartAndEndDates(array $data, ?Company $company = null): array
{
//override for financial years
if($data['date_range'] == 'this_year') {
$first_month_of_year = $company ? $company?->first_month_of_year : 1;
$fin_year_start = now()->createFromDate(now()->year, $first_month_of_year, 1);
$fin_year_start->subYearNoOverflow();
if(now()->subYear()->lt($fin_year_start)) {
$fin_year_start->subYearNoOverflow();
}
}
return match ($data['date_range']) {
EmailStatement::LAST7 => [now()->startOfDay()->subDays(14)->format('Y-m-d'), now()->subDays(7)->startOfDay()->format('Y-m-d')],
EmailStatement::LAST30 => [now()->startOfDay()->subDays(60)->format('Y-m-d'), now()->subDays(30)->startOfDay()->format('Y-m-d')],
EmailStatement::LAST365 => [now()->startOfDay()->subDays(739)->format('Y-m-d'), now()->subDays(365)->startOfDay()->format('Y-m-d')],
EmailStatement::THIS_MONTH => [now()->startOfDay()->subMonthNoOverflow()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->subMonthNoOverflow()->lastOfMonth()->format('Y-m-d')],
EmailStatement::LAST_MONTH => [now()->startOfDay()->subMonthsNoOverflow(2)->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->subMonthNoOverflow()->lastOfMonth()->format('Y-m-d')],
EmailStatement::THIS_QUARTER => [now()->startOfDay()->subQuarterNoOverflow()->startOfQuarter()->format('Y-m-d'), now()->startOfDay()->subQuarterNoOverflow()->endOfQuarter()->format('Y-m-d')],
EmailStatement::LAST_QUARTER => [now()->startOfDay()->subQuartersNoOverflow(2)->startOfQuarter()->format('Y-m-d'), now()->startOfDay()->subQuartersNoOverflow(2)->endOfQuarter()->format('Y-m-d')],
EmailStatement::THIS_YEAR => [$fin_year_start->subYear()->format('Y-m-d'), $fin_year_start->copy()->subDay()->format('Y-m-d')],
EmailStatement::LAST_YEAR => [$fin_year_start->subYear(2)->format('Y-m-d'), $fin_year_start->copy()->subYear()->subDay()->format('Y-m-d')],
EmailStatement::CUSTOM_RANGE => [$data['start_date'], $data['end_date']],
default => [now()->startOfDay()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->lastOfMonth()->format('Y-m-d')],
};
}
}

46
composer.lock generated
View File

@ -535,16 +535,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.315.0",
"version": "3.315.1",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "a7f6026f00771025c32548dac321541face0dedc"
"reference": "13871330833e167d098240dab74b8b069b9b07e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a7f6026f00771025c32548dac321541face0dedc",
"reference": "a7f6026f00771025c32548dac321541face0dedc",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/13871330833e167d098240dab74b8b069b9b07e3",
"reference": "13871330833e167d098240dab74b8b069b9b07e3",
"shasum": ""
},
"require": {
@ -624,9 +624,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.315.0"
"source": "https://github.com/aws/aws-sdk-php/tree/3.315.1"
},
"time": "2024-06-26T18:08:22+00:00"
"time": "2024-06-27T18:03:53+00:00"
},
{
"name": "bacon/bacon-qr-code",
@ -4609,16 +4609,16 @@
},
{
"name": "laravel/framework",
"version": "v11.12.0",
"version": "v11.13.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "9a6d9cea83cfa6b9e8eda05c89741d0411d8ebe8"
"reference": "92deaa4f037ff100e36809443811301819a8cf84"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/9a6d9cea83cfa6b9e8eda05c89741d0411d8ebe8",
"reference": "9a6d9cea83cfa6b9e8eda05c89741d0411d8ebe8",
"url": "https://api.github.com/repos/laravel/framework/zipball/92deaa4f037ff100e36809443811301819a8cf84",
"reference": "92deaa4f037ff100e36809443811301819a8cf84",
"shasum": ""
},
"require": {
@ -4810,7 +4810,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2024-06-25T19:33:56+00:00"
"time": "2024-06-27T09:04:50+00:00"
},
{
"name": "laravel/pint",
@ -11242,16 +11242,16 @@
},
{
"name": "sprain/swiss-qr-bill",
"version": "v4.12.1",
"version": "v4.13",
"source": {
"type": "git",
"url": "https://github.com/sprain/php-swiss-qr-bill.git",
"reference": "3728cd1366ac631a0587c0997a4878c37923e55b"
"reference": "5490e9139c4050d18533440cd9ff51a64955c035"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sprain/php-swiss-qr-bill/zipball/3728cd1366ac631a0587c0997a4878c37923e55b",
"reference": "3728cd1366ac631a0587c0997a4878c37923e55b",
"url": "https://api.github.com/repos/sprain/php-swiss-qr-bill/zipball/5490e9139c4050d18533440cd9ff51a64955c035",
"reference": "5490e9139c4050d18533440cd9ff51a64955c035",
"shasum": ""
},
"require": {
@ -11299,7 +11299,7 @@
"description": "A PHP library to create Swiss QR bills",
"support": {
"issues": "https://github.com/sprain/php-swiss-qr-bill/issues",
"source": "https://github.com/sprain/php-swiss-qr-bill/tree/v4.12.1"
"source": "https://github.com/sprain/php-swiss-qr-bill/tree/v4.13"
},
"funding": [
{
@ -11307,7 +11307,7 @@
"type": "github"
}
],
"time": "2024-05-16T07:19:59+00:00"
"time": "2024-06-27T11:17:56+00:00"
},
{
"name": "square/square",
@ -19238,16 +19238,16 @@
},
{
"name": "spatie/error-solutions",
"version": "1.0.2",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/spatie/error-solutions.git",
"reference": "9782ba6e25cb026cc653619e01ca695d428b3f03"
"reference": "55ea4117e0fde89d520883734ab9b71064c48876"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/error-solutions/zipball/9782ba6e25cb026cc653619e01ca695d428b3f03",
"reference": "9782ba6e25cb026cc653619e01ca695d428b3f03",
"url": "https://api.github.com/repos/spatie/error-solutions/zipball/55ea4117e0fde89d520883734ab9b71064c48876",
"reference": "55ea4117e0fde89d520883734ab9b71064c48876",
"shasum": ""
},
"require": {
@ -19300,7 +19300,7 @@
],
"support": {
"issues": "https://github.com/spatie/error-solutions/issues",
"source": "https://github.com/spatie/error-solutions/tree/1.0.2"
"source": "https://github.com/spatie/error-solutions/tree/1.0.3"
},
"funding": [
{
@ -19308,7 +19308,7 @@
"type": "github"
}
],
"time": "2024-06-26T13:09:17+00:00"
"time": "2024-06-27T12:22:48+00:00"
},
{
"name": "spatie/flare-client-php",

View File

@ -392,7 +392,7 @@ $lang = array(
'payment_cvv' => 'Dette er det 3-4 cifrede nummer på bagsiden af dit kort',
'payment_footer1' => '*Fakturaadressen skal matche den der er tilknyttet kortet.',
'payment_footer2' => '*Klik kun på "Betal Nu" én gang - transaktionen kan tage helt op til 1 minut inden den er færdig.',
'id_number' => 'CVR/SE-nummer',
'id_number' => 'ID Nummer',
'white_label_link' => 'Hvidmærket',
'white_label_header' => 'Hvidmærket',
'bought_white_label' => 'Hvidmærket licens accepteret',
@ -2363,7 +2363,7 @@ $lang = array(
'currency_gold_troy_ounce' => 'Guld Troy Ounce',
'currency_nicaraguan_córdoba' => 'Nicaraguanske Córdoba',
'currency_malagasy_ariary' => 'Madagaskars ariary',
"currency_tongan_pa_anga" => "tonganske paanga",
"currency_tongan_paanga" => "Tongan Pa'anga",
'review_app_help' => 'Vi håber, du nyder at bruge appen.<br/> Hvis du ville overveje :link ville vi sætte stor pris på det!',
'writing_a_review' => 'skrive en anmeldelse',
@ -2879,19 +2879,6 @@ $lang = array(
'refunded' => 'Refunderet',
'marked_quote_as_sent' => 'Succesfuldt markeret tilbud som sendt',
'custom_module_settings' => 'Speciel Modul Indstillinger',
'ticket' => 'Sag',
'tickets' => 'Sager',
'ticket_number' => 'Sag #',
'new_ticket' => 'Ny sag',
'edit_ticket' => 'Redigér sag',
'view_ticket' => 'Vis sag',
'archive_ticket' => 'Arkivér sag',
'restore_ticket' => 'Genskab sag',
'delete_ticket' => 'Slet sag',
'archived_ticket' => 'Sag blev arkiveret',
'archived_tickets' => 'Sager blev arkiveret',
'restored_ticket' => 'Sag blev genskabt',
'deleted_ticket' => 'Sag blev slettet',
'open' => 'Åben',
'new' => 'Ny',
'closed' => 'Lukket',
@ -2908,14 +2895,6 @@ $lang = array(
'assigned_to' => 'Tildelt',
'reply' => 'Svar',
'awaiting_reply' => 'Afventer svar',
'ticket_close' => 'Luk sag',
'ticket_reopen' => 'Genåbn sag',
'ticket_open' => 'Åbn sag',
'ticket_split' => 'Opdel sag',
'ticket_merge' => 'Sammenflet sag',
'ticket_update' => 'Opdatér sag',
'ticket_settings' => 'Sagsindstillinger',
'updated_ticket' => 'Sag blev opdateret',
'mark_spam' => 'Marker som spam',
'local_part' => 'Lokal del',
'local_part_unavailable' => 'Navn taget',
@ -2933,31 +2912,23 @@ $lang = array(
'mime_types' => 'Mime typer',
'mime_types_placeholder' => '. PDF , .docx, .jpg',
'mime_types_help' => 'Kommasepareret liste over tilladte mime-typer, lad tom for alle',
'ticket_number_start_help' => 'Ticket number must be greater than the current ticket number',
'new_ticket_template_id' => 'New ticket',
'new_ticket_autoresponder_help' => 'Selecting a template will send an auto response to a client/contact when a new ticket is created',
'update_ticket_template_id' => 'Updated ticket',
'update_ticket_autoresponder_help' => 'Selecting a template will send an auto response to a client/contact when a ticket is updated',
'close_ticket_template_id' => 'Closed ticket',
'close_ticket_autoresponder_help' => 'Selecting a template will send an auto response to a client/contact when a ticket is closed',
'default_priority' => 'Standardprioritet',
'alert_new_comment_id' => 'Ny kommentar',
'alert_comment_ticket_help' => 'Hvis du vælger en skabelon, sendes der en meddelelse (til agent), når der kommer en kommentar.',
'alert_comment_ticket_email_help' => 'Kommaseparerede e-mails til bcc ved ny kommentar.',
'new_ticket_notification_list' => 'Yderligere nye Sag -meddelelser',
'update_ticket_notification_list' => 'Yderligere meddelelser om nye kommentarer',
'comma_separated_values' => 'admin@example.com, supervisor@example.com',
'alert_ticket_assign_agent_id' => 'Tildeling af sag',
'alert_ticket_assign_agent_id_hel' => 'Valg af en skabelon vil sende en meddelelse (til agent), når en Sag er tildelt.',
'alert_ticket_assign_agent_id_notifications' => 'Yderligere Sag tildelte meddelelser',
'alert_ticket_assign_agent_id_help' => 'Kommaseparerede e-mails til bcc på Sag opgave.',
'alert_ticket_transfer_email_help' => 'Kommaseparerede e-mails til bcc ved Sag overførsel.',
'alert_ticket_overdue_agent_id' => 'Sag er forfalden',
'alert_ticket_overdue_email' => 'Yderligere forfaldne Sag -meddelelser',
'alert_ticket_overdue_email_help' => 'Kommaseparerede e-mails til bcc på Sag forsinket.',
'alert_ticket_overdue_agent_id_help' => 'Valg af en skabelon vil sende en meddelelse (til agent), når en Sag bliver forsinket.',
'default_agent' => 'Standardagent',
'default_agent_help' => 'Hvis valgt vil det automatisk blive tildelt alle indgående billetter',
'show_agent_details' => 'Vis agentoplysninger om svar',
'avatar' => 'Avatar',
'remove_avatar' => 'Fjern avatar',
'ticket_not_found' => 'Sag blev ikke fundet',
'add_template' => 'Tilføj skabelon',
'updated_ticket_template' => 'Sagsskabelon blev opdateret',
'created_ticket_template' => 'Sagsskabelon blev oprettet',
'archive_ticket_template' => 'Arkiv skabelon',
'restore_ticket_template' => 'Genskab skabelon',
'archived_ticket_template' => 'Succesfuldt arkiveret skabelon',
@ -3130,7 +3101,7 @@ $lang = array(
'sign_up_with_google' => 'Tilmeld dig med Google',
'long_press_multiselect' => 'Tryk længe på Multiselect',
'migrate_to_next_version' => 'Migrer til den næste version af Faktura Ninja',
'migrate_intro_text' => 'Vi har arbejdet på næste version af Faktura Ninja. Klik på knappen nedenfor for at starte migreringen.',
'migrate_intro_text' => 'Vi har arbejdet på næste version af Invoice Ninja. Klik på knappen nedenfor for at starte migreringen.',
'start_the_migration' => 'Start migreringen',
'migration' => 'Migration',
'welcome_to_the_new_version' => 'Velkommen til den nye version af Faktura Ninja',
@ -4504,7 +4475,7 @@ $lang = array(
'view_purchase_order' => 'Vis Indkøbsordre',
'purchase_orders_backup_subject' => 'Dine indkøbsordrer er klar til download',
'notification_purchase_order_viewed_subject' => 'Indkøbsordre :invoice blev set af :client',
'notification_purchase_order_viewed' => 'Følgende Sælger :client har set Indkøbsordre :invoice for :amount .',
'notification_purchase_order_viewed' => 'Følgende leverandør :client har set Indkøbsordre :invoice for :amount .',
'purchase_order_date' => 'Dato for købsordre',
'purchase_orders' => 'Indkøbsordre',
'purchase_order_number_placeholder' => 'Indkøbsordre nr. :purchase_order',
@ -5302,6 +5273,19 @@ $lang = array(
'currency_bhutan_ngultrum' => 'Bhutan Ngultrum',
'end_of_month' => 'End Of Month',
'merge_e_invoice_to_pdf' => 'Merge E-Invoice and PDF',
'task_assigned_subject' => 'New task assignment [Task :task] [ :date ]',
'task_assigned_body' => 'You have been assigned task :task <br><br> Description: :description <br><br> Client: :client',
'activity_141' => 'User :user entered note: :notes',
'quote_reminder_subject' => 'Reminder: Quote :quote from :company',
'quote_reminder_message' => 'Reminder for quote :number for :amount',
'quote_reminder1' => 'First Quote Reminder',
'before_valid_until_date' => 'Before the valid until date',
'after_valid_until_date' => 'After the valid until date',
'after_quote_date' => 'After the quote date',
'remind_quote' => 'Remind Quote',
'end_of_month' => 'End Of Month',
'tax_currency_mismatch' => 'Tax currency is different from invoice currency',
'edocument_import_already_exists' => '\nThe invoice has already been imported on :date'
);
return $lang;
return $lang;

View File

@ -5288,7 +5288,16 @@ $lang = array(
'remind_quote' => 'Remind Quote',
'end_of_month' => 'End Of Month',
'tax_currency_mismatch' => 'Tax currency is different from invoice currency',
'edocument_import_already_exists' => '\nThe invoice has already been imported on :date'
'edocument_import_already_exists' => '\nThe invoice has already been imported on :date',
'before_valid_until' => 'Before the valid until',
'after_valid_until' => 'After the valid until',
'task_assigned_notification' => 'Task Assigned Notification',
'task_assigned_notification_help' => 'Send an email when a task is assigned',
'invoices_locked_end_of_month' => 'Invoices are locked at the end of the month',
'referral_url' => 'Referral URL',
'add_comment' => 'Add Comment',
'added_comment' => 'Successfully saved comment',
'tickets' => 'Tickets',
);
return $lang;

View File

@ -2361,7 +2361,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'currency_gold_troy_ounce' => 'Once troy d\'or',
'currency_nicaraguan_córdoba' => 'Cordoba nicaraguayen',
'currency_malagasy_ariary' => 'Ariary malgache',
"currency_tongan_pa_anga" => "Pa'anga tongien",
"currency_tongan_paanga" => "Pa'anga tongien",
'review_app_help' => 'Nous espérons que votre utilisation de cette application vous est agréable.<br/>Un commentaire de votre part serait grandement apprécié!',
'writing_a_review' => 'rédiger un commentaire',
@ -2877,19 +2877,6 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'refunded' => 'Remboursé',
'marked_quote_as_sent' => 'La soumission a été marquée comme envoyée',
'custom_module_settings' => 'Paramètres personnalisés de modules',
'ticket' => 'Billet',
'tickets' => 'Billets',
'ticket_number' => 'Billet #',
'new_ticket' => 'Nouveau billet',
'edit_ticket' => 'Éditer le billet',
'view_ticket' => 'Voir le billet',
'archive_ticket' => 'Archiver le billet',
'restore_ticket' => 'Restaurer le billet',
'delete_ticket' => 'Supprimer le billet',
'archived_ticket' => 'Le billet a été archivé',
'archived_tickets' => 'Les billets ont été archivés',
'restored_ticket' => 'Le billet a été restauré',
'deleted_ticket' => 'Le billet a été supprimé',
'open' => 'Ouvert',
'new' => 'Nouveau',
'closed' => 'Désactivé',
@ -2906,14 +2893,6 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'assigned_to' => 'Assigné à',
'reply' => 'Répondre',
'awaiting_reply' => 'En attente de réponse',
'ticket_close' => 'Fermer le billet',
'ticket_reopen' => 'Réouvrir le billet',
'ticket_open' => 'Ouvrir le billet',
'ticket_split' => 'Scinder le billet',
'ticket_merge' => 'Fusionner le billet',
'ticket_update' => 'Mettre à jour le billet',
'ticket_settings' => 'Paramètres des billets',
'updated_ticket' => 'Billet mis à jour',
'mark_spam' => 'Marquer comme pourriel',
'local_part' => 'Partie locale',
'local_part_unavailable' => 'Nom déjà pris',
@ -2931,31 +2910,23 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'mime_types' => 'Type MIME',
'mime_types_placeholder' => '.pdf , .docx, .jpg',
'mime_types_help' => 'Liste séparée par une virgule pour les types MIME autorisés. Laissant vide pour tout autoriser',
'ticket_number_start_help' => 'Le numéro du billet doit être supérieur au numéro de billet actuel.',
'new_ticket_template_id' => 'Nouveau billet',
'new_ticket_autoresponder_help' => 'La sélection d\'un modèle enverra automatiquement une réponse automatique à un client/contact lors de la création d\'un nouveau billet.',
'update_ticket_template_id' => 'Le billet a été mis à jour',
'update_ticket_autoresponder_help' => 'La sélection d\'un modèle déclenchera automatiquement l\'envoi d\'une réponse automatique à un client/contact lors de la mise à jour d\'un billet.',
'close_ticket_template_id' => 'Le billet a ét fermé',
'close_ticket_autoresponder_help' => 'La sélection d\'un modèle enverra automatiquement une réponse automatique à un client/contact lors de la fermeture d\'un billet.',
'default_priority' => 'Priorité par défaut',
'alert_new_comment_id' => 'Nouveau commentaire',
'alert_comment_ticket_help' => 'En sélectionnant un modèle, une notification (à l\'agent) sera envoyée lorsqu\'un commentaire est fait',
'alert_comment_ticket_email_help' => 'Courriels séparés par une virgule pour Cci sur un nouveau commentaire.',
'new_ticket_notification_list' => 'Notifications de nouveaux billets additionnels',
'update_ticket_notification_list' => 'Notifications de nouveaux commentaires additionnels',
'comma_separated_values' => 'admin@exemple.com, supervisor@exemple.com',
'alert_ticket_assign_agent_id' => 'Assignation de billet',
'alert_ticket_assign_agent_id_hel' => 'En sélectionnant un modèle, une notification (à l\'agent) sera envoyée lorsqu\'un billet est assigné.',
'alert_ticket_assign_agent_id_notifications' => 'Notifications de billets assignés additionnels',
'alert_ticket_assign_agent_id_help' => 'Courriels séparés par une virgule pour Cci pour un billet assigné.',
'alert_ticket_transfer_email_help' => 'Courriels séparés par une virgule pour Cci sur un billet transféré.',
'alert_ticket_overdue_agent_id' => 'Billet en retard',
'alert_ticket_overdue_email' => 'Notifications de billets en retard additionnels',
'alert_ticket_overdue_email_help' => 'Courriels séparés par une virgule pour Cci sur un billet en retard.',
'alert_ticket_overdue_agent_id_help' => 'En sélectionnant un modèle, une notification (à l\'agent) sera envoyée lorsqu\'un billet est en retard.',
'default_agent' => 'Agent par défaut',
'default_agent_help' => 'Cette sélection va automatiquement être assignée à tous les courriels entrants',
'show_agent_details' => 'Afficher les informations de l\'agent dans les réponses',
'avatar' => 'Avatar',
'remove_avatar' => 'Retirer l\'avatar',
'ticket_not_found' => 'Billet introuvable',
'add_template' => 'Ajouter un modèle',
'updated_ticket_template' => 'Modèle de billets mise à jour',
'created_ticket_template' => 'Modèle de billet créés',
'archive_ticket_template' => 'Archiver le modèle',
'restore_ticket_template' => 'Restaurer le modèle',
'archived_ticket_template' => 'Le modèle a été archivé',
@ -5300,6 +5271,19 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'currency_bhutan_ngultrum' => 'Ngultrum Bhoutan',
'end_of_month' => 'Fin de mois',
'merge_e_invoice_to_pdf' => 'Fusionner E-Facture et PDF',
'task_assigned_subject' => 'Nouvelle tâche assignée [Tâche:task] [ :date ]',
'task_assigned_body' => 'Vous avez été assigné à la tâche :task <br><br> Description: :description <br><br> Client: :client',
'activity_141' => 'L\'utilisateur:user a saisi la note: :notes',
'quote_reminder_subject' => 'Rappel: Soumission:quote de :company',
'quote_reminder_message' => 'Rappel pour la soumission :number de :amount',
'quote_reminder1' => 'Rappel pour la première soumission',
'before_valid_until_date' => 'Avant la date Valide jusqu\'au',
'after_valid_until_date' => 'Après la date Valide jusqu\'au',
'after_quote_date' => 'Après al date de soumission',
'remind_quote' => 'Rappel de soumission',
'end_of_month' => 'Fin de mois',
'tax_currency_mismatch' => 'La devise de la taxe est différente de la devise de la facture.',
'edocument_import_already_exists' => '\nLa facture a déjà été importée le :date'
);
return $lang;
return $lang;

View File

@ -31,4 +31,10 @@ parameters:
- '#Access to protected property#'
- '#Call to undefined method .*#'
- '#Argument of an invalid type stdClass supplied for foreach, only iterables are supported.#'
- '#Comparison operation ">=" between int<1, max> and 1 is always true#'
- '#Comparison operation ">=" between int<1, max> and 1 is always true#'
- '#Negated boolean expression is always#'
- '#Ternary operator condition#'
- '#Expression on left side of ?? is not nullable.#'
- '#Left side of && is always true.#'
- '#Right side of && is always true.#'

View File

@ -16,9 +16,9 @@ const RESOURCES = {"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"canvaskit/canvaskit.js": "c86fbd9e7b17accae76e5ad116583dc4",
"canvaskit/canvaskit.wasm": "3d2a2d663e8c5111ac61a46367f751ac",
"canvaskit/skwasm.wasm": "e42815763c5d05bba43f9d0337fa7d84",
"version.json": "1592dbbd49cf08963e29ab3a85640d96",
"version.json": "f789e711f61e122f41a7eda7522a1fba",
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"main.dart.js": "0512d8a34cb5c2d5d565a27c3666d9f9",
"main.dart.js": "bb6cfbe200a5c6a0d8857eaffd21b759",
"assets/NOTICES": "412b336cf9e33e70058d612857effae1",
"assets/AssetManifest.bin": "bf3be26e7055ad9a32f66b3a56138224",
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
@ -307,7 +307,7 @@ const RESOURCES = {"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"assets/FontManifest.json": "087fb858dc3cbfbf6baf6a30004922f1",
"assets/fonts/MaterialIcons-Regular.otf": "a57618538ab8b4c4081d4491870ac333",
"assets/AssetManifest.json": "759f9ef9973f7e26c2a51450b55bb9fa",
"/": "a53ace1edfc2ce12fc50cbbade610be3",
"/": "e847f898173e2b358dcf6efc58032f91",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40"};
// The application shell files that are downloaded before a service worker can

271459
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

269785
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"app_name":"invoiceninja_flutter","version":"5.0.160","build_number":"160","package_name":"invoiceninja_flutter"}
{"app_name":"invoiceninja_flutter","version":"5.0.161","build_number":"161","package_name":"invoiceninja_flutter"}

View File

@ -81,7 +81,7 @@
name="country_id">
<option value="none"></option>
@foreach(App\Utils\TranslationHelper::getCountries() as $country)
<option
<option value="{{ $country->id }}">
{{ $country->iso_3166_2 }}
({{ $country->name }})
</option>

View File

@ -1,4 +1,4 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => ''])
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.paypal'), 'card_title' => ''])
@section('gateway_head')

View File

@ -11,7 +11,9 @@
namespace Tests\Feature;
use App\Models\Client;
use App\Models\CompanyGateway;
use App\Models\Invoice;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\MockAccountData;
use Tests\TestCase;
@ -44,6 +46,122 @@ class ClientModelTest extends TestCase
}
public function testNewWithoutAndDeletedClientFilters()
{
$this->invoice->amount = 10;
$this->invoice->balance = 10;
$this->invoice->status_id=2;
$this->invoice->date = now()->subDays(2);
$this->invoice->due_date = now()->addDays(2);
$this->invoice->save();
$cd = Client::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
]);
$cd2 = Client::factory()->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
]);
$invoice_count = Invoice::where('company_id', $this->company->id)->count();
$this->assertGreaterThan(0, $invoice_count);
$i = Invoice::factory()->create([
'client_id' => $cd->id,
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'status_id' => 2,
'amount' => 10,
'balance' => 10,
'date' => now()->subDays(2)->format('Y-m-d'),
'due_date' => now()->addDays(5)->format('Y-m-d'),
]);
$i2 = Invoice::factory()->create([
'client_id' => $cd2->id,
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'status_id' => 2,
'amount' => 10,
'balance' => 10,
'date' => now()->subDays(2)->format('Y-m-d'),
'due_date' => now()->addDays(5)->format('Y-m-d'),
]);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->get('/api/v1/invoices?status=active');
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals($invoice_count+2, count($arr['data']));
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->get('/api/v1/invoices?upcoming=true&status=active&include=client');
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals($invoice_count + 2, count($arr['data']));
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->get('/api/v1/invoices?upcoming=true&status=active&without_deleted_clients=true');
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals($invoice_count + 2, count($arr['data']));
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->get('/api/v1/invoices?upcoming=true&status=active&filter_deleted_clients=true');
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals($invoice_count + 2, count($arr['data']));
$cd2->is_deleted = true;
$cd2->save();
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->get('/api/v1/invoices?upcoming=true&status=active&without_deleted_clients=true');
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals($invoice_count + 1, count($arr['data']));
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->get('/api/v1/invoices?upcoming=true&status=active&filter_deleted_clients=true');
$response->assertStatus(200);
$arr = $response->json();
$this->assertEquals($invoice_count + 1, count($arr['data']));
}
public function testPaymentMethodsWithCreditsEnforced()
{
@ -51,6 +169,6 @@ class ClientModelTest extends TestCase
$this->assertGreaterThan(0, CompanyGateway::count());
$this->assertEquals(1, count($payment_methods));
$this->assertEquals(2, count($payment_methods));
}
}

View File

@ -189,6 +189,22 @@ class TaskApiTest extends TestCase
}
public function testTaskDivisionByZero()
{
$data = [
"rate" => 0,
"time_log" => '[[1719350900,1719352700,"",true]]',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson("/api/v1/tasks", $data);
$response->assertStatus(200);
}
public function testRequestRuleParsing()
{