Merge pull request #11718 from turbo124/preview

Preview
This commit is contained in:
David Bomba
2026-02-22 16:59:53 +11:00
committed by GitHub
2303 changed files with 202304 additions and 171874 deletions

View File

@@ -1 +1 @@
5.12.49
5.12.65

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -26,7 +26,7 @@ class AsReferralEarningCollection implements CastsAttributes
$items = json_decode($value, true);
return array_map(fn ($item) => new ReferralEarning($item), $items);
return array_map(fn($item) => new ReferralEarning($item), $items);
}
public function set($model, string $key, $value, array $attributes)
@@ -39,6 +39,6 @@ class AsReferralEarningCollection implements CastsAttributes
$value = [$value];
}
return json_encode(array_map(fn ($entity) => get_object_vars($entity), $value));
return json_encode(array_map(fn($entity) => get_object_vars($entity), $value));
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -25,7 +25,7 @@ class AsTaxEntityCollection implements CastsAttributes
$items = json_decode($value, true);
return array_map(fn ($item) => new TaxEntity($item), $items);
return array_map(fn($item) => new TaxEntity($item), $items);
}
public function set($model, string $key, $value, array $attributes)
@@ -38,6 +38,6 @@ class AsTaxEntityCollection implements CastsAttributes
$value = [$value];
}
return json_encode(array_map(fn ($entity) => get_object_vars($entity), $value));
return json_encode(array_map(fn($entity) => get_object_vars($entity), $value));
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -47,7 +47,7 @@ class ClientSyncCast implements CastsAttributes
return [
$key => json_encode([
'qb_id' => $value->qb_id,
])
]),
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -0,0 +1,52 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Casts;
use App\DataMapper\ExpenseSync;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class ExpenseSyncCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return null; // Return null if the value is null
}
$data = json_decode($value, true);
if (!is_array($data)) {
return null;
}
$es = new ExpenseSync();
$es->qb_id = $data['qb_id'];
return $es;
}
public function set($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return [$key => null];
}
$data = [
'qb_id' => $value->qb_id,
];
return [
$key => json_encode($data),
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www/elastic.co/licensing/elastic-license
*/
@@ -60,7 +60,7 @@ class InvoiceBackupCast implements CastsAttributes
'redirect' => $value->redirect,
'adjustable_amount' => $value->adjustable_amount,
'notes' => $value->notes,
])
]),
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -25,14 +25,11 @@ class InvoiceSyncCast implements CastsAttributes
$data = json_decode($value, true);
if (!is_array($data)) {
return null;
if (!is_array($data) || empty($data)) {
return null; // Return null if decoded data is not an array or is empty
}
$is = new InvoiceSync();
$is->qb_id = $data['qb_id'];
return $is;
return InvoiceSync::fromArray($data);
}
public function set($model, string $key, $value, array $attributes)
@@ -41,12 +38,12 @@ class InvoiceSyncCast implements CastsAttributes
return [$key => null];
}
$data = [
'qb_id' => $value->qb_id,
];
return [
$key => json_encode($data)
$key => json_encode([
'qb_id' => $value->qb_id,
'invitations' => $value->invitations,
'dn_completed' => $value->dn_completed,
])
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -46,7 +46,7 @@ class PaymentSyncCast implements CastsAttributes
return [
$key => json_encode([
'qb_id' => $value->qb_id,
])
]),
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -32,7 +32,6 @@ class ProductSyncCast implements CastsAttributes
$ps = new ProductSync();
$ps->qb_id = $data['qb_id'];
return $ps;
}
@@ -48,7 +47,7 @@ class ProductSyncCast implements CastsAttributes
return [
$key => json_encode([
'qb_id' => $value->qb_id,
])
]),
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -30,10 +30,9 @@ class QuickbooksSettingsCast implements CastsAttributes
public function set($model, string $key, $value, array $attributes)
{
if ($value instanceof QuickbooksSettings) {
return json_encode(get_object_vars($value));
return json_encode($value->toArray());
}
return null;
// return json_encode($value);
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -19,25 +19,21 @@ class QuoteSyncCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return null; // Return null if the value is null
}
$data = json_decode($value, true);
if (!is_array($data)) {
return null;
if (!is_array($data) || empty($data)) {
return null; // Return null if decoded data is not an array or is empty
}
$is = new QuoteSync($data);
return $is;
return QuoteSync::fromArray($data);
}
public function set($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return [$key => null];
}
@@ -45,8 +41,9 @@ class QuoteSyncCast implements CastsAttributes
return [
$key => json_encode([
'qb_id' => $value->qb_id,
'invitations' => $value->invitations,
'dn_completed' => $value->dn_completed,
])
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -39,7 +39,7 @@ class TransactionEventMetadataCast implements CastsAttributes
}
return [
$key => json_encode($value->toArray())
$key => json_encode($value->toArray()),
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -45,6 +45,7 @@ use App\Factory\ClientContactFactory;
use App\Factory\VendorContactFactory;
use App\Jobs\Company\CreateCompanyToken;
use App\Models\RecurringInvoiceInvitation;
use App\Utils\BcMath;
use App\Utils\Traits\CleanLineItems;
use Symfony\Component\Console\Input\InputOption;
@@ -112,7 +113,7 @@ class CheckData extends Command
$database_connection = $this->option('database') ? $this->option('database') : 'Connected to Default DB';
$fix_status = $this->option('fix') ? "Fixing Issues" : "Just checking issues ";
$this->logMessage(date('Y-m-d h:i:s').' Running CheckData... on ' . $database_connection . " Fix Status = {$fix_status}");
$this->logMessage(date('Y-m-d h:i:s') . ' Running CheckData... on ' . $database_connection . " Fix Status = {$fix_status}");
if ($database = $this->option('database')) {
config(['database.default' => $database]);
@@ -158,7 +159,7 @@ class CheckData extends Command
if ($this->option('payment_balance')) {
$this->updateClientPaymentBalances();
}
$this->logMessage('Done: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE));
$this->logMessage('Done: ' . strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE));
$this->logMessage('Total execution time in seconds: ' . (microtime(true) - $time_start));
$errorEmail = config('ninja.error_email');
@@ -167,18 +168,18 @@ class CheckData extends Command
Mail::raw($this->log, function ($message) use ($errorEmail, $database) {
$message->to($errorEmail)
->from(config('mail.from.address'), config('mail.from.name'))
->subject('Check-Data: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE)." [{$database}]");
->subject('Check-Data: ' . strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE) . " [{$database}]");
});
} elseif (! $this->isValid) {
new \Exception("Check data failed!!".$this->log);
new \Exception("Check data failed!!" . $this->log);
}
}
private function logMessage($str)
{
$str = date('Y-m-d h:i:s').' '.$str;
$str = date('Y-m-d h:i:s') . ' ' . $str;
$this->info($str);
$this->log .= $str."\n";
$this->log .= $str . "\n";
}
private function checkTaskTimeLogs()
@@ -186,25 +187,24 @@ class CheckData extends Command
\App\Models\Task::query()->cursor()->each(function ($task) {
$time_log = json_decode(($task->time_log ?? ''), true) ?? [];
foreach($time_log as &$log){
if(count($log) > 4){
foreach ($time_log as &$log) {
if (count($log) > 4) {
$this->logMessage("Task #{$task->id} has a time log with more than 4 elements");
if($this->option('tasks') == 'true'){
$log = [(int)$log[0], (int)$log[1], (string)$log[2], (bool)$log[3]];
if ($this->option('tasks') == 'true') {
$log = [(int) $log[0], (int) $log[1], (string) $log[2], (bool) $log[3]];
}
}
elseif(count($log) == 4){
} elseif (count($log) == 4) {
if($this->option('tasks') == 'true'){
$log = [(int)$log[0], (int)$log[1], (string)$log[2], (bool)$log[3]];
if ($this->option('tasks') == 'true') {
$log = [(int) $log[0], (int) $log[1], (string) $log[2], (bool) $log[3]];
}
}
}
unset($log); // Unset the reference variable
if($this->option('tasks') == 'true'){
if ($this->option('tasks') == 'true') {
$task->time_log = json_encode($time_log);
$task->saveQuietly();
}
@@ -269,7 +269,7 @@ class CheckData extends Command
->havingRaw('count(users.id) > 1')
->get(['users.oauth_user_id']);
$this->logMessage($users->count().' users with duplicate oauth ids');
$this->logMessage($users->count() . ' users with duplicate oauth ids');
if ($users->count() > 0) {
$this->isValid = false;
@@ -278,7 +278,7 @@ class CheckData extends Command
if ($this->option('fix') == 'true') {
foreach ($users as $user) {
$first = true;
$this->logMessage('checking '.$user->oauth_user_id);
$this->logMessage('checking ' . $user->oauth_user_id);
$matches = DB::table('users')
->where('oauth_user_id', '=', $user->oauth_user_id)
->orderBy('id')
@@ -286,11 +286,11 @@ class CheckData extends Command
foreach ($matches as $match) {
if ($first) {
$this->logMessage('skipping '.$match->id);
$this->logMessage('skipping ' . $match->id);
$first = false;
continue;
}
$this->logMessage('updating '.$match->id);
$this->logMessage('updating ' . $match->id);
DB::table('users')
->where('id', '=', $match->id)
@@ -311,7 +311,7 @@ class CheckData extends Command
->whereNull('contact_key')
->orderBy('id')
->get(['id']);
$this->logMessage($contacts->count().' contacts without a contact_key');
$this->logMessage($contacts->count() . ' contacts without a contact_key');
if ($contacts->count() > 0) {
$this->isValid = false;
@@ -342,7 +342,7 @@ class CheckData extends Command
}
$clients = $clients->get(['clients.id', 'clients.user_id', 'clients.company_id']);
$this->logMessage($clients->count().' clients without any contacts');
$this->logMessage($clients->count() . ' clients without any contacts');
if ($clients->count() > 0) {
$this->isValid = false;
@@ -369,7 +369,7 @@ class CheckData extends Command
->orderBy('id')
->get(['id']);
$this->logMessage($contacts->count().' contacts without a contact_key');
$this->logMessage($contacts->count() . ' contacts without a contact_key');
if ($contacts->count() > 0) {
$this->isValid = false;
@@ -388,7 +388,7 @@ class CheckData extends Command
$vendors = Vendor::withTrashed()->doesntHave('contacts');
$this->logMessage($vendors->count().' vendors without any contacts');
$this->logMessage($vendors->count() . ' vendors without any contacts');
if ($vendors->count() > 0) {
$this->isValid = false;
@@ -420,7 +420,7 @@ class CheckData extends Command
->havingRaw('count(invoice_invitations.id) = 0')
->get(['invoices.id', 'invoices.user_id', 'invoices.company_id', 'invoices.client_id']);
$this->logMessage($invoices->count().' invoices without any invitations');
$this->logMessage($invoices->count() . ' invoices without any invitations');
if ($invoices->count() > 0) {
$this->isValid = false;
@@ -492,7 +492,7 @@ class CheckData extends Command
$contact_class = ClientContact::class;
$entity_key = \Illuminate\Support\Str::of(class_basename($entity))->snake()->append('_id')->toString();
$entity_obj = get_class($entity).'Invitation';
$entity_obj = get_class($entity) . 'Invitation';
if ($entity instanceof PurchaseOrder) {
$client_vendor_key = 'vendor_id';
@@ -549,7 +549,7 @@ class CheckData extends Command
{
$entity_key = "{$entity}_id";
$entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
$entity_obj = 'App\Models\\' . ucfirst(Str::camel($entity)) . 'Invitation';
foreach ($entities as $entity) {
$invitation = new $entity_obj();
@@ -665,12 +665,12 @@ class CheckData extends Command
if (round($total_paid_to_date, 2) != round($_client->client_paid_to_date, 2)) {
$this->wrong_paid_to_dates++;
$this->logMessage($client->present()->name().' id = # '.$client->id." - Client Paid To Date = {$client->paid_to_date} != Invoice Payments = {$total_paid_to_date} - {$_client->payments_applied} + {$credits_used_for_payments[0]->credit_payment}");
$this->logMessage($client->present()->name() . ' id = # ' . $client->id . " - Client Paid To Date = {$client->paid_to_date} != Invoice Payments = {$total_paid_to_date} - {$_client->payments_applied} + {$credits_used_for_payments[0]->credit_payment}");
$this->isValid = false;
if ($this->option('paid_to_date')) {
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->paid_to_date} to {$total_paid_to_date}");
$this->logMessage("# {$client->id} " . $client->present()->name() . ' - ' . $client->number . " Fixing {$client->paid_to_date} to {$total_paid_to_date}");
$client->paid_to_date = $total_paid_to_date;
$client->saveQuietly();
}
@@ -709,17 +709,17 @@ class CheckData extends Command
$clients = $this->clientBalanceQuery();
foreach ($clients as $client) {
$client = (array)$client;
$client = (array) $client;
if ((string) $client['invoice_balance'] != (string) $client['client_balance']) {
$this->wrong_paid_to_dates++;
$client_object = Client::withTrashed()->find($client['client_id']);
$this->logMessage($client_object->present()->name().' - '.$client_object->id." - calculated client balances do not match Invoice Balances = ". $client['invoice_balance'] ." - Client Balance = ".rtrim($client['client_balance'], '0'));
$this->logMessage($client_object->present()->name() . ' - ' . $client_object->id . " - calculated client balances do not match Invoice Balances = " . $client['invoice_balance'] . " - Client Balance = " . rtrim($client['client_balance'], '0'));
if ($this->option('client_balance')) {
$this->logMessage("# {$client_object->id} " . $client_object->present()->name().' - '.$client_object->number." Fixing {$client_object->balance} to " . $client['invoice_balance']);
$this->logMessage("# {$client_object->id} " . $client_object->present()->name() . ' - ' . $client_object->number . " Fixing {$client_object->balance} to " . $client['invoice_balance']);
$client_object->balance = $client['invoice_balance'];
$client_object->saveQuietly();
}
@@ -757,14 +757,14 @@ class CheckData extends Command
$over_payment = $over_payment * -1;
if (floatval($over_payment) == floatval($client->balance)) {
if (BcMath::equal($over_payment, $client->balance)) {
} else {
$this->logMessage("# {$client->id} # {$client->name} {$client->balance} is invalid should be {$over_payment}");
}
if ($this->option('client_balance') && (floatval($over_payment) != floatval($client->balance))) {
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to 0");
$this->logMessage("# {$client->id} " . $client->present()->name() . ' - ' . $client->number . " Fixing {$client->balance} to 0");
$client->balance = $over_payment;
$client->saveQuietly();
@@ -814,12 +814,12 @@ class CheckData extends Command
$this->wrong_balances++;
$ledger_balance = $ledger ? $ledger->balance : 0;
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger_balance}");
$this->logMessage("# {$client->id} " . $client->present()->name() . ' - ' . $client->number . " - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger_balance}");
$this->isValid = false;
if ($this->option('client_balance')) {
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
$this->logMessage("# {$client->id} " . $client->present()->name() . ' - ' . $client->number . " Fixing {$client->balance} to {$invoice_balance}");
$client->balance = $invoice_balance;
$client->saveQuietly();
}
@@ -847,13 +847,13 @@ class CheckData extends Command
if ($ledger && number_format($ledger->balance, 4) != number_format($client->balance, 4)) {
$this->wrong_balances++;
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." - Balance Failure - Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}");
$this->logMessage("# {$client->id} " . $client->present()->name() . ' - ' . $client->number . " - Balance Failure - Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}");
$this->isValid = false;
if ($this->option('ledger_balance')) {
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
$this->logMessage("# {$client->id} " . $client->present()->name() . ' - ' . $client->number . " Fixing {$client->balance} to {$invoice_balance}");
$client->balance = $invoice_balance;
$client->saveQuietly();
@@ -868,9 +868,7 @@ class CheckData extends Command
$this->logMessage("{$this->wrong_balances} clients with incorrect ledger balances");
}
private function checkLogoFiles()
{
}
private function checkLogoFiles() {}
/**
* @return array
@@ -930,7 +928,7 @@ class CheckData extends Command
if ($records->count()) {
$this->isValid = false;
$this->logMessage($records->count()." {$table} records with incorrect {$entityType} company id");
$this->logMessage($records->count() . " {$table} records with incorrect {$entityType} company id");
}
}
}
@@ -943,7 +941,7 @@ class CheckData extends Command
return 'companies';
}
return $type.'s';
return $type . 's';
}
public function checkAccountStatuses()
@@ -980,7 +978,7 @@ class CheckData extends Command
Client::query()->withTrashed()->whereNull("settings->currency_id")->orWhereJsonContains('settings', ['currency_id' => ''])->cursor()->each(function ($client) {
$settings = $client->settings;
$settings->currency_id = (string)$client->company->settings->currency_id;
$settings->currency_id = (string) $client->company->settings->currency_id;
$client->settings = $settings;
$client->saveQuietly();
@@ -1095,7 +1093,7 @@ class CheckData extends Command
foreach (Invoice::with(['payments'])->where('is_deleted', 0)->where('balance', '>', 0)->whereHas('payments')->where('status_id', 4)->cursor() as $invoice) {
$this->wrong_paid_status++;
$this->logMessage("# {$invoice->id} " . ' - '.$invoice->number." - Marked as paid, but balance = {$invoice->balance}");
$this->logMessage("# {$invoice->id} " . ' - ' . $invoice->number . " - Marked as paid, but balance = {$invoice->balance}");
if ($this->option('balance_status')) {
$val = $invoice->balance;
@@ -1106,7 +1104,7 @@ class CheckData extends Command
$p = $invoice->payments->first();
if ($p && (int)$p->amount == 0) {
if ($p && (int) $p->amount == 0) {
$p->amount = $val;
$p->applied = $val;
$p->saveQuietly();
@@ -1121,7 +1119,7 @@ class CheckData extends Command
}
}
$this->logMessage($this->wrong_paid_status." wrong invoices with bad balance state");
$this->logMessage($this->wrong_paid_status . " wrong invoices with bad balance state");
}
public function checkNinjaPortalUrls()
@@ -1138,7 +1136,7 @@ class CheckData extends Command
$cc = ClientContact::on('db-ninja-01')->where('company_id', config('ninja.ninja_default_company_id'))->where('email', $cu->user->email)->first();
if ($cc) {
$ninja_portal_url = config('ninja.ninja_client_portal')."/client/ninja/{$cc->contact_key}/{$cu->account->key}";
$ninja_portal_url = config('ninja.ninja_client_portal') . "/client/ninja/{$cc->contact_key}/{$cu->account->key}";
$cu->ninja_portal_url = $ninja_portal_url;
$cu->save();
@@ -1151,7 +1149,7 @@ class CheckData extends Command
$cc = $c->contacts()->first();
if ($cc) {
$ninja_portal_url = config('ninja.ninja_client_portal')."/client/ninja/{$cc->contact_key}/{$cu->account->key}";
$ninja_portal_url = config('ninja.ninja_client_portal') . "/client/ninja/{$cc->contact_key}/{$cu->account->key}";
$cu->ninja_portal_url = $ninja_portal_url;
$cu->save();

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -55,7 +55,7 @@ class CreateAccount extends Command
*/
public function handle()
{
$this->info(date('r').' Create Single Account...');
$this->info(date('r') . ' Create Single Account...');
$this->createAccount();
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -95,7 +95,7 @@ class CreateSingleAccount extends Command
MultiDB::setDb($this->option('database'));
$this->info(date('r').' Create Single Sample Account...');
$this->info(date('r') . ' Create Single Sample Account...');
$this->count = 5;
$this->gateway = $this->argument('gateway');
@@ -142,7 +142,7 @@ class CreateSingleAccount extends Command
'default_password_timeout' => 30 * 60000,
'portal_mode' => 'domain',
'portal_domain' => 'http://ninja.test:8000',
'track_inventory' => true
'track_inventory' => true,
]);
$custom_fields = new \stdClass();
@@ -223,21 +223,21 @@ class CreateSingleAccount extends Command
'user_id' => $user->id,
'company_id' => $company->id,
'name' => 'GST',
'rate' => 10
'rate' => 10,
]);
TaxRate::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'name' => 'VAT',
'rate' => 17.5
'rate' => 17.5,
]);
TaxRate::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'name' => 'CA Sales Tax',
'rate' => 5
'rate' => 5,
]);
$bi = BankIntegration::factory()->create([
@@ -255,13 +255,13 @@ class CreateSingleAccount extends Command
$btr = BankTransactionRule::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'applies_to' => (bool)rand(0, 1) ? 'CREDIT' : 'DEBIT',
'applies_to' => (bool) rand(0, 1) ? 'CREDIT' : 'DEBIT',
]);
$client = Client::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'name' => 'cypress'
'name' => 'cypress',
]);
$client->custom_value1 = $company->company_key;
@@ -278,11 +278,11 @@ class CreateSingleAccount extends Command
]);
$this->info('Creating '.$this->count.' clients');
$this->info('Creating ' . $this->count . ' clients');
for ($x = 0; $x < $this->count; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->info('Creating client # ' . $z);
$this->createClient($company, $user);
}
@@ -292,15 +292,15 @@ class CreateSingleAccount extends Command
for ($x = 0; $x < $this->count; $x++) {
$client = $company->clients->random();
$this->info('creating invoice for client #'.$client->id);
$this->info('creating invoice for client #' . $client->id);
$this->createInvoice($client);
$this->info('creating invoice for client #'.$client->id);
$this->info('creating invoice for client #' . $client->id);
$this->createInvoice($client);
$this->info('creating invoice for client #'.$client->id);
$this->info('creating invoice for client #' . $client->id);
$this->createInvoice($client);
$this->info('creating invoice for client #'.$client->id);
$this->info('creating invoice for client #' . $client->id);
$this->createInvoice($client);
$this->info('creating invoice for client #'.$client->id);
$this->info('creating invoice for client #' . $client->id);
$this->createInvoice($client);
$client = $company->clients->random();
@@ -310,17 +310,17 @@ class CreateSingleAccount extends Command
$client = $company->clients->random();
$this->info('creating quote for client #'.$client->id);
$this->info('creating quote for client #' . $client->id);
$this->createQuote($client);
$client = $company->clients->random();
$this->info('creating expense for client #'.$client->id);
$this->info('creating expense for client #' . $client->id);
$this->createExpense($client);
$client = $company->clients->random();
$this->info('creating vendor for client #'.$client->id);
$this->info('creating vendor for client #' . $client->id);
$this->createVendor($client);
$client = $company->clients->random();
@@ -423,6 +423,18 @@ class CreateSingleAccount extends Command
'quantity' => 1,
]);
$p1a = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'pro_plan_annual',
'notes' => 'The Pro Plan Annual',
'cost' => 120,
'price' => 120,
'quantity' => 1,
]);
$p2 = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
@@ -474,7 +486,7 @@ class CreateSingleAccount extends Command
]);
$p4= Product::factory()->create([
$p4 = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'docuninja_user',
@@ -528,7 +540,7 @@ class CreateSingleAccount extends Command
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$sub->save();
if(!\App\Models\Subscription::find(6)){
if (!\App\Models\Subscription::find(6)) {
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->id = 6;
@@ -552,17 +564,29 @@ class CreateSingleAccount extends Command
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$sub->max_seats_limit =2;
$sub->max_seats_limit = 2;
$sub->per_seat_enabled = true;
$sub->save();
}
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->id = 66;
$sub->name = " PRO Pro Plan Annual";
$sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p1a->hashed_id}";
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_ANNUALLY;
$sub->save();
$_sub = $sub->replicate();
$_sub->id = 41;
$_sub->name = "Enterprise Plan 3-5 Users";
$_sub->recurring_product_ids = "{$pe5->hashed_id}";
$_sub->max_seats_limit =5;
$_sub->max_seats_limit = 5;
$_sub->per_seat_enabled = true;
$_sub->save();
@@ -570,7 +594,7 @@ class CreateSingleAccount extends Command
$_sub->id = 46;
$_sub->name = "Enterprise Plan 6-10 Users";
$_sub->recurring_product_ids = "{$pe10->hashed_id}";
$_sub->max_seats_limit =10;
$_sub->max_seats_limit = 10;
$_sub->per_seat_enabled = true;
$_sub->save();
@@ -578,7 +602,7 @@ class CreateSingleAccount extends Command
$_sub->id = 51;
$_sub->name = "Enterprise Plan 11-20 Users";
$_sub->recurring_product_ids = "{$pe20->hashed_id}";
$_sub->max_seats_limit =20;
$_sub->max_seats_limit = 20;
$_sub->per_seat_enabled = true;
$_sub->save();
@@ -602,21 +626,21 @@ class CreateSingleAccount extends Command
$sub->save();
if($config = config('admin-api.products')){
if ($config = config('admin-api.products')) {
foreach($config as $key => $product){
foreach ($config as $key => $product) {
if(!$p = Product::where('product_key', $key)->first()){
if (!$p = Product::where('product_key', $key)->first()) {
$p = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => $key,
'notes' => $product['description'],
'price' => $product['price']
'price' => $product['price'],
]);
if(!Subscription::find($product['subscription_id'])){
if (!Subscription::find($product['subscription_id'])) {
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->id = $product['subscription_id'];
@@ -721,7 +745,7 @@ class CreateSingleAccount extends Command
'status_id' => $status->id ?? null,
'number' => rand(10000, 100000000),
'rate' => rand(1, 150),
'client_id' => $client->id
'client_id' => $client->id,
]);
}
@@ -739,7 +763,7 @@ class CreateSingleAccount extends Command
Carbon::now()->addSeconds($min)->timestamp,
Carbon::now()->addSeconds($min += $rando)->timestamp,
$this->faker->sentence,
rand(0, 1) === 0 ? false : true
rand(0, 1) === 0 ? false : true,
];
$min += 300;
@@ -1178,7 +1202,7 @@ class CreateSingleAccount extends Command
'company_id' => $company->id,
'name' => 'cypress',
'country_id' => 826,
'settings' => $c_settings
'settings' => $c_settings,
]);
$cg = new CompanyGateway();

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -82,7 +82,7 @@ class CreateTestData extends Command
$this->invoice_repo = new InvoiceRepository();
$this->info(date('r').' Running CreateTestData...');
$this->info(date('r') . ' Running CreateTestData...');
$this->count = $this->argument('count');
$this->info('Warming up cache');
@@ -140,11 +140,11 @@ class CreateTestData extends Command
'company_id' => $company->id,
]);
$this->info('Creating '.$this->count.' clients');
$this->info('Creating ' . $this->count . ' clients');
for ($x = 0; $x < $this->count; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->info('Creating client # ' . $z);
$this->createClient($company, $user);
}
@@ -152,37 +152,37 @@ class CreateTestData extends Command
for ($x = 0; $x < $this->count; $x++) {
$client = $company->clients->random();
$this->info('creating invoice for client #'.$client->id);
$this->info('creating invoice for client #' . $client->id);
$this->createInvoice($client);
$client = $company->clients->random();
$this->info('creating credit for client #'.$client->id);
$this->info('creating credit for client #' . $client->id);
$this->createCredit($client);
$client = $company->clients->random();
$this->info('creating quote for client #'.$client->id);
$this->info('creating quote for client #' . $client->id);
$this->createQuote($client);
$client = $company->clients->random();
$this->info('creating expense for client #'.$client->id);
$this->info('creating expense for client #' . $client->id);
$this->createExpense($client);
$client = $company->clients->random();
$this->info('creating vendor for client #'.$client->id);
$this->info('creating vendor for client #' . $client->id);
$this->createVendor($client);
$client = $company->clients->random();
$this->info('creating task for client #'.$client->id);
$this->info('creating task for client #' . $client->id);
$this->createTask($client);
$client = $company->clients->random();
$this->info('creating project for client #'.$client->id);
$this->info('creating project for client #' . $client->id);
$this->createProject($client);
}
}
@@ -236,11 +236,11 @@ class CreateTestData extends Command
$this->count = $this->count * 10;
$this->info('Creating '.$this->count.' clients');
$this->info('Creating ' . $this->count . ' clients');
for ($x = 0; $x < $this->count; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->info('Creating client # ' . $z);
$this->createClient($company, $user);
}
@@ -248,37 +248,37 @@ class CreateTestData extends Command
for ($x = 0; $x < $this->count * 100; $x++) {
$client = $company->clients->random();
$this->info('creating invoice for client #'.$client->id);
$this->info('creating invoice for client #' . $client->id);
$this->createInvoice($client);
$client = $company->clients->random();
$this->info('creating credit for client #'.$client->id);
$this->info('creating credit for client #' . $client->id);
$this->createCredit($client);
$client = $company->clients->random();
$this->info('creating quote for client #'.$client->id);
$this->info('creating quote for client #' . $client->id);
$this->createQuote($client);
$client = $company->clients->random();
$this->info('creating expense for client #'.$client->id);
$this->info('creating expense for client #' . $client->id);
$this->createExpense($client);
$client = $company->clients->random();
$this->info('creating vendor for client #'.$client->id);
$this->info('creating vendor for client #' . $client->id);
$this->createVendor($client);
$client = $company->clients->random();
$this->info('creating task for client #'.$client->id);
$this->info('creating task for client #' . $client->id);
$this->createTask($client);
$client = $company->clients->random();
$this->info('creating project for client #'.$client->id);
$this->info('creating project for client #' . $client->id);
$this->createProject($client);
}
}
@@ -333,11 +333,11 @@ class CreateTestData extends Command
$this->count = $this->count * 10;
$this->info('Creating '.$this->count.' clients');
$this->info('Creating ' . $this->count . ' clients');
for ($x = 0; $x < $this->count * 100; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->info('Creating client # ' . $z);
$this->createClient($company, $user);
}
@@ -345,37 +345,37 @@ class CreateTestData extends Command
for ($x = 0; $x < $this->count; $x++) {
$client = $company->clients->random();
$this->info('creating invoice for client #'.$client->id);
$this->info('creating invoice for client #' . $client->id);
$this->createInvoice($client);
$client = $company->clients->random();
$this->info('creating credit for client #'.$client->id);
$this->info('creating credit for client #' . $client->id);
$this->createCredit($client);
$client = $company->clients->random();
$this->info('creating quote for client #'.$client->id);
$this->info('creating quote for client #' . $client->id);
$this->createQuote($client);
$client = $company->clients->random();
$this->info('creating expense for client #'.$client->id);
$this->info('creating expense for client #' . $client->id);
$this->createExpense($client);
$client = $company->clients->random();
$this->info('creating vendor for client #'.$client->id);
$this->info('creating vendor for client #' . $client->id);
$this->createVendor($client);
$client = $company->clients->random();
$this->info('creating task for client #'.$client->id);
$this->info('creating task for client #' . $client->id);
$this->createTask($client);
$client = $company->clients->random();
$this->info('creating project for client #'.$client->id);
$this->info('creating project for client #' . $client->id);
$this->createProject($client);
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -222,11 +222,11 @@ class DemoMode extends Command
'company_id' => $company->id,
]);
$this->info('Creating '.$this->count.' clients');
$this->info('Creating ' . $this->count . ' clients');
for ($x = 0; $x < $this->count; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->info('Creating client # ' . $z);
$this->createClient($company, $user, $u2->id);
}
@@ -234,7 +234,7 @@ class DemoMode extends Command
for ($x = 0; $x < $this->count; $x++) {
$client = $company->clients->random();
$this->info('creating entities for client #'.$client->id);
$this->info('creating entities for client #' . $client->id);
$this->createInvoice($client, $u2->id);
$this->createRecurringInvoice($client, $u2->id);

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -68,7 +68,7 @@ class EncryptNinja extends Command
foreach ($this->files as $file) {
$contents = Storage::disk('base')->get($file);
$encrypted = encrypt($contents);
Storage::disk('base')->put($file.".enc", $encrypted);
Storage::disk('base')->put($file . ".enc", $encrypted);
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -86,7 +86,7 @@ class HostedMigrations extends Command
if ($file->getExtension() === 'zip') {
$company = $user->companies()->first();
$this->info('Started processing: '.$file->getBasename().' at '.now());
$this->info('Started processing: ' . $file->getBasename() . ' at ' . now());
$zip = new ZipArchive();
$archive = $zip->open($file->getRealPath());
@@ -104,7 +104,7 @@ class HostedMigrations extends Command
$import_file = public_path("storage/migrations/$filename/migration.json");
Import::dispatch($import_file, $user->companies()->first(), $user);
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
} catch (NonExistingMigrationFile|ProcessingMigrationArchiveFailed|ResourceNotAvailableForMigration|MigrationValidatorFailed|ResourceDependencyMissing $e) {
\Mail::to($user)->send(new MigrationFailed($e, $company));
if (app()->environment() !== 'production') {

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -85,7 +85,7 @@ class ImportMigrations extends Command
$user = $this->getUser();
$company = $this->getUser()->companies()->first();
$this->info('Started processing: '.$file->getBasename().' at '.now());
$this->info('Started processing: ' . $file->getBasename() . ' at ' . now());
$zip = new ZipArchive();
$archive = $zip->open($file->getRealPath());
@@ -104,7 +104,7 @@ class ImportMigrations extends Command
Import::dispatch($import_file, $this->getUser()->companies()->first(), $this->getUser());
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
} catch (NonExistingMigrationFile|ProcessingMigrationArchiveFailed|ResourceNotAvailableForMigration|MigrationValidatorFailed|ResourceDependencyMissing $e) {
\Mail::to($user)->send(new MigrationFailed($e, $company));
if (app()->environment() !== 'production') {
@@ -122,7 +122,7 @@ class ImportMigrations extends Command
$user = User::factory()->create([
'account_id' => $account->id,
'email' => Str::random(10).'@example.com',
'email' => Str::random(10) . '@example.com',
'confirmation_code' => $this->createDbHash($company->db),
]);

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -62,7 +62,7 @@ class MobileLocalization extends Command
private function laravelResources()
{
$resources = (array)$this->getResources();
$resources = (array) $this->getResources();
if (is_iterable($resources)) {
foreach ($resources as $key => $val) {
@@ -117,7 +117,7 @@ class MobileLocalization extends Command
$data = str_replace("'", '"', $data);
$data = str_replace("\#", "'", $data);
return json_decode('{'.rtrim($data, ',').'}');
return json_decode('{' . rtrim($data, ',') . '}');
}
protected function getOptions()

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -34,7 +34,7 @@ class OpenApiYaml extends Command
private array $directories = [
'/components/schemas',
'/paths/'
'/paths/',
];
/**
@@ -65,8 +65,8 @@ class OpenApiYaml extends Command
}
Storage::disk('base')->delete('/openapi/api-docs.yaml');
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/info.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/paths.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path . '/info.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path . '/paths.yaml'));
//iterate paths
$directory = new DirectoryIterator($path . '/paths/');
@@ -77,11 +77,11 @@ class OpenApiYaml extends Command
}
}
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path . '/components.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/examples.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path . '/components/examples.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/responses.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path . '/components/responses.yaml'));
$directory = new DirectoryIterator($path . '/components/responses/');
@@ -91,7 +91,7 @@ class OpenApiYaml extends Command
}
}
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/parameters.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path . '/components/parameters.yaml'));
$directory = new DirectoryIterator($path . '/components/parameters/');
@@ -101,7 +101,7 @@ class OpenApiYaml extends Command
}
}
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/schemas.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path . '/components/schemas.yaml'));
//iterate schemas
@@ -115,6 +115,6 @@ class OpenApiYaml extends Command
// Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/components/schemas/account.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path.'/misc/misc.yaml'));
Storage::disk('base')->append('/openapi/api-docs.yaml', file_get_contents($path . '/misc/misc.yaml'));
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -0,0 +1,237 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\Utils\Ninja;
use App\Models\Account;
use App\Models\Company;
use App\Utils\TempFile;
use Illuminate\Console\Command;
use App\Utils\Traits\SavesDocuments;
use App\Services\EDocument\Gateway\Storecove\Storecove;
class PullPeppolDocs extends Command
{
use SavesDocuments;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:pull-peppol-docs';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Pull Peppol docs from the E-Invoice API';
/**
* Execute the console command.
*
* @return mixed
* @throws \Exception
*/
public function handle()
{
set_time_limit(3600);
if (Ninja::isHosted()) {
$this->info("Pulling Peppol docs is not supported on hosted.");
return;
}
$this->info("Pulling Peppol docs from the E-Invoice API");
$account = Account::first();
$quota_count = $account->e_invoice_quota;
$this->info("E-Invoice Quota Remaining: $quota_count");
$this->info("E-Invoice Token: " . $account->e_invoicing_token);
if (!isset($account->e_invoicing_token)) {
$this->info("No e-invoicing token found! You will not be able to authenticate with the E-Invoice API. Try logging out and back in again.");
$this->info("Updating Token...");
$response_array = $this->updateToken($account);
$this->info($response[1]);
if($response_array[0] != 200){
$this->error("Failed to update token exiting");
return;
}
}
$this->info("License key in use: " . config('ninja.license_key'));
Account::query()
->with('companies')
->where('e_invoice_quota', '>', 0)
->whereHas('companies', function ($q) {
$q->whereNotNull('legal_entity_id');
})
->cursor()
->each(function ($account) {
$account->companies->filter(function ($company) {
return $company->settings->e_invoice_type == 'PEPPOL' && ($company->tax_data->acts_as_receiver ?? false);
})
->each(function ($company) {
$this->info("Pulling Peppol docs for company: {$company->present()->name()}");
$response = \Illuminate\Support\Facades\Http::baseUrl(config('ninja.hosted_ninja_url'))
->withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'X-EInvoice-Token' => $company->account->e_invoicing_token,
])
->post('/api/einvoice/peppol/documents', data: [
'license_key' => config('ninja.license_key'),
'account_key' => $company->account->key,
'company_key' => $company->company_key,
'legal_entity_id' => $company->legal_entity_id,
]);
if ($response->successful()) {
$hash = $response->header('X-CONFIRMATION-HASH');
$this->info($response->body() );
$this->handleSuccess($response->json(), $company, $hash);
} else {
nlog($response->body());
}
});
});
}
private function updateToken(Account $account): array
{
$response = \Illuminate\Support\Facades\Http::baseUrl(config('ninja.hosted_ninja_url'))
->withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
])
->post('/api/einvoice/tokens/rotate', data: [
'license' => config('ninja.license_key'),
'account_key' => $account->key,
]);
if ($response->successful()) {
$account->update([
'e_invoicing_token' => $response->json('token'),
]);
return [200, $response->json('token')];
// return 'success';
}
return [422, $response->body()];
}
/**
* Handle the success of the Peppol docs pull
*
* @param array $received_documents
* @param Company $company
* @param string $hash
* @return void
*/
private function handleSuccess(array $received_documents, Company $company, string $hash): void
{
$storecove = new Storecove();
$doc_count = count($received_documents);
$this->info("{$doc_count} documents found.");
if ($doc_count > 0) {
$this->info('Processing documents...');
foreach ($received_documents as $document) {
$storecove_invoice = $storecove->expense->getStorecoveInvoice(json_encode($document['document']['invoice']));
$expense = $storecove->expense->createExpense($storecove_invoice, $company);
$file_name = $document['guid'];
if (strlen($document['html'] ?? '') > 5) {
$upload_document = TempFile::UploadedFileFromRaw($document['html'], "{$file_name}.html", 'text/html');
$this->saveDocument($upload_document, $expense, true);
$upload_document = null;
}
if (strlen($document['original_base64_xml'] ?? '') > 5) {
$upload_document = TempFile::UploadedFileFromBase64($document['original_base64_xml'], "{$file_name}.xml", 'application/xml');
$this->saveDocument($upload_document, $expense, true);
$upload_document = null;
}
foreach ($document['document']['invoice']['attachments'] as $attachment) {
$upload_document = TempFile::UploadedFileFromBase64($attachment['document'], $attachment['filename'], $attachment['mime_type']);
$this->saveDocument($upload_document, $expense, true);
$upload_document = null;
}
$this->info("Document {$file_name} processed.");
}
$this->info("Finished processing documents, flushing upstream...");
$response = \Illuminate\Support\Facades\Http::baseUrl(config('ninja.hosted_ninja_url'))
->withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'X-EInvoice-Token' => $company->account->e_invoicing_token,
])
->post('/api/einvoice/peppol/documents/flush', data: [
'license_key' => config('ninja.license_key'),
'account_key' => $company->account->key,
'company_key' => $company->company_key,
'legal_entity_id' => $company->legal_entity_id,
'hash' => $hash,
]);
if ($response->successful()) {
}
$this->info("Finished flushing upstream.");
$this->info("Finished task!");
}
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -56,7 +56,7 @@ class ReactBuilder extends Command
$directoryIterator = false;
try {
$directoryIterator = new \RecursiveDirectoryIterator(public_path('react/v'.config('ninja.app_version').'/'), \RecursiveDirectoryIterator::SKIP_DOTS);
$directoryIterator = new \RecursiveDirectoryIterator(public_path('react/v' . config('ninja.app_version') . '/'), \RecursiveDirectoryIterator::SKIP_DOTS);
} catch (\Exception $e) {
$this->error('React files not found');
return;
@@ -65,14 +65,14 @@ class ReactBuilder extends Command
foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {
if ($file->getExtension() == 'js') {
if (str_contains($file->getFileName(), 'index-')) {
$includes .= '<script type="module" crossorigin src="/react/v'.config('ninja.app_version').'/'.$file->getFileName().'"></script>'."\n";
$includes .= '<script type="module" crossorigin src="/react/v' . config('ninja.app_version') . '/' . $file->getFileName() . '"></script>' . "\n";
} else {
$includes .= '<link rel="modulepreload" href="/react/v'.config('ninja.app_version').'/'.$file->getFileName().'">'."\n";
$includes .= '<link rel="modulepreload" href="/react/v' . config('ninja.app_version') . '/' . $file->getFileName() . '">' . "\n";
}
}
if (str_contains($file->getFileName(), '.css')) {
$includes .= '<link rel="stylesheet" href="/react/v'.config('ninja.app_version').'/'.$file->getFileName().'">'."\n";
$includes .= '<link rel="stylesheet" href="/react/v' . config('ninja.app_version') . '/' . $file->getFileName() . '">' . "\n";
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -81,8 +81,8 @@ class S3Cleanup extends Command
private function logMessage($str)
{
$str = date('Y-m-d h:i:s').' '.$str;
$str = date('Y-m-d h:i:s') . ' ' . $str;
$this->info($str);
$this->log .= $str."\n";
$this->log .= $str . "\n";
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -82,7 +82,7 @@ class SendRemindersCron extends Command
$invoice = $this->calcLateFee($invoice, $reminder_template);
//check if this reminder needs to be emailed
if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3']) && $invoice->client->getSetting('enable_'.$reminder_template)) {
if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3']) && $invoice->client->getSetting('enable_' . $reminder_template)) {
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
EmailEntity::dispatch($invitation->withoutRelations(), $invitation->company->db, $reminder_template);
nlog("Firing reminder email for invoice {$invoice->number}");
@@ -205,9 +205,7 @@ class SendRemindersCron extends Command
}
}
private function webHookExpiredQuotes()
{
}
private function webHookExpiredQuotes() {}
private function executeWebhooks()
{

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -118,7 +118,7 @@ class TranslationsExport extends Command
foreach ($this->langs as $lang) {
$import_file = "textsphp_{$lang}.php";
$dir = $this->option('path') ?? storage_path('lang_import/');
$path = $dir.$import_file;
$path = $dir . $import_file;
if (file_exists($path)) {
$this->logMessage($path);
@@ -152,8 +152,8 @@ class TranslationsExport extends Command
private function logMessage($str)
{
$str = date('Y-m-d h:i:s').' '.$str;
$str = date('Y-m-d h:i:s') . ' ' . $str;
$this->info($str);
$this->log .= $str."\n";
$this->log .= $str . "\n";
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -82,7 +82,7 @@ class TypeCheck extends Command
if ($client) {
$this->checkClient($client);
} else {
$this->logMessage(date('Y-m-d h:i:s').' Could not find this client');
$this->logMessage(date('Y-m-d h:i:s') . ' Could not find this client');
}
}
@@ -92,14 +92,14 @@ class TypeCheck extends Command
if ($company) {
$this->checkCompany($company);
} else {
$this->logMessage(date('Y-m-d h:i:s').' Could not find this company');
$this->logMessage(date('Y-m-d h:i:s') . ' Could not find this company');
}
}
}
private function checkClient($client)
{
$this->logMessage(date('Y-m-d h:i:s').' Checking Client => '.$client->present()->name().' '.$client->id);
$this->logMessage(date('Y-m-d h:i:s') . ' Checking Client => ' . $client->present()->name() . ' ' . $client->id);
$entity_settings = $this->checkSettingType($client->settings);
$entity_settings->md5 = md5(time());
@@ -109,14 +109,14 @@ class TypeCheck extends Command
private function checkCompany($company)
{
$this->logMessage(date('Y-m-d h:i:s').' Checking Company => '.$company->present()->name().' '.$company->id);
$this->logMessage(date('Y-m-d h:i:s') . ' Checking Company => ' . $company->present()->name() . ' ' . $company->id);
$company->saveSettings((array) $company->settings, $company);
}
private function checkAll()
{
$this->logMessage(date('Y-m-d h:i:s').' Checking all clients and companies.');
$this->logMessage(date('Y-m-d h:i:s') . ' Checking all clients and companies.');
Client::withTrashed()->cursor()->each(function ($client) {
$this->logMessage("Checking client {$client->id}");
@@ -141,8 +141,8 @@ class TypeCheck extends Command
private function logMessage($str)
{
$str = date('Y-m-d h:i:s').' '.$str;
$str = date('Y-m-d h:i:s') . ' ' . $str;
$this->info($str);
$this->log .= $str."\n";
$this->log .= $str . "\n";
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www/elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -16,8 +16,8 @@ class ClientRegistrationFields
{
public static function generate()
{
$data =
[
$data
= [
[
'key' => 'first_name',
'required' => true,

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -90,7 +90,7 @@ class ClientSettings extends BaseSettings
}
if (is_array($client_settings)) {
$client_settings = (object)$client_settings;
$client_settings = (object) $client_settings;
}
foreach ($company_settings as $key => $value) {

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -944,7 +944,7 @@ class CompanySettings extends BaseSettings
$notification = new stdClass();
$notification->email = [];
if(Ninja::isSelfHost()) {
if (Ninja::isSelfHost()) {
$notification->email = ['invoice_sent_all', 'payment_success_all', 'payment_manual_all'];
}
@@ -1094,7 +1094,7 @@ class CompanySettings extends BaseSettings
],
'statement_details' => [
'$statement_date',
'$balance'
'$balance',
],
'delivery_note_columns' => [
'$product.item',

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -0,0 +1,10 @@
<?php
namespace App\DataMapper;
class DocuNinjaSync
{
public string $document_id = '';
public string $document_invitation_id = '';
public string $signature = '';
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -31,7 +31,7 @@ class EmailTemplateDefaults
'email_template_custom2',
'email_template_custom3',
'email_template_purchase_order',
'email_template_payment_failed'
'email_template_payment_failed',
];
public static function getDefaultTemplate($template, $locale)
@@ -141,7 +141,7 @@ class EmailTemplateDefaults
public static function emailPaymentFailedTemplate()
{
return '<p>$client<br><br>'.ctrans('texts.client_payment_failure_body', ['invoice' => '$number', 'amount' => '$amount']).'</p><div>$payment_error</div><br><div>$view_button</div>';
return '<p>$client<br><br>' . ctrans('texts.client_payment_failure_body', ['invoice' => '$number', 'amount' => '$amount']) . '</p><div>$payment_error</div><br><div>$view_button</div>';
}
public static function emailQuoteReminder1Subject()
@@ -152,7 +152,7 @@ class EmailTemplateDefaults
public static function emailQuoteReminder1Body()
{
return '<p>$client<br><br>'.self::transformText('quote_reminder_message').'</p><div>$view_button</div>';
return '<p>$client<br><br>' . self::transformText('quote_reminder_message') . '</p><div>$view_button</div>';
}
@@ -178,14 +178,14 @@ class EmailTemplateDefaults
public static function emailInvoiceTemplate()
{
$invoice_message = '<p>$client<br><br>'.self::transformText('invoice_message').'</p><div>$view_button</div>';
$invoice_message = '<p>$client<br><br>' . self::transformText('invoice_message') . '</p><div>$view_button</div>';
return $invoice_message;
}
public static function emailInvoiceReminderTemplate()
{
$invoice_message = '<p>$client<br><br>'.self::transformText('reminder_message').'</p><div>$view_button</div>';
$invoice_message = '<p>$client<br><br>' . self::transformText('reminder_message') . '</p><div>$view_button</div>';
return $invoice_message;
}
@@ -197,7 +197,7 @@ class EmailTemplateDefaults
public static function emailQuoteTemplate()
{
$quote_message = '<p>$client<br><br>'.self::transformText('quote_message').'</p><div>$view_button</div>';
$quote_message = '<p>$client<br><br>' . self::transformText('quote_message') . '</p><div>$view_button</div>';
return $quote_message;
}
@@ -214,28 +214,28 @@ class EmailTemplateDefaults
public static function emailPurchaseOrderTemplate()
{
$purchase_order_message = '<p>$vendor<br><br>'.self::transformText('purchase_order_message').'</p><div>$view_button</div>';
$purchase_order_message = '<p>$vendor<br><br>' . self::transformText('purchase_order_message') . '</p><div>$view_button</div>';
return $purchase_order_message;
}
public static function emailPaymentTemplate()
{
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div>$view_button</div>';
$payment_message = '<p>$client<br><br>' . self::transformText('payment_message') . '<br><br>$invoices</p><div>$view_button</div>';
return $payment_message;
}
public static function emailCreditTemplate()
{
$credit_message = '<p>$client<br><br>'.self::transformText('credit_message').'</p><div>$view_button</div>';
$credit_message = '<p>$client<br><br>' . self::transformText('credit_message') . '</p><div>$view_button</div>';
return $credit_message;
}
public static function emailPaymentPartialTemplate()
{
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div>$view_button</div>';
$payment_message = '<p>$client<br><br>' . self::transformText('payment_message') . '<br><br>$invoices</p><div>$view_button</div>';
return $payment_message;
}
@@ -292,7 +292,7 @@ class EmailTemplateDefaults
public static function emailStatementTemplate()
{
$statement_message = '<p>$client<br><br>'.self::transformText('client_statement_body').'<br></p>';
$statement_message = '<p>$client<br><br>' . self::transformText('client_statement_body') . '<br></p>';
return $statement_message;
@@ -303,6 +303,6 @@ class EmailTemplateDefaults
{
//preformat the string, removing trailing colons.
return str_replace(':', '$', rtrim(ctrans('texts.'.$string), ':'));
return str_replace(':', '$', rtrim(ctrans('texts.' . $string), ':'));
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper;
use App\Casts\ExpenseSyncCast;
use App\DataMapper\TaxReport\TaxReport;
use Illuminate\Contracts\Database\Eloquent\Castable;
/**
* ExpenseSync.
*/
class ExpenseSync implements Castable
{
public string $qb_id;
public function __construct(array $attributes = [])
{
$this->qb_id = $attributes['qb_id'] ?? '';
}
/**
* Get the name of the caster class to use when casting from / to this cast target.
*
* @param array<string, mixed> $arguments
*/
public static function castUsing(array $arguments): string
{
return ExpenseSyncCast::class;
}
public static function fromArray(array $data): self
{
return new self($data);
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www/elastic.co/licensing/elastic-license
*/
@@ -36,7 +36,7 @@ class InvoiceBackup implements Castable
*/
public function __construct(
public string $guid = '',
public Cancellation $cancellation = new Cancellation(0,0),
public Cancellation $cancellation = new Cancellation(0, 0),
public ?string $parent_invoice_id = null,
public ?string $parent_invoice_number = null,
public ?string $document_type = null,
@@ -103,4 +103,3 @@ class InvoiceBackup implements Castable
return $this->child_invoice_ids->toArray();
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -70,6 +70,8 @@ class InvoiceItem
public $unit_code = 'C62';
public $income_account_id = '';
public static $casts = [
'net_cost' => 'float',
'task_id' => 'string',
@@ -99,5 +101,6 @@ class InvoiceItem
'custom_value3' => 'string',
'custom_value4' => 'string',
'unit_code' => 'string',
'income_account_id' => 'string',
];
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -21,13 +21,12 @@ use Illuminate\Contracts\Database\Eloquent\Castable;
*/
class InvoiceSync implements Castable
{
public string $qb_id;
public function __construct(array $attributes = [])
{
$this->qb_id = $attributes['qb_id'] ?? '';
}
public function __construct(
public string $qb_id = '',
public array $invitations = [],
public bool $dn_completed = false,
){}
/**
* Get the name of the caster class to use when casting from / to this cast target.
*
@@ -40,6 +39,62 @@ class InvoiceSync implements Castable
public static function fromArray(array $data): self
{
return new self($data);
return new self(
qb_id: $data['qb_id'] ?? '',
invitations: $data['invitations'] ?? [],
dn_completed: $data['dn_completed'] ?? false,
);
}
/**
* Add an invitation to the invitations array
*
* @param string $invitation_key The invitation key
* @param string $dn_id The DocuNinja ID
* @param string $dn_invitation_id The DocuNinja invitation ID
* @param string $dn_sig The DocuNinja signature
*/
public function addInvitation(
string $invitation_key,
string $dn_id,
string $dn_invitation_id,
string $dn_sig
): void {
$this->invitations[] = [
'invitation_key' => $invitation_key,
'dn_id' => $dn_id,
'dn_invitation_id' => $dn_invitation_id,
'dn_sig' => $dn_sig,
];
}
/**
* Get invitation data by invitation key
*
* @param string $invitation_key The invitation key
* @return array|null The invitation data or null if not found
*/
public function getInvitation(string $invitation_key): ?array
{
foreach ($this->invitations as $invitation) {
if ($invitation['invitation_key'] === $invitation_key) {
return $invitation;
}
}
return null;
}
/**
* Remove an invitation by invitation key
*
* @param string $invitation_key The invitation key
*/
public function removeInvitation(string $invitation_key): void
{
$this->invitations = array_filter($this->invitations, function($invitation) use ($invitation_key) {
return $invitation['invitation_key'] !== $invitation_key;
});
// Re-index the array to maintain numeric keys
$this->invitations = array_values($this->invitations);
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -32,6 +32,8 @@ class QuickbooksSettings implements Castable
public string $baseURL;
public string $companyName;
public QuickbooksSync $settings;
public function __construct(array $attributes = [])
@@ -42,6 +44,7 @@ class QuickbooksSettings implements Castable
$this->accessTokenExpiresAt = $attributes['accessTokenExpiresAt'] ?? 0;
$this->refreshTokenExpiresAt = $attributes['refreshTokenExpiresAt'] ?? 0;
$this->baseURL = $attributes['baseURL'] ?? '';
$this->companyName = $attributes['companyName'] ?? '';
$this->settings = new QuickbooksSync($attributes['settings'] ?? []);
}
@@ -55,4 +58,75 @@ class QuickbooksSettings implements Castable
return new self($data);
}
public function toArray(): array
{
return [
'accessTokenKey' => $this->accessTokenKey,
'refresh_token' => $this->refresh_token,
'realmID' => $this->realmID,
'accessTokenExpiresAt' => $this->accessTokenExpiresAt,
'refreshTokenExpiresAt' => $this->refreshTokenExpiresAt,
'baseURL' => $this->baseURL,
'companyName' => $this->companyName,
'settings' => $this->settings->toArray(),
];
}
/**
*
* Patches our settings object with the
* selected changes we authorize.
*
* @param array $changes
* @return self
*/
public function with(array $changes): self
{
$settings = $this->settings->toArray();
$new_settings = [
'client' => [
'direction' => $changes['client']['direction'] ?? $this->settings->client->direction->value,
],
'invoice' => [
'direction' => $changes['invoice']['direction'] ?? $this->settings->invoice->direction->value,
],
'product' => [
'direction' => $changes['product']['direction'] ?? $this->settings->product->direction->value,
],
'qb_income_account_id' => $changes['qb_income_account_id'] ?? $this->settings->qb_income_account_id,
'automatic_taxes' => $changes['automatic_taxes'] ?? $this->settings->automatic_taxes,
];
$final_settings['settings'] = array_merge($settings, $new_settings);
return new self(array_merge($this->toArray(), $final_settings));
}
/**
* Check if this QuickbooksSettings instance represents actual data or is just a default empty object.
*
* @return bool True if this has actual QuickBooks connection data, false if it's just defaults
*/
public function isConfigured(): bool
{
// If accessTokenKey is set, we have a connection
return !empty($this->accessTokenKey);
}
/**
* Check if this QuickbooksSettings instance is empty (default values only).
*
* @return bool True if this is an empty/default instance
*/
public function isEmpty(): bool
{
return empty($this->accessTokenKey)
&& empty($this->refresh_token)
&& empty($this->realmID)
&& $this->accessTokenExpiresAt === 0
&& $this->refreshTokenExpiresAt === 0
&& empty($this->baseURL);
}
}

View File

@@ -5,15 +5,22 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper;
use App\Models\Product;
/**
* QuickbooksSync.
*
* Product type to income account mapping:
* Keys are Product::PRODUCT_TYPE_* constants (int). Values are QuickBooks account IDs (string|null).
* Example: [Product::PRODUCT_TYPE_SERVICE => '123', Product::PRODUCT_TYPE_PHYSICAL => '456']
* Null values indicate the account has not been configured for that product type.
*/
class QuickbooksSync
{
@@ -35,9 +42,25 @@ class QuickbooksSync
public QuickbooksSyncMap $expense;
public string $default_income_account = '';
public QuickbooksSyncMap $expense_category;
public string $default_expense_account = '';
/**
* QuickBooks income account ID per product type.
* Use getAccountId(int $productTypeId) or the typed properties (physical, service, etc.).
*/
public array $income_account_map;
public array $tax_rate_map;
public ?string $qb_income_account_id = null;
public bool $automatic_taxes = false;
public ?string $default_taxable_code = null;
public ?string $default_exempt_code = null;
public ?string $country = null;
public function __construct(array $attributes = [])
{
@@ -50,7 +73,36 @@ class QuickbooksSync
$this->product = new QuickbooksSyncMap($attributes['product'] ?? []);
$this->payment = new QuickbooksSyncMap($attributes['payment'] ?? []);
$this->expense = new QuickbooksSyncMap($attributes['expense'] ?? []);
$this->default_income_account = $attributes['default_income_account'] ?? '';
$this->default_expense_account = $attributes['default_expense_account'] ?? '';
$this->expense_category = new QuickbooksSyncMap($attributes['expense_category'] ?? []);
$this->income_account_map = $attributes['income_account_map'] ?? [];
$this->qb_income_account_id = $attributes['qb_income_account_id'] ?? null;
$this->tax_rate_map = $attributes['tax_rate_map'] ?? [];
$this->automatic_taxes = $attributes['automatic_taxes'] ?? false; //requires us to syncronously push the invoice to QB, and return fully formed Invoice with taxes included.
$this->default_taxable_code = $attributes['default_taxable_code'] ?? null;
$this->default_exempt_code = $attributes['default_exempt_code'] ?? null;
$this->country = $attributes['country'] ?? null;
}
public function toArray(): array
{
return [
'client' => $this->client->toArray(),
'vendor' => $this->vendor->toArray(),
'invoice' => $this->invoice->toArray(),
'sales' => $this->sales->toArray(),
'quote' => $this->quote->toArray(),
'purchase_order' => $this->purchase_order->toArray(),
'product' => $this->product->toArray(),
'payment' => $this->payment->toArray(),
'expense' => $this->expense->toArray(),
'expense_category' => $this->expense_category->toArray(),
'income_account_map' => $this->income_account_map,
'qb_income_account_id' => $this->qb_income_account_id,
'tax_rate_map' => $this->tax_rate_map,
'automatic_taxes' => $this->automatic_taxes,
'default_taxable_code' => $this->default_taxable_code,
'default_exempt_code' => $this->default_exempt_code,
'country' => $this->country,
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -19,13 +19,19 @@ use App\Enum\SyncDirection;
*/
class QuickbooksSyncMap
{
public SyncDirection $direction = SyncDirection::BIDIRECTIONAL;
public SyncDirection $direction = SyncDirection::NONE;
public function __construct(array $attributes = [])
{
$this->direction = isset($attributes['direction'])
? SyncDirection::from($attributes['direction'])
: SyncDirection::BIDIRECTIONAL;
: SyncDirection::NONE;
}
public function toArray(): array
{
return [
'direction' => $this->direction->value,
];
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -20,15 +20,11 @@ use Illuminate\Contracts\Database\Eloquent\Castable;
*/
class QuoteSync implements Castable
{
public string $qb_id;
public function __construct(array $attributes = [])
{
$this->qb_id = $attributes['qb_id'] ?? '';
}
public function __construct(
public string $qb_id = '',
public array $invitations = [],
public bool $dn_completed = false,
){}
/**
* Get the name of the caster class to use when casting from / to this cast target.
*
@@ -41,6 +37,62 @@ class QuoteSync implements Castable
public static function fromArray(array $data): self
{
return new self($data);
return new self(
qb_id: $data['qb_id'] ?? '',
invitations: $data['invitations'] ?? [],
dn_completed: $data['dn_completed'] ?? false,
);
}
/**
* Add an invitation to the invitations array
*
* @param string $invitation_key The invitation key
* @param string $dn_id The DocuNinja ID
* @param string $dn_invitation_id The DocuNinja invitation ID
* @param string $dn_sig The DocuNinja signature
*/
public function addInvitation(
string $invitation_key,
string $dn_id,
string $dn_invitation_id,
string $dn_sig
): void {
$this->invitations[] = [
'invitation_key' => $invitation_key,
'dn_id' => $dn_id,
'dn_invitation_id' => $dn_invitation_id,
'dn_sig' => $dn_sig,
];
}
/**
* Get invitation data by invitation key
*
* @param string $invitation_key The invitation key
* @return array|null The invitation data or null if not found
*/
public function getInvitation(string $invitation_key): ?array
{
foreach ($this->invitations as $invitation) {
if ($invitation['invitation_key'] === $invitation_key) {
return $invitation;
}
}
return null;
}
/**
* Remove an invitation by invitation key
*
* @param string $invitation_key The invitation key
*/
public function removeInvitation(string $invitation_key): void
{
$this->invitations = array_filter($this->invitations, function($invitation) use ($invitation_key) {
return $invitation['invitation_key'] !== $invitation_key;
});
// Re-index the array to maintain numeric keys
$this->invitations = array_values($this->invitations);
}
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -14,7 +14,6 @@ namespace App\DataMapper\Schedule;
class InvoiceOutstandingTasks
{
/**
* Defines the template name
*

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -276,6 +276,10 @@ class SettingsData
public string $email_template_reminder_endless = ''; //@implemented
public string $email_template_payment_failed = ''; //@implemented
public string $email_subject_payment_failed = ''; //@implemented
public string $email_signature = ''; //@implemented
public bool $enable_email_markup = true; //@TODO -
@@ -481,7 +485,7 @@ class SettingsData
try {
settype($object->{$key}, gettype($this->{$key}));
} catch (\Exception | \Error | \Throwable $e) {
} catch (\Exception|\Error|\Throwable $e) {
if (property_exists($this, $key)) {
$object->{$key} = $this->{$key};

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
@@ -106,9 +106,7 @@ class PayPalBalanceAffecting
public $creditTransactionalFee;
public $originalInvoiceId;
public function __construct(private array $import_row)
{
}
public function __construct(private array $import_row) {}
public function run(): self
{
@@ -120,7 +118,7 @@ class PayPalBalanceAffecting
if ($prop) {
echo "Setting {$prop} to {$value}".PHP_EOL;
echo "Setting {$prop} to {$value}" . PHP_EOL;
$this->{$prop} = $value;
}
@@ -133,7 +131,7 @@ class PayPalBalanceAffecting
{
foreach ($this->key_map as $value) {
echo "Setting {$value} to null".PHP_EOL;
echo "Setting {$value} to null" . PHP_EOL;
$this->{$value} = null;
}

View File

@@ -5,7 +5,7 @@
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
* @copyright Copyright (c) 2026. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/

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