Compare commits

..

78 Commits

Author SHA1 Message Date
David Bomba 29bfbaf644
Merge pull request #11185 from turbo124/verifactu
Verifactu
2025-08-15 13:28:44 +10:00
David Bomba 27dd9907f3 Cleanup for tests 2025-08-15 13:26:38 +10:00
David Bomba 5a188ac355 Fixes for tests 2025-08-15 13:22:29 +10:00
David Bomba cfc41eb29a Document validation for verifactu 2025-08-15 13:19:58 +10:00
David Bomba e58f19f593 Validation for verifactu documents 2025-08-15 13:13:51 +10:00
David Bomba e8336c85d7 Update rules for setting Impuesto values 2025-08-15 13:02:59 +10:00
David Bomba 117709e551 Validation checks 2025-08-15 12:26:40 +10:00
David Bomba 70dea557f5 Ensure tax codes that are sent are correct 2025-08-14 14:41:21 +10:00
David Bomba 084f0fea25 Activity translations for verifactu 2025-08-14 12:17:03 +10:00
David Bomba 256555c952 Add AEAT response to logs 2025-08-14 12:08:17 +10:00
David Bomba 9b98d45d3b Align tests with new workflow 2025-08-14 11:28:02 +10:00
David Bomba c3b5a972a1 Align tests with new workflow 2025-08-14 10:47:18 +10:00
David Bomba 7e1a6bc1c7 Align tests with new workflow 2025-08-14 10:38:51 +10:00
David Bomba 0a7744a70e Add IDOtro class 2025-08-14 10:07:32 +10:00
David Bomba 1252cdf7ae Integration of Verifactu with UI 2025-08-14 08:36:55 +10:00
David Bomba af926a394c Wiring up verifactu sending 2025-08-13 14:39:50 +10:00
David Bomba c5c9c4325e Wiring up verifactu sending 2025-08-13 14:26:03 +10:00
David Bomba 3d3b5f6938 Integration works for Verifactu 2025-08-13 13:15:51 +10:00
David Bomba ff92756dbc Verifactu api tests 2025-08-13 11:48:18 +10:00
David Bomba a141ca1549 Refactor tests to remove modifications of existing invoices 2025-08-13 11:39:11 +10:00
David Bomba bf8041ab7c Working on verifactu document mutations 2025-08-13 11:05:02 +10:00
David Bomba 8bc1513591 Wire up AEAT for processing 2025-08-13 10:28:10 +10:00
David Bomba 63e6f75a24 Additional rules around tests 2025-08-12 18:34:38 +10:00
David Bomba 5482f44bea Additional rules around tests 2025-08-12 14:42:32 +10:00
David Bomba 81ec3986ca Tests around the handling of verifactu credit amounts 2025-08-12 14:33:52 +10:00
David Bomba 1a3badf748 Tests around the handling of verifactu credit amounts 2025-08-12 13:52:05 +10:00
David Bomba c7e79fe673 Refactor to use generate parent/child ids 2025-08-12 13:41:11 +10:00
David Bomba 8d23ba14d4 Refactor to use generate parent/child ids 2025-08-12 12:29:36 +10:00
David Bomba 1836ccc434 Update for ivnoice backup casting 2025-08-12 12:13:44 +10:00
David Bomba 94b628b6eb Update for ivnoice backup casting 2025-08-12 11:59:44 +10:00
David Bomba 67df175525 Update for ivnoice backup casting 2025-08-12 10:24:18 +10:00
David Bomba 47f33c8691 Validation rules for modification invoice for Verifactu 2025-08-12 09:17:57 +10:00
David Bomba 6a0fff10ae Tests around handling cancellations in verifactu 2025-08-11 11:37:21 +10:00
David Bomba a447b6a20b Tests around handling cancellations in verifactu 2025-08-11 11:00:30 +10:00
David Bomba f7961ecb61 Additional tests for verifactu restore/archive logic 2025-08-11 10:10:33 +10:00
David Bomba 37c74ee18c Functional tests of spanish environment 2025-08-11 10:09:12 +10:00
David Bomba 555eb80018 Functional tests of spanish environment 2025-08-11 09:54:53 +10:00
David Bomba 1e8727c4a4 Force locking for spanish users 2025-08-11 09:25:48 +10:00
David Bomba 74f71d61d6 Padding settings 2025-08-11 09:14:51 +10:00
David Bomba 8a137329d4 Updates for Verifactu 2025-08-10 15:32:35 +10:00
David Bomba c02c87765b Create and cancel invoices working as expected 2025-08-10 13:03:51 +10:00
David Bomba 7393360db3 Working on feature flow and tests for verifactu submissions 2025-08-10 11:25:36 +10:00
David Bomba f7055b516e Working of feature flow of verifactu 2025-08-09 10:22:14 +10:00
David Bomba ee775e58a0 logging 2025-08-09 09:57:02 +10:00
David Bomba 5ff70dbeae Static analysis cleanup 2025-08-08 15:14:21 +10:00
David Bomba 3791469c31 Static analysis cleanup 2025-08-08 15:12:24 +10:00
David Bomba 1a86d5445b Updates for correct date formats for invoice cancellation tests 2025-08-08 14:07:11 +10:00
David Bomba 442ff42ceb additional tests 2025-08-08 14:04:13 +10:00
David Bomba b94316dbed XmlInterface 2025-08-08 13:17:06 +10:00
David Bomba 14fd4063f5 Verifactu feature tests 2025-08-08 12:43:23 +10:00
David Bomba 97f2e70f5d Expand tests to cancellation and modifications 2025-08-08 12:17:18 +10:00
David Bomba edd0de38ca Fixes for validation tests 2025-08-08 11:26:40 +10:00
David Bomba d53e1012af Add XSD validator for Verifactu 2025-08-08 11:23:03 +10:00
David Bomba 5895c1b0ed Tests for modified invoices 2025-08-08 10:40:38 +10:00
David Bomba aa918f7ec0 Verifactu initial invoice creation 2025-08-08 09:01:31 +10:00
David Bomba 33078ee86c Verifactu invoice generation 2025-08-07 21:56:11 +10:00
David Bomba 6c8c270c2f Verifactu invoice generation 2025-08-07 21:53:13 +10:00
David Bomba 5afd3b85bc additional verifactu validation tests 2025-08-07 21:26:30 +10:00
David Bomba dab787c3ae Validation rules for verifactu invoices 2025-08-07 19:42:18 +10:00
David Bomba 03a39f33b8 Add Entity Level Validation for Verifactu 2025-08-07 19:34:23 +10:00
David Bomba cbc5cb5f9b Skip WSTest by default 2025-08-07 18:45:33 +10:00
David Bomba 4127eb32f9 Add logging to wstest for requirements 2025-08-07 17:12:57 +10:00
David Bomba bf5359cb72 Add VerifactuLog 2025-08-07 15:16:04 +10:00
David Bomba d42735f2ee Tests 2025-08-07 14:36:13 +10:00
David Bomba ea663394b1 Working on ws tests 2025-08-07 14:15:17 +10:00
David Bomba b93ec6dd93 Additional tests for verifactu 2025-08-04 08:09:08 +10:00
David Bomba a431dd43d4 Fixes for verifactu WS 2025-06-26 17:29:48 +10:00
David Bomba 1c5c568251 WS Tests 2025-06-25 11:09:15 +10:00
David Bomba 604ba82f8f WS Tests 2025-06-25 08:52:37 +10:00
David Bomba c8e0cdd090 WS Tests 2025-06-25 08:50:56 +10:00
David Bomba 54ba4349f6 Tests for verifactu 2025-06-22 10:33:26 +10:00
David Bomba 0865ab3c3e Clean up for verifactu tests 2025-04-25 17:08:46 +10:00
David Bomba fc8cb7af36 Clean up for verifactu tests 2025-04-25 17:07:49 +10:00
David Bomba 46de411160 Finish test suite 2025-04-25 17:03:16 +10:00
David Bomba ff50e3c9d9 Finish test suite 2025-04-25 17:02:22 +10:00
David Bomba fdf7d2d3cf Refactor for additional test cases 2025-04-25 16:09:40 +10:00
David Bomba dd60eb3b58 Up to signing xml 2025-04-25 14:52:07 +10:00
David Bomba 1f4fae314c Stubs for generating Verifactu Standard Invoices 2025-04-25 14:03:26 +10:00
2231 changed files with 424988 additions and 480199 deletions

View File

@ -1,3 +1,4 @@
# Invoice Ninja Code of Conduct
The development team has invested a tremendous amount of time and energy into this project. While we appreciate that bugs can be frustrating we ask that our community refrain from insults and snide remarks. We're happy to provide support to both our hosted and selfhosted communities but ask that feedback is always polite.

View File

@ -66,7 +66,7 @@ A good bug report shouldn't leave others needing to chase you up for more inform
#### How Do I Submit a Good Bug Report?
> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <contact@invoiceninja.com>.
> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <>.
We use GitHub issues to track bugs and errors. If you run into an issue with the project:
@ -116,4 +116,4 @@ All PRs should be created against the v5-develop branch.
## Attribution
This guide is based on the **contributing.md**. [Make your own](https://contributing.md/)!
This guide is based on the **contributing.md**. [Make your own](https://contributing.md/)!

View File

@ -179,4 +179,4 @@ For further information on responsible disclosure please read [here](https://che
## License
Invoice Ninja is released under the Elastic License.
See [LICENSE](LICENSE) for details.
See [LICENSE](LICENSE) for details.

View File

@ -1,5 +0,0 @@
## Security
If you find a security issue with this application, please send an email to contact@invoiceninja.com.
Please follow responsible disclosure procedures if you detect an issue.
For further information on responsible disclosure please read [here](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html).

View File

@ -1 +1 @@
5.12.23
5.11.70

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -38,8 +37,8 @@ class ClientSyncCast implements CastsAttributes
public function set($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return [$key => null];
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -0,0 +1,58 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www/elastic.co/licensing/elastic-license
*/
namespace App\Casts;
use App\DataMapper\InvoiceBackup;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class InvoiceBackupCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return new InvoiceBackup();
}
$data = json_decode($value, true) ?? [];
return InvoiceBackup::fromArray($data);
}
public function set($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return [$key => null];
}
// Ensure we're dealing with our object type
if (! $value instanceof InvoiceBackup) {
throw new \InvalidArgumentException('Value must be an InvoiceBackup instance.');
}
return [
$key => json_encode([
'guid' => $value->guid,
'cancellation' => $value->cancellation ? [
'adjustment' => $value->cancellation->adjustment,
'status_id' => $value->cancellation->status_id,
] : [],
'parent_invoice_id' => $value->parent_invoice_id,
'parent_invoice_number' => $value->parent_invoice_number,
'document_type' => $value->document_type,
'child_invoice_ids' => $value->child_invoice_ids->toArray(),
'redirect' => $value->redirect,
'adjustable_amount' => $value->adjustable_amount,
])
];
}
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -19,6 +18,7 @@ class InvoiceSyncCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return null; // Return null if the value is null
}
@ -29,24 +29,28 @@ class InvoiceSyncCast implements CastsAttributes
return null;
}
$is = new InvoiceSync();
$is->qb_id = $data['qb_id'];
$is = new InvoiceSync($data);
return $is;
}
public function set($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return [$key => null];
}
$data = [
'qb_id' => $value->qb_id,
];
if (is_null($value)) {
return [$key => null];
}
return [
$key => json_encode([
'qb_id' => $value->qb_id,
])
];
return [
$key => json_encode($data)
];
}
}

View File

@ -38,7 +38,7 @@ class PaymentSyncCast implements CastsAttributes
public function set($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return [$key => null];
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -39,10 +38,10 @@ class ProductSyncCast implements CastsAttributes
public function set($model, string $key, $value, array $attributes)
{
if (is_null($value)) {
return [$key => null];
}
if (is_null($value)) {
return [$key => null];
}
return [

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

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

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -87,7 +86,7 @@ class CheckData extends Command
/**
* @var string
*/
protected $signature = 'ninja:check-data {--database=} {--fix=} {--portal_url=} {--client_id=} {--vendor_id=} {--paid_to_date=} {--client_balance=} {--ledger_balance=} {--balance_status=} {--bank_transaction=} {--line_items=} {--payment_balance=} {--tasks=}';
protected $signature = 'ninja:check-data {--database=} {--fix=} {--portal_url=} {--client_id=} {--vendor_id=} {--paid_to_date=} {--client_balance=} {--ledger_balance=} {--balance_status=} {--bank_transaction=} {--line_items=} {--payment_balance=}';
/**
* @var string
@ -118,7 +117,6 @@ class CheckData extends Command
config(['database.default' => $database]);
}
$this->checkTaskTimeLogs();
$this->checkInvoiceBalances();
$this->checkClientBalanceEdgeCases();
$this->checkPaidToDatesNew();
@ -181,37 +179,6 @@ class CheckData extends Command
$this->log .= $str."\n";
}
private function checkTaskTimeLogs()
{
\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){
$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]];
}
}
elseif(count($log) == 4){
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'){
$task->time_log = json_encode($time_log);
$task->saveQuietly();
}
});
}
private function checkCompanyTokens()
{
CompanyUser::query()->cursor()->each(function ($cu) {
@ -623,7 +590,7 @@ class CheckData extends Command
private function clientCreditPaymentablesNew($client)
{
$results = \DB::select("
SELECT
SUM(paymentables.amount - paymentables.refunded) as credit_payment
@ -970,14 +937,14 @@ class CheckData extends Command
public function checkClientSettings()
{
if ($this->option('fix') == 'true') {
Client::query()->withTrashed()->whereNull('country_id')->orWhere('country_id', 0)->cursor()->each(function ($client) {
Client::query()->whereNull('country_id')->orWhere('country_id', 0)->cursor()->each(function ($client) {
$client->country_id = $client->company->settings->country_id;
$client->saveQuietly();
$this->logMessage("Fixing country for # {$client->id}");
});
Client::query()->withTrashed()->whereNull("settings->currency_id")->orWhereJsonContains('settings', ['currency_id' => ''])->cursor()->each(function ($client) {
Client::query()->whereNull("settings->currency_id")->orWhereJsonContains('settings', ['currency_id' => ''])->cursor()->each(function ($client) {
$settings = $client->settings;
$settings->currency_id = (string)$client->company->settings->currency_id;
$client->settings = $settings;
@ -987,7 +954,7 @@ class CheckData extends Command
});
Payment::withTrashed()->withTrashed()->where('exchange_rate', 0)->cursor()->each(function ($payment) {
Payment::withTrashed()->where('exchange_rate', 0)->cursor()->each(function ($payment) {
$payment->exchange_rate = 1;
$payment->saveQuietly();
@ -1000,11 +967,11 @@ class CheckData extends Command
})
->cursor()
->each(function ($p) {
$p->currency_id = $p->client->settings->currency_id ?? $p->company->settings->currency_id;
$p->currency_id = $p->client->settings->currency_id;
$p->saveQuietly();
$this->logMessage("Fixing currency for # {$p->id} with new currency {$p->currency_id}");
$this->logMessage("Fixing currency for # {$p->id}");
});
@ -1015,7 +982,7 @@ class CheckData extends Command
$c->subdomain = MultiDB::randomSubdomainGenerator();
$c->save();
$this->logMessage("Fixing subdomain for # {$c->id} with new subdomain {$c->subdomain}");
$this->logMessage("Fixing subdomain for # {$c->id}");
});
@ -1076,7 +1043,7 @@ class CheckData extends Command
public function checkVendorSettings()
{
if ($this->option('fix') == 'true') {
Vendor::query()->withTrashed()->whereNull('currency_id')->orWhere('currency_id', '')->cursor()->each(function ($vendor) {
Vendor::query()->whereNull('currency_id')->orWhere('currency_id', '')->cursor()->each(function ($vendor) {
$vendor->currency_id = $vendor->company->settings->currency_id;
$vendor->saveQuietly();
@ -1174,7 +1141,7 @@ class CheckData extends Command
$bt->invoice_ids = collect($bt->payment->invoices)->pluck('hashed_id')->implode(',');
$bt->save();
$this->logMessage("Fixing - {$bt->id} with new invoice IDs {$bt->invoice_ids}");
$this->logMessage("Fixing - {$bt->id}");
}
@ -1195,7 +1162,7 @@ class CheckData extends Command
$c->send_email = false;
$c->saveQuietly();
$this->logMessage("Fixing - {$c->id} with new send email {$c->send_email}");
$this->logMessage("Fixing - {$c->id}");
});
}
@ -1234,11 +1201,11 @@ class CheckData extends Command
if ($this->option('fix') == 'true') {
$p->cursor()->each(function ($payment) {
$payment->currency_id = $payment->client->settings->currency_id ?? $payment->company->settings->currency_id;
$payment->saveQuietly();
$p->cursor()->each(function ($c) {
$c->currency_id = $c->client->settings->currency_id ?? $c->company->settings->currency_id;
$c->saveQuietly();
$this->logMessage("Fixing payment ID - {$payment->id} with new currency {$payment->currency_id}");
$this->logMessage("Fixing - {$c->id}");
});
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -33,7 +32,6 @@ use App\Models\TaxRate;
use App\Libraries\MultiDB;
use App\Models\TaskStatus;
use App\Models\CompanyToken;
use App\Models\Subscription;
use App\Models\ClientContact;
use App\Models\VendorContact;
use App\Models\CompanyGateway;
@ -144,12 +142,6 @@ class CreateSingleAccount extends Command
'portal_domain' => 'http://ninja.test:8000',
'track_inventory' => true
]);
$custom_fields = new \stdClass();
$custom_fields->client1 = 'CKey|single_line_text';
$custom_fields->client2 = 'AKey|single_line_text';
$company->custom_fields = $custom_fields;
$faker = \Faker\Factory::create();
$settings = $company->settings;
@ -263,11 +255,7 @@ class CreateSingleAccount extends Command
'company_id' => $company->id,
'name' => 'cypress'
]);
$client->custom_value1 = $company->company_key;
$client->custom_value2 = $account->key;
$client->save();
ClientContact::factory()->create([
'user_id' => $user->id,
'client_id' => $client->id,
@ -418,8 +406,8 @@ class CreateSingleAccount extends Command
'company_id' => $company->id,
'product_key' => 'pro_plan',
'notes' => 'The Pro Plan',
'cost' => 12,
'price' => 12,
'cost' => 10,
'price' => 10,
'quantity' => 1,
]);
@ -428,38 +416,8 @@ class CreateSingleAccount extends Command
'company_id' => $company->id,
'product_key' => 'enterprise_plan',
'notes' => 'The Enterprise Plan',
'cost' => 16,
'price' => 16,
'quantity' => 1,
]);
$pe5 = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'enterprise_plan_5',
'notes' => 'The Enterprise Plan 5',
'cost' => 28,
'price' => 28,
'quantity' => 1,
]);
$pe10 = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'enterprise_plan_10',
'notes' => 'The Enterprise Plan 10',
'cost' => 56,
'price' => 56,
'quantity' => 1,
]);
$pe20 = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'enterprise_plan_20',
'notes' => 'The Enterprise Plan 20',
'cost' => 112,
'price' => 112,
'cost' => 14,
'price' => 14,
'quantity' => 1,
]);
@ -473,28 +431,6 @@ class CreateSingleAccount extends Command
'quantity' => 1,
]);
$p4= Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'docuninja_user',
'notes' => 'The DocuNinja Monthly User Plan',
'cost' => 6,
'price' => 6,
'quantity' => 1,
]);
$p5 = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'docuninja_user_annual',
'notes' => 'The DocuNinja Annual User Plan',
'cost' => 60,
'price' => 60,
'quantity' => 1,
]);
$webhook_config = [
'post_purchase_url' => 'http://ninja.test:8000/api/admin/plan',
'post_purchase_rest_method' => 'post',
@ -527,119 +463,8 @@ class CreateSingleAccount extends Command
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$sub->save();
if(!\App\Models\Subscription::find(6)){
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->id = 6;
$sub->name = " PRO Pro Plan";
$sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p1->hashed_id}";
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$sub->save();
}
if (!\App\Models\Subscription::find(11)) {
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->id = 11;
$sub->name = " EEE Enterprise Plan";
$sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p2->hashed_id}";
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$sub->max_seats_limit =2;
$sub->per_seat_enabled = true;
$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->per_seat_enabled = true;
$_sub->save();
$_sub = $sub->replicate();
$_sub->id = 46;
$_sub->name = "Enterprise Plan 6-10 Users";
$_sub->recurring_product_ids = "{$pe10->hashed_id}";
$_sub->max_seats_limit =10;
$_sub->per_seat_enabled = true;
$_sub->save();
$_sub = $sub->replicate();
$_sub->id = 51;
$_sub->name = "Enterprise Plan 11-20 Users";
$_sub->recurring_product_ids = "{$pe20->hashed_id}";
$_sub->max_seats_limit =20;
$_sub->per_seat_enabled = true;
$_sub->save();
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->name = "DocuNinja Monthly Plan";
$sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p4->hashed_id}";
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$sub->save();
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->name = "DocuNinja Annual Plan";
$sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p5->hashed_id}";
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_ANNUALLY;
$sub->save();
if($config = config('admin-api.products')){
foreach($config as $key => $product){
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']
]);
if(!Subscription::find($product['subscription_id'])){
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->id = $product['subscription_id'];
$sub->name = $product['description'];
$sub->recurring_product_ids = "{$p->hashed_id}";
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = $product['term'] == 'month' ? RecurringInvoice::FREQUENCY_MONTHLY : RecurringInvoice::FREQUENCY_ANNUALLY;
$sub->max_seats_limit = $product['users'] ?? 1;
$sub->per_seat_enabled = true;
$sub->save();
}
}
}
}
}
private function createClient($company, $user)
{
// dispatch(function () use ($company, $user) {

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -71,7 +70,7 @@ class DesignUpdate extends Command
private function handleOnDb()
{
foreach (Design::where('is_custom', false)->get() as $design) {
$invoice_design = new \App\Services\Pdf\DesignExtractor($design->name);
$design_object = new stdClass();

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -57,9 +56,8 @@ class S3Cleanup extends Command
$c1 = Company::on('db-ninja-01')->pluck('company_key');
$c2 = Company::on('db-ninja-02')->pluck('company_key');
$c3 = Company::on('db-ninja-03')->pluck('company_key');
$merged = $c1->merge($c2)->merge($c3)->toArray();
$merged = $c1->merge($c2)->toArray();
$directories = Storage::disk(config('filesystems.default'))->directories();

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -89,9 +88,9 @@ class SendRemindersCron extends Command
});
if ($invoice->invitations->count() > 0) {
// event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
event(new \App\Events\General\EntityWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $reminder_template));
// event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
event(new \App\Events\General\EntityWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), $reminder_template));
$invoice->entityEmailEvent($invoice->invitations->first(), $reminder_template);
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -36,7 +35,6 @@ class TranslationsExport extends Command
protected $log = '';
private array $langs = [
'af_ZA',
'ar',
'bg',
'ca',
@ -57,7 +55,6 @@ class TranslationsExport extends Command
'he',
'hr',
'hu',
'id_ID',
'it',
'ja',
'km_KH',

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -34,13 +33,11 @@ use App\Jobs\Ninja\BankTransactionSync;
use App\Jobs\Cron\RecurringExpensesCron;
use App\Jobs\Cron\RecurringInvoicesCron;
use App\Jobs\EDocument\EInvoicePullDocs;
use App\Jobs\Cron\InvoiceTaxSummary;
use Illuminate\Console\Scheduling\Schedule;
use App\Jobs\Invoice\InvoiceCheckLateWebhook;
use App\Jobs\Subscription\CleanStaleInvoiceOrder;
use App\PaymentDrivers\Rotessa\Jobs\TransactionReport;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use App\Console\Commands\CreateElasticIndex;
class Kernel extends ConsoleKernel
{
@ -73,29 +70,6 @@ class Kernel extends ConsoleKernel
/* Checks for scheduled tasks */
$schedule->job(new TaskScheduler())->hourlyAt(10)->withoutOverlapping()->name('task-scheduler-job')->onOneServer();
// Run hourly over 26-hour period for complete timezone coverage
$schedule->job(new InvoiceTaxSummary())
->hourly()
->when(function () {
$now = now();
$hour = $now->hour;
// Run for 26 hours starting from UTC 10:00 on last day of month
// This covers the transition period when timezones move to next month
if ($now->isSameDay($now->copy()->endOfMonth())) {
// Start at UTC 10:00 (when UTC+14 moves to next day)
return $hour >= 10;
} elseif ($now->isSameDay($now->copy()->startOfMonth())) {
// Continue until UTC 12:00 (when UTC-12 moves to next day)
return $hour <= 12;
}
return false;
})
->withoutOverlapping()
->name('invoice-tax-summary-26hour-coverage')
->onOneServer();
/* Checks Rotessa Transactions */
$schedule->job(new TransactionReport())->dailyAt('01:48')->withoutOverlapping()->name('rotessa-transaction-report')->onOneServer();
@ -157,8 +131,6 @@ class Kernel extends ConsoleKernel
$schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('02:20')->withoutOverlapping()->name('check-data-db-2-job')->onOneServer();
$schedule->command('ninja:check-data --database=db-ninja-03')->dailyAt('02:30')->withoutOverlapping()->name('check-data-db-2-job')->onOneServer();
$schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping()->name('s3-cleanup-job')->onOneServer();
}
@ -180,5 +152,4 @@ class Kernel extends ConsoleKernel
require base_path('routes/console.php');
}
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -76,10 +75,9 @@ class EmailSuccess extends GenericMixedMetric
*/
public $string_metric8 = '';
public function __construct($string_metric7 = '', $string_metric8 = '', string $string_metric9 = '')
public function __construct($string_metric7 = '', $string_metric8 = '')
{
$this->string_metric7 = $string_metric7;
$this->string_metric8 = $string_metric8;
$this->string_metric9 = $string_metric9;
}
}

View File

@ -1,60 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class JobFailureAnalytics extends GenericMixedMetric
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'mixed_metric';
/**
* The name of the counter.
* @var string
*/
public $name = 'job.failed';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
*/
public $datetime;
/**
* The Class failure name
* set to 0.
*
* @var string
*/
public $string_metric5 = 'name';
public $string_metric6 = 'exception';
public $int_metric1 = 1;
public function __construct($string_metric5, $string_metric6)
{
$this->string_metric5 = $string_metric5;
$this->string_metric6 = $string_metric6;
}
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -0,0 +1,32 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www/elastic.co/licensing/elastic-license
*/
namespace App\DataMapper;
/**
* Cancellation value object for invoice backup data.
*/
class Cancellation
{
public function __construct(
public float $adjustment = 0, // The cancellation adjustment amount
public int $status_id = 0 //The status id of the invoice when it was cancelled
) {}
public static function fromArray(array $data): self
{
return new self(
adjustment: $data['adjustment'] ?? 0,
status_id: $data['status_id'] ?? 0
);
}
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -12,9 +11,8 @@
namespace App\DataMapper;
use stdClass;
use App\Utils\Ninja;
use App\Utils\Traits\MakesHash;
use stdClass;
/**
* CompanySettings.
@ -231,7 +229,7 @@ class CompanySettings extends BaseSettings
public $require_quote_signature = false; //@TODO ben to confirm
//email settings
public $email_sending_method = 'default'; //enum 'default','gmail','office365' 'client_postmark', 'client_mailgun', 'mailgun', 'client_brevo', 'client_ses', 'ses' //@implemented
public $email_sending_method = 'default'; //enum 'default','gmail','office365' 'client_postmark', 'client_mailgun', 'mailgun', 'client_brevo' //@implemented
public $gmail_sending_user_id = '0'; //@implemented
@ -479,7 +477,7 @@ class CompanySettings extends BaseSettings
public $sync_invoice_quote_columns = true;
public $e_invoice_type = 'EN16931';
public $e_invoice_type = 'EN16931'; //verifactu
public $e_quote_type = 'OrderX_Comfort';
@ -530,18 +528,7 @@ class CompanySettings extends BaseSettings
public bool $unlock_invoice_documents_after_payment = false;
public string $ses_secret_key = '';
public string $ses_access_key = '';
public string $ses_region = '';
public string $ses_topic_arn = '';
public string $ses_from_address = '';
public static $casts = [
'ses_from_address' => 'string',
'ses_topic_arn' => 'string',
'ses_secret_key' => 'string',
'ses_access_key' => 'string',
'ses_region' => 'string',
'unlock_invoice_documents_after_payment' => 'bool',
'preference_product_notes_for_html_view' => 'bool',
'enable_client_profile_update' => 'bool',
@ -943,10 +930,7 @@ class CompanySettings extends BaseSettings
{
$notification = new stdClass();
$notification->email = [];
if(Ninja::isSelfHost()) {
$notification->email = ['invoice_sent_all', 'payment_success_all', 'payment_manual_all'];
}
$notification->email = ['invoice_sent_all', 'payment_success_all', 'payment_manual_all'];
return $notification;
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -0,0 +1,92 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www/elastic.co/licensing/elastic-license
*/
namespace App\DataMapper;
use App\Casts\InvoiceBackupCast;
use App\DataMapper\Cancellation;
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Support\Collection;
/**
* InvoiceBackup.
*/
class InvoiceBackup implements Castable
{
public function __construct(
public string $guid = '', // The E-INVOICE SENT GUID reference - or enum to advise the document has been successfully sent.
public Cancellation $cancellation = new Cancellation(0,0),
public ?string $parent_invoice_id = null, // The id of the invoice that was cancelled
public ?string $parent_invoice_number = null, // The number of the invoice that was cancelled
public ?string $document_type = null, // F1, R2
public Collection $child_invoice_ids = new Collection(), // Collection of child invoice IDs
public ?string $redirect = null, // The redirect url for the invoice
public float $adjustable_amount = 0,
) {}
/**
* 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 InvoiceBackupCast::class;
}
public static function fromArray(array $data): self
{
return new self(
guid: $data['guid'] ?? '',
cancellation: Cancellation::fromArray($data['cancellation'] ?? []),
parent_invoice_id: $data['parent_invoice_id'] ?? null,
parent_invoice_number: $data['parent_invoice_number'] ?? null,
document_type: $data['document_type'] ?? null,
child_invoice_ids: isset($data['child_invoice_ids']) ? collect($data['child_invoice_ids']) : new Collection(),
redirect: $data['redirect'] ?? null,
adjustable_amount: $data['adjustable_amount'] ?? 0,
);
}
/**
* Add a child invoice ID to the collection
*/
public function addChildInvoiceId(string $invoiceId): void
{
$this->child_invoice_ids->push($invoiceId);
}
/**
* Remove a child invoice ID from the collection
*/
public function removeChildInvoiceId(string $invoiceId): void
{
$this->child_invoice_ids = $this->child_invoice_ids->reject($invoiceId);
}
/**
* Check if a child invoice ID exists
*/
public function hasChildInvoiceId(string $invoiceId): bool
{
return $this->child_invoice_ids->contains($invoiceId);
}
/**
* Get all child invoice IDs as an array
*/
public function getChildInvoiceIds(): array
{
return $this->child_invoice_ids->toArray();
}
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -13,7 +12,6 @@
namespace App\DataMapper;
use App\Casts\InvoiceSyncCast;
use App\DataMapper\TaxReport\TaxReport;
use Illuminate\Contracts\Database\Eloquent\Castable;
/**
@ -25,7 +23,9 @@ class InvoiceSync implements Castable
public function __construct(array $attributes = [])
{
$this->qb_id = $attributes['qb_id'] ?? '';
}
/**

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -18,9 +18,9 @@ class ReferralEarning
public string $version = 'alpha';
public string $referral_start_date = ''; // The date this referral was registered.
public string $qualifies_after = ''; // The date the payout qualifies after (5 months / 1 year)
public string $period_ending = ''; // The Date this set relates to. ie 2024-07-31 = July 2024
public string $account_key = '';

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,57 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Schedule;
class InvoiceOutstandingTasks
{
/**
* Defines the template name
*
* @var string
*/
public string $template = 'invoice_outstanding_tasks';
/**
* The date range the report should include
*
* @var string
*/
public string $date_range = 'this_month';
/**
* An array of clients hashed_ids
*
* Leave blank if this action should apply to all clients
*
* @var array
*/
public array $clients = [];
/**
* If true, the invoice will be auto-sent
* else it will be generated and kept in a draft state
*
* @var bool
*/
public bool $auto_send = false;
/**
* If true, the project tasks will be included in the report
*
* @var bool
*/
public bool $include_project_tasks = false;
}

View File

@ -1,38 +0,0 @@
<?php
namespace App\DataMapper\Schedule;
class PaymentSchedule
{
/**
* The template name
*
* @var string
*/
public string $template = 'payment_schedule';
/**
*
* @var array(
* 'id' => int,
* 'date' => string,
* 'amount' => float,
* 'is_amount' => bool
* )
*/
public array $schedule = [];
/**
* The invoice id
*
* @var string
*/
public string $invoice_id = '';
/**
* Whether to auto bill the invoice
*
* @var bool
*/
public bool $auto_bill = false;
}

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
@ -112,7 +111,7 @@ class BaseRule implements RuleInterface
];
/** EU TAXES */
/** Supported E Delivery Countries */
public array $peppol_business_countries = [
'AT',
@ -253,10 +252,10 @@ class BaseRule implements RuleInterface
$tax_data = $company->origin_tax_data;
} elseif ($this->invoice->location && $this->invoice->location->is_shipping_location && $this->invoice->location->tax_data) {
} elseif($this->invoice->location && $this->invoice->location->is_shipping_location && $this->invoice->location->tax_data){
$tax_data = $this->invoice->location->tax_data;
} elseif ($this->client->tax_data) {

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

View File

@ -1,5 +1,4 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*

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