commit
328d0d7482
|
|
@ -30,6 +30,11 @@ jobs:
|
|||
id: set_date
|
||||
run: echo "current_date=$(date '+%Y-%m-%d')" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Prepare React FrontEnd
|
||||
run: |
|
||||
git clone https://${{secrets.commit_secret}}@github.com/invoiceninja/ui.git
|
||||
|
|
@ -58,14 +63,14 @@ jobs:
|
|||
rm .env || true
|
||||
rm -rf ui || true
|
||||
# Set permissions: directories 755, files 644
|
||||
chmod -R a=r,u+w,a+X .
|
||||
find . -path ./vendor -prune -o -type f -exec chmod 644 {} \+
|
||||
find . -path ./vendor -prune -o -type d -exec chmod 755 {} \+
|
||||
|
||||
- name: Build project
|
||||
run: |
|
||||
shopt -s dotglob
|
||||
tar --exclude='public/storage' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar *
|
||||
cd ../
|
||||
tar --exclude='public/storage' --exclude='invoiceninja.zip' -zcvf invoiceninja.tar.gz invoiceninja/
|
||||
tar --exclude='public/storage' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar.gz *
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
# 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <>.
|
||||
> 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>.
|
||||
|
||||
|
||||
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/)!
|
||||
|
|
@ -63,11 +63,13 @@ All Pro and Enterprise features from the hosted app are included in the source-a
|
|||
In addition to the official [Invoice Ninja - Self-Hosted Installation Guide](https://invoiceninja.github.io/en/self-host-installation/) we have a few commands for you.
|
||||
|
||||
```sh
|
||||
git clone --single-branch --branch v5-stable https://github.com/invoiceninja/invoiceninja.git
|
||||
git clone --depth 1 -b v5.11.53 https://github.com/invoiceninja/invoiceninja.git
|
||||
cp .env.example .env
|
||||
composer i -o --no-dev
|
||||
```
|
||||
|
||||
**Note** replace v5.11.53 with the latest tag version, you will also want to ensure that when performing updates, you use the latest tag version rather than a particular branch, ie v5-develop. This will ensure that you are not pulling in work in progress code.
|
||||
|
||||
Please Note:
|
||||
Your APP_KEY in the .env file is used to encrypt data, if you lose this you will not be able to run the application.
|
||||
|
||||
|
|
@ -177,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.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
## 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).
|
||||
|
|
@ -1 +1 @@
|
|||
5.11.43
|
||||
5.12.23
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -37,8 +38,8 @@ class ClientSyncCast implements CastsAttributes
|
|||
|
||||
public function set($model, string $key, $value, array $attributes)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
if (is_null($value)) {
|
||||
return [$key => null];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -18,7 +19,6 @@ 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,28 +29,24 @@ class InvoiceSyncCast implements CastsAttributes
|
|||
return null;
|
||||
}
|
||||
|
||||
$is = new InvoiceSync($data);
|
||||
|
||||
$is = new InvoiceSync();
|
||||
$is->qb_id = $data['qb_id'];
|
||||
|
||||
return $is;
|
||||
}
|
||||
|
||||
public function set($model, string $key, $value, array $attributes)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return [$key => null];
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (is_null($value)) {
|
||||
return [$key => null];
|
||||
}
|
||||
|
||||
|
||||
|
||||
return [
|
||||
$key => json_encode([
|
||||
'qb_id' => $value->qb_id,
|
||||
])
|
||||
];
|
||||
|
||||
$data = [
|
||||
'qb_id' => $value->qb_id,
|
||||
];
|
||||
|
||||
return [
|
||||
$key => json_encode($data)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class PaymentSyncCast implements CastsAttributes
|
|||
|
||||
public function set($model, string $key, $value, array $attributes)
|
||||
{
|
||||
|
||||
|
||||
if (is_null($value)) {
|
||||
return [$key => null];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -38,10 +39,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 [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<?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\QuoteSync;
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$is = new QuoteSync($data);
|
||||
|
||||
return $is;
|
||||
}
|
||||
|
||||
public function set($model, string $key, $value, array $attributes)
|
||||
{
|
||||
|
||||
if (is_null($value)) {
|
||||
return [$key => null];
|
||||
}
|
||||
|
||||
return [
|
||||
$key => json_encode([
|
||||
'qb_id' => $value->qb_id,
|
||||
])
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?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())
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -86,7 +87,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=}';
|
||||
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=}';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
|
@ -117,6 +118,7 @@ class CheckData extends Command
|
|||
config(['database.default' => $database]);
|
||||
}
|
||||
|
||||
$this->checkTaskTimeLogs();
|
||||
$this->checkInvoiceBalances();
|
||||
$this->checkClientBalanceEdgeCases();
|
||||
$this->checkPaidToDatesNew();
|
||||
|
|
@ -179,6 +181,37 @@ 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) {
|
||||
|
|
@ -568,6 +601,7 @@ class CheckData extends Command
|
|||
});
|
||||
}
|
||||
|
||||
//@deprecated
|
||||
private function clientCreditPaymentables($client)
|
||||
{
|
||||
$results = \DB::select("
|
||||
|
|
@ -587,6 +621,28 @@ class CheckData extends Command
|
|||
return $results;
|
||||
}
|
||||
|
||||
private function clientCreditPaymentablesNew($client)
|
||||
{
|
||||
|
||||
$results = \DB::select("
|
||||
SELECT
|
||||
SUM(paymentables.amount - paymentables.refunded) as credit_payment
|
||||
FROM payments
|
||||
LEFT JOIN paymentables
|
||||
ON
|
||||
payments.id = paymentables.payment_id
|
||||
WHERE paymentable_type = 'invoices'
|
||||
AND paymentables.deleted_at is NULL
|
||||
AND paymentables.amount > 0
|
||||
AND payments.is_deleted = 0
|
||||
AND payments.client_id = ?;
|
||||
", [$client->id]);
|
||||
|
||||
return $results;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function checkPaidToDatesNew()
|
||||
{
|
||||
$clients_to_check = $this->clientPaidToDateQuery();
|
||||
|
|
@ -599,9 +655,12 @@ class CheckData extends Command
|
|||
$credits_from_reversal = Credit::withTrashed()->where('client_id', $client->id)->where('is_deleted', 0)->whereNotNull('invoice_id')->sum('amount');
|
||||
|
||||
$credits_used_for_payments = $this->clientCreditPaymentables($client);
|
||||
|
||||
$total_paid_to_date = $_client->payments_applied + $credits_used_for_payments[0]->credit_payment - $credits_from_reversal;
|
||||
|
||||
//2025-03-06 - new method
|
||||
// $credits_used_for_payments = $this->clientCreditPaymentablesNew($client);
|
||||
// $total_paid_to_date = $credits_used_for_payments[0]->credit_payment;
|
||||
|
||||
if (round($total_paid_to_date, 2) != round($_client->client_paid_to_date, 2)) {
|
||||
$this->wrong_paid_to_dates++;
|
||||
|
||||
|
|
@ -911,14 +970,14 @@ class CheckData extends Command
|
|||
public function checkClientSettings()
|
||||
{
|
||||
if ($this->option('fix') == 'true') {
|
||||
Client::query()->whereNull('country_id')->orWhere('country_id', 0)->cursor()->each(function ($client) {
|
||||
Client::query()->withTrashed()->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()->whereNull("settings->currency_id")->orWhereJsonContains('settings', ['currency_id' => ''])->cursor()->each(function ($client) {
|
||||
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;
|
||||
$client->settings = $settings;
|
||||
|
|
@ -928,7 +987,7 @@ class CheckData extends Command
|
|||
|
||||
});
|
||||
|
||||
Payment::withTrashed()->where('exchange_rate', 0)->cursor()->each(function ($payment) {
|
||||
Payment::withTrashed()->withTrashed()->where('exchange_rate', 0)->cursor()->each(function ($payment) {
|
||||
$payment->exchange_rate = 1;
|
||||
$payment->saveQuietly();
|
||||
|
||||
|
|
@ -941,11 +1000,11 @@ class CheckData extends Command
|
|||
})
|
||||
->cursor()
|
||||
->each(function ($p) {
|
||||
$p->currency_id = $p->client->settings->currency_id;
|
||||
$p->currency_id = $p->client->settings->currency_id ?? $p->company->settings->currency_id;
|
||||
$p->saveQuietly();
|
||||
|
||||
|
||||
$this->logMessage("Fixing currency for # {$p->id}");
|
||||
$this->logMessage("Fixing currency for # {$p->id} with new currency {$p->currency_id}");
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -956,7 +1015,7 @@ class CheckData extends Command
|
|||
$c->subdomain = MultiDB::randomSubdomainGenerator();
|
||||
$c->save();
|
||||
|
||||
$this->logMessage("Fixing subdomain for # {$c->id}");
|
||||
$this->logMessage("Fixing subdomain for # {$c->id} with new subdomain {$c->subdomain}");
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -1017,7 +1076,7 @@ class CheckData extends Command
|
|||
public function checkVendorSettings()
|
||||
{
|
||||
if ($this->option('fix') == 'true') {
|
||||
Vendor::query()->whereNull('currency_id')->orWhere('currency_id', '')->cursor()->each(function ($vendor) {
|
||||
Vendor::query()->withTrashed()->whereNull('currency_id')->orWhere('currency_id', '')->cursor()->each(function ($vendor) {
|
||||
$vendor->currency_id = $vendor->company->settings->currency_id;
|
||||
$vendor->saveQuietly();
|
||||
|
||||
|
|
@ -1115,7 +1174,7 @@ class CheckData extends Command
|
|||
$bt->invoice_ids = collect($bt->payment->invoices)->pluck('hashed_id')->implode(',');
|
||||
$bt->save();
|
||||
|
||||
$this->logMessage("Fixing - {$bt->id}");
|
||||
$this->logMessage("Fixing - {$bt->id} with new invoice IDs {$bt->invoice_ids}");
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1136,7 +1195,7 @@ class CheckData extends Command
|
|||
$c->send_email = false;
|
||||
$c->saveQuietly();
|
||||
|
||||
$this->logMessage("Fixing - {$c->id}");
|
||||
$this->logMessage("Fixing - {$c->id} with new send email {$c->send_email}");
|
||||
|
||||
});
|
||||
}
|
||||
|
|
@ -1175,11 +1234,11 @@ class CheckData extends Command
|
|||
|
||||
if ($this->option('fix') == 'true') {
|
||||
|
||||
$p->cursor()->each(function ($c) {
|
||||
$c->currency_id = $c->client->settings->currency_id ?? $c->company->settings->currency_id;
|
||||
$c->saveQuietly();
|
||||
$p->cursor()->each(function ($payment) {
|
||||
$payment->currency_id = $payment->client->settings->currency_id ?? $payment->company->settings->currency_id;
|
||||
$payment->saveQuietly();
|
||||
|
||||
$this->logMessage("Fixing - {$c->id}");
|
||||
$this->logMessage("Fixing payment ID - {$payment->id} with new currency {$payment->currency_id}");
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -32,6 +33,7 @@ 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;
|
||||
|
|
@ -142,6 +144,12 @@ 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;
|
||||
|
|
@ -255,7 +263,11 @@ 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,
|
||||
|
|
@ -406,8 +418,8 @@ class CreateSingleAccount extends Command
|
|||
'company_id' => $company->id,
|
||||
'product_key' => 'pro_plan',
|
||||
'notes' => 'The Pro Plan',
|
||||
'cost' => 10,
|
||||
'price' => 10,
|
||||
'cost' => 12,
|
||||
'price' => 12,
|
||||
'quantity' => 1,
|
||||
]);
|
||||
|
||||
|
|
@ -416,8 +428,38 @@ class CreateSingleAccount extends Command
|
|||
'company_id' => $company->id,
|
||||
'product_key' => 'enterprise_plan',
|
||||
'notes' => 'The Enterprise Plan',
|
||||
'cost' => 14,
|
||||
'price' => 14,
|
||||
'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,
|
||||
'quantity' => 1,
|
||||
]);
|
||||
|
||||
|
|
@ -431,6 +473,28 @@ 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',
|
||||
|
|
@ -463,8 +527,119 @@ 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) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -70,7 +71,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();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -56,8 +57,9 @@ 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)->toArray();
|
||||
$merged = $c1->merge($c2)->merge($c3)->toArray();
|
||||
|
||||
$directories = Storage::disk(config('filesystems.default'))->directories();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -88,9 +89,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -35,6 +36,7 @@ class TranslationsExport extends Command
|
|||
protected $log = '';
|
||||
|
||||
private array $langs = [
|
||||
'af_ZA',
|
||||
'ar',
|
||||
'bg',
|
||||
'ca',
|
||||
|
|
@ -55,6 +57,7 @@ class TranslationsExport extends Command
|
|||
'he',
|
||||
'hr',
|
||||
'hu',
|
||||
'id_ID',
|
||||
'it',
|
||||
'ja',
|
||||
'km_KH',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -33,11 +34,13 @@ 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
|
||||
{
|
||||
|
|
@ -70,6 +73,29 @@ 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();
|
||||
|
||||
|
|
@ -131,6 +157,8 @@ 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();
|
||||
}
|
||||
|
||||
|
|
@ -152,4 +180,5 @@ class Kernel extends ConsoleKernel
|
|||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -73,7 +74,7 @@ class DbQuery extends GenericMixedMetric
|
|||
$this->string_metric6 = $string_metric6;
|
||||
$this->double_metric2 = $double_metric2;
|
||||
$this->string_metric7 = $string_metric7;
|
||||
$this->string_metric8 = mb_convert_encoding($string_metric8, "UTF-8");
|
||||
$this->string_metric9 = mb_convert_encoding($string_metric9, "UTF-8");
|
||||
$this->string_metric8 = mb_convert_encoding($string_metric8 ?? '', "UTF-8");
|
||||
$this->string_metric9 = mb_convert_encoding($string_metric9 ?? '', "UTF-8");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -75,9 +76,10 @@ class EmailSuccess extends GenericMixedMetric
|
|||
*/
|
||||
public $string_metric8 = '';
|
||||
|
||||
public function __construct($string_metric7 = '', $string_metric8 = '')
|
||||
public function __construct($string_metric7 = '', $string_metric8 = '', string $string_metric9 = '')
|
||||
{
|
||||
$this->string_metric7 = $string_metric7;
|
||||
$this->string_metric8 = $string_metric8;
|
||||
$this->string_metric9 = $string_metric9;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -11,8 +12,9 @@
|
|||
|
||||
namespace App\DataMapper;
|
||||
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use stdClass;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
/**
|
||||
* CompanySettings.
|
||||
|
|
@ -229,7 +231,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' //@implemented
|
||||
public $email_sending_method = 'default'; //enum 'default','gmail','office365' 'client_postmark', 'client_mailgun', 'mailgun', 'client_brevo', 'client_ses', 'ses' //@implemented
|
||||
|
||||
public $gmail_sending_user_id = '0'; //@implemented
|
||||
|
||||
|
|
@ -528,7 +530,18 @@ 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',
|
||||
|
|
@ -930,7 +943,10 @@ class CompanySettings extends BaseSettings
|
|||
{
|
||||
$notification = new stdClass();
|
||||
$notification->email = [];
|
||||
$notification->email = ['invoice_sent_all', 'payment_success_all', 'payment_manual_all'];
|
||||
|
||||
if(Ninja::isSelfHost()) {
|
||||
$notification->email = ['invoice_sent_all', 'payment_success_all', 'payment_manual_all'];
|
||||
}
|
||||
|
||||
return $notification;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -12,6 +13,7 @@
|
|||
namespace App\DataMapper;
|
||||
|
||||
use App\Casts\InvoiceSyncCast;
|
||||
use App\DataMapper\TaxReport\TaxReport;
|
||||
use Illuminate\Contracts\Database\Eloquent\Castable;
|
||||
|
||||
/**
|
||||
|
|
@ -23,9 +25,7 @@ class InvoiceSync implements Castable
|
|||
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
|
||||
$this->qb_id = $attributes['qb_id'] ?? '';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
<?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\QuoteSyncCast;
|
||||
use Illuminate\Contracts\Database\Eloquent\Castable;
|
||||
|
||||
/**
|
||||
* QuoteSync.
|
||||
*/
|
||||
class QuoteSync 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 QuoteSyncCast::class;
|
||||
}
|
||||
|
||||
public static function fromArray(array $data): self
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = '';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
<?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;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?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;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
@ -111,7 +112,7 @@ class BaseRule implements RuleInterface
|
|||
];
|
||||
|
||||
/** EU TAXES */
|
||||
|
||||
|
||||
/** Supported E Delivery Countries */
|
||||
public array $peppol_business_countries = [
|
||||
'AT',
|
||||
|
|
@ -252,10 +253,10 @@ class BaseRule implements RuleInterface
|
|||
|
||||
$tax_data = $company->origin_tax_data;
|
||||
|
||||
} elseif($this->client->location && $this->client->location->is_shipping_location && $this->client->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;
|
||||
|
||||
$tax_data = $this->client->location->tax_data;
|
||||
|
||||
} elseif ($this->client->tax_data) {
|
||||
|
||||
|
||||
|
|
@ -334,6 +335,7 @@ class BaseRule implements RuleInterface
|
|||
|
||||
public function defaultForeign(): self
|
||||
{
|
||||
// nlog("default foreign");
|
||||
if ($this->invoice->client->is_tax_exempt) {
|
||||
|
||||
$this->tax_rate1 = 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue