Merge branch 'v5-stable' into patch-4

This commit is contained in:
David Bomba 2022-12-07 19:44:45 +11:00 committed by GitHub
commit 5d5cc1eec2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
730 changed files with 636414 additions and 391009 deletions

View File

@ -57,7 +57,6 @@ DELETE_PDF_DAYS=60
DELETE_BACKUP_DAYS=60
COMPOSER_AUTH='{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}'
SENTRY_LARAVEL_DSN=https://39389664f3f14969b4c43dadda00a40b@sentry2.invoicing.co/5
GOOGLE_PLAY_PACKAGE_NAME=
APPSTORE_PASSWORD=

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
/public/react
/storage/*.key
/storage/debugbar
/storage/*
/vendor
/.idea
/.vscode

View File

@ -3,7 +3,6 @@
</p>
![v5-develop phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-develop)
![v5-stable phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-stable)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/d16c78aad8574466bf83232b513ef4fb)](https://www.codacy.com/gh/turbo124/invoiceninja/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=turbo124/invoiceninja&amp;utm_campaign=Badge_Grade)
<a href="https://cla-assistant.io/invoiceninja/invoiceninja"><img src="https://cla-assistant.io/readme/badge/invoiceninja/invoiceninja" alt="CLA assistant" /></a>
@ -19,18 +18,33 @@ Just make sure to add the `invoice-ninja` tag to your question.
Version 5 of Invoice Ninja is here! We've taken the best parts of version 4 and bolted on all of the most requested features to produce a invoicing application like no other.
The new interface has a lot more functionality so it isn't a carbon copy of v4, but once you get used to the new layout and functionality we are sure you will love it!
All Pro and Enterprise features from the hosted app are included in the open-code. We offer a $30 per year white-label license to remove the Invoice Ninja branding from client facing parts of the app.
## Referral Program
* Earn 50% of Pro & Enterprise Plans up to 4 years - [Learn more](https://www.invoiceninja.com/referral-program/)
* [Videos](https://www.youtube.com/@appinvoiceninja)
* [API Documentation](https://app.swaggerhub.com/apis/invoiceninja/invoiceninja)
* [APP Documentation](https://invoiceninja.github.io/)
* [Support Forum](https://forum.invoiceninja.com)
* [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/)
## Mobile Apps
* [iPhone](https://apps.apple.com/app/id1503970375?platform=iphone)
* [Android](https://play.google.com/store/apps/details?id=com.invoiceninja.app)
## Desktop Apps
* [macOS](https://apps.apple.com/app/id1503970375?platform=mac)
* [Windows](https://microsoft.com/en-us/p/invoice-ninja/9n3f2bbcfdr6)
* [Linux](https://snapcraft.io/invoiceninja)
## Installation Options
* [Docker File](https://hub.docker.com/r/invoiceninja/invoiceninja/)
* [Cloudron](https://cloudron.io/store/com.invoiceninja.cloudronapp.html)
* [Softaculous](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja)
## Recommended Providers
* [Stripe](https://stripe.com/)
* [Postmark](https://postmarkapp.com/)
## Development
* [API Documentation](https://app.swaggerhub.com/apis/invoiceninja/invoiceninja)
* [APP Documentation](https://invoiceninja.github.io/)
## Quick Start
@ -68,43 +82,11 @@ user: user@example.com
pass: password
```
## Contribution guide.
Code Style to follow [PSR-2](https://www.php-fig.org/psr/psr-2/) standards.
All methods names to be in CamelCase
All variables names to be in snake_case
Where practical code should be strongly typed, ie your methods must return a type ie
`public function doThis() : void`
PHP >= 7.3 allows the return type Nullable so there should be no circumstance a type cannot be return by using the following:
`public function doThat() ?:string`
To improve chances of PRs being merged please include tests to ensure your code works well and integrates with the rest of the project.
## Documentation
API documentation is hosted using Swagger and can be found [HERE](https://app.swaggerhub.com/apis/invoiceninja/invoiceninja)
Installation, Configuration and Troubleshooting documentation can be found [HERE] (https://invoiceninja.github.io)
## Credits
* [Hillel Coren](https://hillelcoren.com/)
* [David Bomba](https://github.com/turbo124)
* [All contributors](https://github.com/invoiceninja/invoiceninja/graphs/contributors)
**Special thanks to:**
* [Holger Lösken](https://github.com/codedge) - [codedge](http://codedge.de)
* [Samuel Laulhau](https://github.com/lalop) - [Lalop](http://lalop.co/)
* [Alexander Vanderveen](https://blog.technicallycomputers.ca/) - [Technically Computers](https://www.technicallycomputers.ca/)
* [Efthymios Sarmpanis](https://github.com/esarbanis)
* [Gianfranco Gasbarri](https://github.com/gincos)
* [Clemens Mol](https://github.com/clemensmol)
* [Benjamin Beganović](https://github.com/beganovich)
* [All contributors](https://github.com/invoiceninja/invoiceninja/graphs/contributors)
## Security

View File

@ -1 +1 @@
5.5.25
5.5.47

View File

@ -13,7 +13,9 @@ namespace App\Console\Commands;
use App\Libraries\MultiDB;
use App\Models\Backup;
use App\Models\Company;
use App\Models\Design;
use App\Models\Document;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use stdClass;
@ -25,14 +27,14 @@ class BackupUpdate extends Command
*
* @var string
*/
protected $signature = 'ninja:backup-update';
protected $signature = 'ninja:backup-files {--disk=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Shift backups from DB to storage';
protected $description = 'Shift files between object storage locations';
/**
* Create a new command instance.
@ -74,17 +76,52 @@ class BackupUpdate extends Command
{
set_time_limit(0);
Backup::whereHas('activity')->whereRaw('html_backup IS NOT NULL')->cursor()->each(function ($backup) {
if (strlen($backup->html_backup) > 1 && $backup->activity->invoice->exists()) {
$client = $backup->activity->invoice->client;
$backup->storeRemotely($backup->html_backup, $client);
} elseif (strlen($backup->html_backup) > 1 && $backup->activity->quote->exists()) {
$client = $backup->activity->quote->client;
$backup->storeRemotely($backup->html_backup, $client);
} elseif (strlen($backup->html_backup) > 1 && $backup->activity->credit->exists()) {
$client = $backup->activity->credit->client;
$backup->storeRemotely($backup->html_backup, $client);
}
});
//logos
Company::cursor()
->each(function ($company){
$company_logo = $company->present()->logo();
if($company_logo == 'https://invoicing.co/images/new_logo.png')
return;
$logo = @file_get_contents($company_logo);
if($logo){
$path = str_replace("https://objects.invoicing.co/", "", $company->present()->logo());
$path = str_replace("https://v5-at-backup.us-southeast-1.linodeobjects.com/", "", $path);
Storage::disk($this->option('disk'))->put($path, $logo);
}
});
//documents
Document::cursor()
->each(function ($document){
$doc_bin = $document->getFile();
if($doc_bin)
Storage::disk($this->option('disk'))->put($document->url, $doc_bin);
});
//backups
Backup::cursor()
->each(function ($backup){
$backup_bin = Storage::disk('s3')->get($backup->filename);
if($backup_bin)
Storage::disk($this->option('disk'))->put($backup->filename, $backup_bin);
});
}
}

View File

@ -703,13 +703,32 @@ class CheckData extends Command
->count();
if($count == 0){
$this->logMessage("# {$client->id} # {$client->name} {$client->balance} is invalid should be 0");
//factor in over payments to the client balance
$over_payment = Payment::where('client_id', $client->id)
->where('is_deleted', 0)
->whereIn('status_id', [1,4])
->selectRaw('sum(amount - applied) as p')
->pluck('p')
->first();
if($this->option('client_balance')){
$over_payment = $over_payment*-1;
if(floatval($over_payment) == floatval($client->balance)){
}
else {
$this->logMessage("# {$client->id} # {$client->name} {$client->balance} is invalid should be {$over_payment}");
}
if($this->option('client_balance') && (floatval($over_payment) != floatval($client->balance) )){
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->balance} to 0");
$client->balance = 0;
$client->balance = $over_payment;
$client->save();
}
@ -1029,6 +1048,29 @@ class CheckData extends Command
$this->logMessage("Fixing - {$ninja_portal_url}");
}
else{
$c = Client::on('db-ninja-01')->where("company_id", config('ninja.ninja_default_company_id'))->where('custom_value2', $cu->account->key)->first();
if($c)
{
$cc = $c->contacts()->first();
if($cc)
{
$ninja_portal_url = "https://invoiceninja.invoicing.co/client/ninja/{$cc->contact_key}/{$cu->account->key}";
$cu->ninja_portal_url = $ninja_portal_url;
$cu->save();
$this->logMessage("Fixing - {$ninja_portal_url}");
}
}
}
});

View File

@ -25,6 +25,9 @@ use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Libraries\MultiDB;
use App\Models\Account;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\BankTransactionRule;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
@ -47,6 +50,7 @@ use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use Carbon\Carbon;
use Database\Factories\BankTransactionRuleFactory;
use Faker\Factory;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
@ -87,7 +91,7 @@ class CreateSingleAccount extends Command
MultiDB::setDb($this->option('database'));
$this->info(date('r').' Create Single Sample Account...');
$this->count = 1;
$this->count = 5;
$this->gateway = $this->argument('gateway');
$this->info('Warming up cache');
@ -179,6 +183,23 @@ class CreateSingleAccount extends Command
'rate' => 5
]);
$bi = BankIntegration::factory()->create([
'account_id' => $account->id,
'company_id' => $company->id,
'user_id' => $user->id,
]);
BankTransaction::factory()->count(50)->create([
'bank_integration_id' => $bi->id,
'user_id' => $user->id,
'company_id' => $company->id,
]);
$btr = BankTransactionRule::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'applies_to' => (bool)rand(0,1) ? 'CREDIT' : 'DEBIT',
]);
$this->info('Creating '.$this->count.' clients');
@ -358,7 +379,7 @@ class CreateSingleAccount extends Command
private function createExpense($client)
{
Expense::factory()->count(rand(1, 2))->create([
Expense::factory()->count(rand(1, 20))->create([
'user_id' => $client->user->id,
'client_id' => $client->id,
'company_id' => $client->company->id,
@ -590,7 +611,6 @@ class CreateSingleAccount extends Command
$cached_tables = config('ninja.cached_tables');
foreach ($cached_tables as $name => $class) {
if (! Cache::has($name)) {
// check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) {
continue;
@ -608,7 +628,6 @@ class CreateSingleAccount extends Command
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
}
}

View File

@ -437,7 +437,7 @@ class CreateTestData extends Command
'company_id' => $client->company->id,
]);
Document::factory()->count(50)->create([
Document::factory()->count(5)->create([
'user_id' => $client->user->id,
'company_id' => $client->company_id,
'documentable_type' => Vendor::class,

View File

@ -22,6 +22,8 @@ use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Ninja\CompanySizeCheck;
use App\Jobs\Util\VersionCheck;
use App\Models\Account;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
@ -39,6 +41,7 @@ use App\Models\Vendor;
use App\Models\VendorContact;
use App\Repositories\InvoiceRepository;
use App\Utils\Ninja;
use App\Utils\Traits\AppSetup;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use Carbon\Carbon;
@ -51,7 +54,7 @@ use Illuminate\Support\Str;
class DemoMode extends Command
{
use MakesHash, GeneratesCounter;
use MakesHash, GeneratesCounter, AppSetup;
protected $signature = 'ninja:demo-mode';
@ -81,34 +84,14 @@ class DemoMode extends Command
$cached_tables = config('ninja.cached_tables');
foreach ($cached_tables as $name => $class) {
if (! Cache::has($name)) {
// check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) {
continue;
}
if ($name == 'payment_terms') {
$orderBy = 'num_days';
} elseif ($name == 'fonts') {
$orderBy = 'sort_order';
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
$orderBy = 'name';
} else {
$orderBy = 'id';
}
$tableData = $class::orderBy($orderBy)->get();
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
}
$this->info('Migrating');
Artisan::call('migrate:fresh --force');
$this->info('Seeding');
Artisan::call('db:seed --force');
$this->buildCache(true);
$this->info('Seeding Random Data');
$this->createSmallAccount();
@ -223,6 +206,18 @@ class DemoMode extends Command
'company_id' => $company->id,
]);
$bi = BankIntegration::factory()->create([
'account_id' => $account->id,
'company_id' => $company->id,
'user_id' => $user->id,
]);
BankTransaction::factory()->count(50)->create([
'bank_integration_id' => $bi->id,
'user_id' => $user->id,
'company_id' => $company->id,
]);
$this->info('Creating '.$this->count.' clients');
for ($x = 0; $x < $this->count; $x++) {

View File

@ -103,8 +103,9 @@ class MobileLocalization extends Command
$data = substr($data, $start, $end - $start - 5);
$data = str_replace("\n", '', $data);
$data = str_replace('"', "\'", $data);
$data = str_replace("\'", "\#", $data);
$data = str_replace("'", '"', $data);
$data = str_replace("\#", "'", $data);
return json_decode('{'.rtrim($data, ',').'}');
}

View File

@ -42,7 +42,7 @@ class S3Cleanup extends Command
*/
public function handle()
{
if (! Ninja::isHosted()) {
if (!Ninja::isHosted()) {
return;
}

View File

@ -26,6 +26,7 @@ use App\Utils\Traits\MakesReminders;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\App;
//@deprecated 27-11-2022 - only ever should be used for testing
class SendRemindersCron extends Command
{
use MakesReminders, MakesDates;
@ -175,7 +176,7 @@ class SendRemindersCron extends Command
/**Refresh Invoice values*/
$invoice->calc()->getInvoice()->save();
$invoice->fresh();
$invoice->service()->deletePdf();
$invoice->service()->deletePdf()->save();
/* Refresh the client here to ensure the balance is fresh */
$client = $invoice->client;

View File

@ -89,14 +89,14 @@ class TranslationsExport extends Command
*/
public function handle()
{
Storage::makeDirectory(storage_path('lang'));
Storage::disk('local')->makeDirectory('lang');
foreach ($this->langs as $lang) {
Storage::makeDirectory(storage_path("lang/{$lang}"));
Storage::disk('local')->makeDirectory("lang/{$lang}");
$translations = Lang::getLoader()->load($lang, 'texts');
Storage::put(storage_path("lang/{$lang}/{$lang}.json"), json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE));
Storage::disk('local')->put("lang/{$lang}/{$lang}.json", json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE));
}
}
}

View File

@ -17,10 +17,12 @@ use App\Jobs\Cron\RecurringInvoicesCron;
use App\Jobs\Cron\SubscriptionCron;
use App\Jobs\Ledger\LedgerBalanceUpdate;
use App\Jobs\Ninja\AdjustEmailQuota;
use App\Jobs\Ninja\BankTransactionSync;
use App\Jobs\Ninja\CompanySizeCheck;
use App\Jobs\Ninja\QueueSize;
use App\Jobs\Ninja\SystemMaintenance;
use App\Jobs\Ninja\TaskScheduler;
use App\Jobs\Quote\QuoteCheckExpired;
use App\Jobs\Util\DiskCleanup;
use App\Jobs\Util\ReminderJob;
use App\Jobs\Util\SchedulerCheck;
@ -42,55 +44,81 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
/* Check for the latest version of Invoice Ninja */
$schedule->job(new VersionCheck)->daily();
$schedule->job(new DiskCleanup)->daily()->withoutOverlapping();
/* Checks and cleans redundant files */
$schedule->job(new DiskCleanup)->dailyAt('02:10')->withoutOverlapping()->name('disk-cleanup-job')->onOneServer();
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping();
/* Send reminders */
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping()->name('reminder-job')->onOneServer();
$schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping();
/* Returns the number of jobs in the queue */
$schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping()->name('queue-size-job')->onOneServer();
$schedule->job(new CompanySizeCheck)->daily()->withoutOverlapping();
/* Checks for large companies and marked them as is_large */
$schedule->job(new CompanySizeCheck)->dailyAt('23:20')->withoutOverlapping()->name('company-size-job')->onOneServer();
$schedule->job(new UpdateExchangeRates)->daily()->withoutOverlapping();
/* Pulls in the latest exchange rates */
$schedule->job(new UpdateExchangeRates)->dailyAt('23:30')->withoutOverlapping()->name('exchange-rate-job')->onOneServer();
$schedule->job(new SubscriptionCron)->daily()->withoutOverlapping();
/* Runs cleanup code for subscriptions */
$schedule->job(new SubscriptionCron)->dailyAt('00:01')->withoutOverlapping()->name('subscription-job')->onOneServer();
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
/* Sends recurring invoices*/
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping()->name('recurring-invoice-job')->onOneServer();
$schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping();
/* Sends recurring invoices*/
$schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping()->name('recurring-expense-job')->onOneServer();
$schedule->job(new AutoBillCron)->dailyAt('06:00')->withoutOverlapping();
/* Fires notifications for expired Quotes */
$schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping()->name('quote-expired-job')->onOneServer();
$schedule->job(new SchedulerCheck)->daily()->withoutOverlapping();
/* Performs auto billing */
$schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping()->name('auto-bill-job')->onOneServer();
$schedule->job(new TaskScheduler())->daily()->withoutOverlapping();
/* Checks the status of the scheduler */
$schedule->job(new SchedulerCheck)->dailyAt('01:10')->withoutOverlapping();
$schedule->job(new SystemMaintenance)->weekly()->withoutOverlapping();
/* Checks for scheduled tasks */
$schedule->job(new TaskScheduler())->dailyAt('06:50')->withoutOverlapping()->name('task-scheduler-job')->onOneServer();
/* Performs system maintenance such as pruning the backup table */
$schedule->job(new SystemMaintenance)->sundays()->at('02:30')->withoutOverlapping()->name('system-maintenance-job')->onOneServer();
/* Pulls in bank transactions from third party services */
$schedule->job(new BankTransactionSync)->dailyAt('04:10')->withoutOverlapping()->name('bank-trans-sync-job')->onOneServer();
if (Ninja::isSelfHost()) {
$schedule->call(function () {
Account::whereNotNull('id')->update(['is_scheduler_running' => true]);
})->everyFiveMinutes();
}
/* Run hosted specific jobs */
if (Ninja::isHosted()) {
$schedule->job(new AdjustEmailQuota)->dailyAt('23:30')->withoutOverlapping();
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
//not used @deprecate
// $schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
$schedule->command('ninja:check-data --database=db-ninja-01')->daily('02:00')->withoutOverlapping();
$schedule->command('ninja:check-data --database=db-ninja-01')->dailyAt('02:10')->withoutOverlapping()->name('check-data-db-1-job')->onOneServer();
$schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('02:05')->withoutOverlapping();
$schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('02:20')->withoutOverlapping()->name('check-data-db-2-job')->onOneServer();
$schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping()->name('s3-cleanup-job')->onOneServer();
$schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping();
}
if (config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled') && ! config('ninja.is_docker')) {
$schedule->command('queue:work database --stop-when-empty --memory=256')->everyMinute()->withoutOverlapping();
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class AccountSignup 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 = 'account.signup';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
/**
* The Class failure name
* set to 0.
*
* @var string
*/
public $string_metric5 = 'plan';
/**
* The exception string
* set to 0.
*
* @var string
*/
public $string_metric6 = 'term';
/**
* The counter
* set to 1.
*
* @var string
*/
public $int_metric1 = 1;
/**
* Company Key
* @var string
*/
public $string_metric7 = 'key';
public function __construct($string_metric5, $string_metric6, $string_metric7)
{
$this->string_metric5 = $string_metric5 ?: 'free';
$this->string_metric6 = $string_metric6 ?: 'year';
$this->string_metric7 = $string_metric7;
}
}

View File

@ -0,0 +1,50 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class BankAccountsCreated 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 = 'bank_accounts.created';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
public $int_metric1 = 0;
public function __construct($int_metric1)
{
$this->int_metric1 = $int_metric1;
}
}

View File

@ -44,6 +44,8 @@ class InvoiceItem
public $line_total = 0;
public $gross_line_total = 0;
public $tax_amount = 0;
public $date = '';
@ -75,6 +77,7 @@ class InvoiceItem
'sort_id' => 'string',
'line_total' => 'float',
'gross_line_total' => 'float',
'tax_amount' => 'float',
'date' => 'string',
'custom_value1' => 'string',
'custom_value2' => 'string',

View File

@ -21,6 +21,7 @@ use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
use Illuminate\Database\Eloquent\RelationNotFoundException;
use Illuminate\Database\QueryException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Illuminate\Http\Request;
@ -31,6 +32,7 @@ use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Schema;
use Illuminate\Validation\ValidationException;
use PDOException;
use Sentry\Laravel\Integration;
use Sentry\State\Scope;
use Swift_TransportException;
use Symfony\Component\Console\Exception\CommandNotFoundException;
@ -83,7 +85,7 @@ class Handler extends ExceptionHandler
}
if (Ninja::isHosted() && ! ($exception instanceof ValidationException)) {
app('sentry')->configureScope(function (Scope $scope): void {
Integration::configureScope(function (Scope $scope): void {
$name = 'hosted@invoiceninja.com';
if (auth()->guard('contact') && auth()->guard('contact')->user()) {
@ -103,9 +105,9 @@ class Handler extends ExceptionHandler
]);
});
app('sentry')->captureException($exception);
Integration::captureUnhandledException($exception);
} elseif (app()->bound('sentry') && $this->shouldReport($exception)) {
app('sentry')->configureScope(function (Scope $scope): void {
Integration::configureScope(function (Scope $scope): void {
if (auth()->guard('contact') && auth()->guard('contact')->user() && auth()->guard('contact')->user()->company->account->report_errors) {
$scope->setUser([
'id' => auth()->guard('contact')->user()->company->account->key,
@ -122,7 +124,7 @@ class Handler extends ExceptionHandler
});
if ($this->validException($exception)) {
app('sentry')->captureException($exception);
Integration::captureUnhandledException($exception);
}
}
@ -181,7 +183,7 @@ class Handler extends ExceptionHandler
} elseif ($exception instanceof FatalThrowableError && $request->expectsJson()) {
return response()->json(['message'=>'Fatal error'], 500);
} elseif ($exception instanceof AuthorizationException) {
return response()->json(['message'=>'You are not authorized to view or perform this action'], 401);
return response()->json(['message'=> $exception->getMessage()], 401);
} elseif ($exception instanceof TokenMismatchException) {
return redirect()
->back()
@ -197,14 +199,21 @@ class Handler extends ExceptionHandler
// nlog($exception->validator->getMessageBag());
return response()->json(['message' => 'The given data was invalid.', 'errors' => $exception->validator->getMessageBag()], 422);
} elseif ($exception instanceof RelationNotFoundException && $request->expectsJson()) {
return response()->json(['message' => $exception->getMessage()], 400);
return response()->json(['message' => "Relation `{$exception->relation}` is not a valid include."], 400);
} elseif ($exception instanceof GenericPaymentDriverFailure && $request->expectsJson()) {
return response()->json(['message' => $exception->getMessage()], 400);
} elseif ($exception instanceof GenericPaymentDriverFailure) {
return response()->json(['message' => $exception->getMessage()], 400);
} elseif ($exception instanceof StripeConnectFailure) {
return response()->json(['message' => $exception->getMessage()], 400);
}
}
// elseif ($exception instanceof QueryException) {
// return response()->json(['message' => 'We had a problem executing this query. Please retry.'], 500);
// }
return parent::render($request, $exception);
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class YodleeApiException extends Exception
{
/**
* Report the exception.
*
* @return void
*/
public function report()
{
//
}
/**
* Render the exception into an HTTP response.
*
* @param Request $request
* @return Response
*/
public function render($request)
{
// $msg = 'Unable to refund the transaction';
$msg = ctrans('texts.error');
if ($this->getMessage() && strlen($this->getMessage()) >= 1) {
$msg = $this->getMessage();
}
return response()->json([
'message' => $msg,
], 400);
}
}

View File

@ -76,6 +76,7 @@ class ClientExport extends BaseExport
'contact_custom_value3' => 'contact.custom_value3',
'contact_custom_value4' => 'contact.custom_value4',
'email' => 'contact.email',
'status' => 'status'
];
private array $decorate_keys = [
@ -173,6 +174,19 @@ class ClientExport extends BaseExport
$entity['industry_id'] = $client->industry ? ctrans("texts.industry_{$client->industry->name}") : '';
}
$entity['status'] = $this->calculateStatus($client);
return $entity;
}
private function calculateStatus($client)
{
if($client->is_deleted)
return ctrans('texts.deleted');
if($client->deleted_at)
return ctrans('texts.arcvived');
return ctrans('texts.active');
}
}

View File

@ -88,6 +88,7 @@ class InvoiceItemExport extends BaseExport
private array $decorate_keys = [
'client',
'currency_id',
'status'
];
public function __construct(Company $company, array $input)
@ -116,6 +117,7 @@ class InvoiceItemExport extends BaseExport
$this->csv->insertOne($this->buildHeader());
$query = Invoice::query()
->withTrashed()
->with('client')->where('company_id', $this->company->id)
->where('is_deleted',0);
@ -206,10 +208,10 @@ class InvoiceItemExport extends BaseExport
if(in_array('currency_id', $this->input['report_keys']))
$entity['currency'] = $invoice->client->currency() ? $invoice->client->currency()->code : $invoice->company->currency()->code;
if(in_array('client_id', $this->input['report_keys']))
// if(in_array('client_id', $this->input['report_keys']))
$entity['client'] = $invoice->client->present()->name();
if(in_array('status_id', $this->input['report_keys']))
// if(in_array('status_id', $this->input['report_keys']))
$entity['status'] = $invoice->stringStatus($invoice->status_id);
return $entity;

View File

@ -89,7 +89,10 @@ class PaymentExport extends BaseExport
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Payment::query()->where('company_id', $this->company->id)->where('is_deleted', 0);
$query = Payment::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);

View File

@ -82,7 +82,10 @@ class ProductExport extends BaseExport
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Product::query()->where('company_id', $this->company->id)->where('is_deleted', 0);
$query = Product::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);

View File

@ -99,6 +99,7 @@ class QuoteExport extends BaseExport
$this->csv->insertOne($this->buildHeader());
$query = Quote::query()
->withTrashed()
->with('client')
->where('company_id', $this->company->id)
->where('is_deleted', 0);

View File

@ -116,6 +116,7 @@ class QuoteItemExport extends BaseExport
$this->csv->insertOne($this->buildHeader());
$query = Quote::query()
->withTrashed()
->with('client')->where('company_id', $this->company->id)
->where('is_deleted', 0);

View File

@ -54,6 +54,7 @@ class RecurringInvoiceExport extends BaseExport
'po_number' => 'po_number',
'private_notes' => 'private_notes',
'public_notes' => 'public_notes',
'next_send_date' => 'next_send_date',
'status' => 'status_id',
'tax_name1' => 'tax_name1',
'tax_name2' => 'tax_name2',
@ -66,6 +67,7 @@ class RecurringInvoiceExport extends BaseExport
'currency' => 'currency_id',
'vendor' => 'vendor_id',
'project' => 'project_id',
'frequency' => 'frequency_id'
];
private array $decorate_keys = [
@ -162,6 +164,8 @@ class RecurringInvoiceExport extends BaseExport
$entity['vendor'] = $invoice->vendor ? $invoice->vendor->name : '';
}
$entity['frequency'] = $invoice->frequencyForKey($invoice->frequency_id);
return $entity;
}
}

View File

@ -91,7 +91,10 @@ class TaskExport extends BaseExport
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Task::query()->where('company_id', $this->company->id)->where('is_deleted', 0);
$query = Task::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);

View File

@ -0,0 +1,37 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Factory;
use App\Models\BankIntegration;
use Illuminate\Support\Str;
class BankIntegrationFactory
{
public static function create(int $company_id, int $user_id, int $account_id) :BankIntegration
{
$bank_integration = new BankIntegration;
$bank_integration->account_id = $account_id;
$bank_integration->user_id = $user_id;
$bank_integration->company_id = $company_id;
$bank_integration->provider_name = '';
$bank_integration->bank_account_id = '';
$bank_integration->bank_account_name = '';
$bank_integration->bank_account_number = '';
$bank_integration->bank_account_status = '';
$bank_integration->bank_account_type = '';
$bank_integration->balance = 0;
$bank_integration->currency = '';
return $bank_integration;
}
}

View File

@ -0,0 +1,35 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Factory;
use App\Models\BankTransaction;
use Illuminate\Support\Str;
class BankTransactionFactory
{
public static function create(int $company_id, int $user_id) :BankTransaction
{
$bank_transaction = new BankTransaction;
$bank_transaction->user_id = $user_id;
$bank_transaction->company_id = $company_id;
$bank_transaction->amount = 0;
$bank_transaction->currency_id = 1;
$bank_transaction->account_type = '';
$bank_transaction->category_type = '';
$bank_transaction->date = now()->format('Y-m-d');
$bank_transaction->description = '';
$bank_transaction->status_id = 1;
return $bank_transaction;
}
}

View File

@ -0,0 +1,30 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Factory;
use App\Models\BankTransactionRule;
use Illuminate\Support\Str;
class BankTransactionRuleFactory
{
public static function create(int $company_id, int $user_id) :BankTransactionRule
{
$bank_transaction_rule = new BankTransactionRule;
$bank_transaction_rule->user_id = $user_id;
$bank_transaction_rule->company_id = $company_id;
$bank_transaction_rule->name = '';
$bank_transaction_rule->rules = [];
return $bank_transaction_rule;
}
}

View File

@ -29,6 +29,7 @@ class RecurringInvoiceFactory
$invoice->private_notes = '';
$invoice->date = null;
$invoice->due_date = null;
$invoice->due_date_days = 'terms';
$invoice->partial_due_date = null;
$invoice->is_deleted = false;
$invoice->line_items = json_encode([]);

View File

@ -25,9 +25,9 @@ class RecurringInvoiceToInvoiceFactory
$invoice->discount = $recurring_invoice->discount;
$invoice->is_amount_discount = $recurring_invoice->is_amount_discount;
$invoice->po_number = $recurring_invoice->po_number;
$invoice->footer = self::tranformObject($recurring_invoice->footer, $client);
$invoice->terms = self::tranformObject($recurring_invoice->terms, $client);
$invoice->public_notes = self::tranformObject($recurring_invoice->public_notes, $client);
$invoice->footer = $recurring_invoice->footer ? self::tranformObject($recurring_invoice->footer, $client) : null;
$invoice->terms = $recurring_invoice->terms ? self::tranformObject($recurring_invoice->terms, $client) : null;
$invoice->public_notes = $recurring_invoice->public_notes ? self::tranformObject($recurring_invoice->public_notes, $client) : null;
$invoice->private_notes = $recurring_invoice->private_notes;
$invoice->is_deleted = $recurring_invoice->is_deleted;
$invoice->line_items = self::transformItems($recurring_invoice, $client);

View File

@ -0,0 +1,133 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Filters;
use App\Models\BankIntegration;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
/**
* BankIntegrationFilters.
*/
class BankIntegrationFilters extends QueryFilters
{
/**
* Filter by name.
*
* @param string $name
* @return Builder
*/
public function name(string $name = ''): Builder
{
if(strlen($name) >=1)
return $this->builder->where('bank_account_name', 'like', '%'.$name.'%');
return $this->builder;
}
/**
* Filter based on search text.
*
* @param string query filter
* @return Builder
* @deprecated
*/
public function filter(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
return $this->builder->where(function ($query) use ($filter) {
$query->where('bank_integrations.bank_account_name', 'like', '%'.$filter.'%');
});
}
/**
* Filters the list based on the status
* archived, active, deleted.
*
* @param string filter
* @return Builder
*/
public function status(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
$table = 'bank_integrations';
$filters = explode(',', $filter);
return $this->builder->where(function ($query) use ($filters, $table) {
$query->whereNull($table.'.id');
if (in_array(parent::STATUS_ACTIVE, $filters)) {
$query->orWhereNull($table.'.deleted_at');
}
if (in_array(parent::STATUS_ARCHIVED, $filters)) {
$query->orWhere(function ($query) use ($table) {
$query->whereNotNull($table.'.deleted_at');
if (! in_array($table, ['users'])) {
$query->where($table.'.is_deleted', '=', 0);
}
});
}
if (in_array(parent::STATUS_DELETED, $filters)) {
$query->orWhere($table.'.is_deleted', '=', 1);
}
});
}
/**
* Sorts the list based on $sort.
*
* @param string sort formatted as column|asc
* @return Builder
*/
public function sort(string $sort) : Builder
{
$sort_col = explode('|', $sort);
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}
/**
* Returns the base query.
*
* @param int company_id
* @param User $user
* @return Builder
* @deprecated
*/
public function baseQuery(int $company_id, User $user) : Builder
{
}
/**
* Filters the query by the users company ID.
*
* @return Illuminate\Database\Query\Builder
*/
public function entityFilter()
{
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
return $this->builder->company();
}
}

View File

@ -0,0 +1,197 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Filters;
use App\Models\BankTransaction;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
/**
* BankTransactionFilters.
*/
class BankTransactionFilters extends QueryFilters
{
/**
* Filter by name.
*
* @param string $name
* @return Builder
*/
public function name(string $name = ''): Builder
{
if(strlen($name) >=1)
return $this->builder->where('bank_account_name', 'like', '%'.$name.'%');
return $this->builder;
}
/**
* Filter based on search text.
*
* @param string query filter
* @return Builder
* @deprecated
*/
public function filter(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
return $this->builder->where(function ($query) use ($filter) {
$query->where('bank_transactions.description', 'like', '%'.$filter.'%');
});
}
/**
* Filter based on client status.
*
* Statuses we need to handle
* - all
* - unmatched
* - matched
* - converted
* - deposits
* - withdrawals
*
* @return Builder
*/
public function client_status(string $value = '') :Builder
{
if (strlen($value) == 0) {
return $this->builder;
}
$status_parameters = explode(',', $value);
if (in_array('all', $status_parameters)) {
return $this->builder;
}
if (in_array('unmatched', $status_parameters)) {
$this->builder->where('status_id', BankTransaction::STATUS_UNMATCHED);
}
if (in_array('matched', $status_parameters)) {
$this->builder->where('status_id', BankTransaction::STATUS_MATCHED);
}
if (in_array('converted', $status_parameters)) {
$this->builder->where('status_id', BankTransaction::STATUS_CONVERTED);
}
if (in_array('deposits', $status_parameters)) {
$this->builder->where('base_type', 'CREDIT');
}
if (in_array('withdrawals', $status_parameters)) {
$this->builder->where('base_type', 'DEBIT');
}
return $this->builder;
}
/**
* Filters the list based on the status
* archived, active, deleted.
*
* @param string filter
* @return Builder
*/
public function status(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
$table = 'bank_transactions';
$filters = explode(',', $filter);
return $this->builder->where(function ($query) use ($filters, $table) {
$query->whereNull($table.'.id');
if (in_array(parent::STATUS_ACTIVE, $filters)) {
$query->orWhereNull($table.'.deleted_at');
}
if (in_array(parent::STATUS_ARCHIVED, $filters)) {
$query->orWhere(function ($query) use ($table) {
$query->whereNotNull($table.'.deleted_at');
if (! in_array($table, ['users'])) {
$query->where($table.'.is_deleted', '=', 0);
}
});
}
if (in_array(parent::STATUS_DELETED, $filters)) {
$query->orWhere($table.'.is_deleted', '=', 1);
}
});
}
/**
* Sorts the list based on $sort.
*
* @param string sort formatted as column|asc
* @return Builder
*/
public function sort(string $sort) : Builder
{
$sort_col = explode('|', $sort);
if(!is_array($sort_col))
return $this->builder;
if($sort_col[0] == 'deposit')
return $this->builder->where('base_type', 'CREDIT')->orderBy('amount', $sort_col[1]);
if($sort_col[0] == 'withdrawal')
return $this->builder->where('base_type', 'DEBIT')->orderBy('amount', $sort_col[1]);
if($sort_col[0] == 'status')
$sort_col[0] = 'status_id';
if(in_array($sort_col[0],['invoices','expense']))
return $this->builder;
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}
/**
* Returns the base query.
*
* @param int company_id
* @param User $user
* @return Builder
* @deprecated
*/
public function baseQuery(int $company_id, User $user) : Builder
{
}
/**
* Filters the query by the users company ID.
*
* @return Illuminate\Database\Query\Builder
*/
public function entityFilter()
{
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
return $this->builder->company();
}
}

View File

@ -0,0 +1,133 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Filters;
use App\Models\BankTransactionRule;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
/**
* BankTransactionRuleilters.
*/
class BankTransactionRuleFilters extends QueryFilters
{
/**
* Filter by name.
*
* @param string $name
* @return Builder
*/
public function name(string $name = ''): Builder
{
if(strlen($name) >=1)
return $this->builder->where('name', 'like', '%'.$name.'%');
return $this->builder;
}
/**
* Filter based on search text.
*
* @param string query filter
* @return Builder
* @deprecated
*/
public function filter(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
return $this->builder->where(function ($query) use ($filter) {
$query->where('bank_transaction_rules.name', 'like', '%'.$filter.'%');
});
}
/**
* Filters the list based on the status
* archived, active, deleted.
*
* @param string filter
* @return Builder
*/
public function status(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
$table = 'bank_transaction_rules';
$filters = explode(',', $filter);
return $this->builder->where(function ($query) use ($filters, $table) {
$query->whereNull($table.'.id');
if (in_array(parent::STATUS_ACTIVE, $filters)) {
$query->orWhereNull($table.'.deleted_at');
}
if (in_array(parent::STATUS_ARCHIVED, $filters)) {
$query->orWhere(function ($query) use ($table) {
$query->whereNotNull($table.'.deleted_at');
if (! in_array($table, ['users'])) {
$query->where($table.'.is_deleted', '=', 0);
}
});
}
if (in_array(parent::STATUS_DELETED, $filters)) {
$query->orWhere($table.'.is_deleted', '=', 1);
}
});
}
/**
* Sorts the list based on $sort.
*
* @param string sort formatted as column|asc
* @return Builder
*/
public function sort(string $sort) : Builder
{
$sort_col = explode('|', $sort);
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}
/**
* Returns the base query.
*
* @param int company_id
* @param User $user
* @return Builder
* @deprecated
*/
public function baseQuery(int $company_id, User $user) : Builder
{
}
/**
* Filters the query by the users company ID.
*
* @return Illuminate\Database\Query\Builder
*/
public function entityFilter()
{
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
return $this->builder->company();
}
}

View File

@ -28,7 +28,7 @@ class ClientFilters extends QueryFilters
* @param string $name
* @return Builder
*/
public function name(string $name): Builder
public function name(string $name = ''): Builder
{
if(strlen($name) >=1)
return $this->builder->where('name', 'like', '%'.$name.'%');

View File

@ -136,6 +136,6 @@ class DesignFilters extends QueryFilters
public function entityFilter()
{
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
return $this->builder->whereCompanyId(auth()->user()->company()->id)->orWhere('company_id', null);
return $this->builder->where('company_id', auth()->user()->company()->id)->orWhere('company_id', null)->orderBy('id','asc');
}
}

View File

@ -11,6 +11,7 @@
namespace App\Filters;
use App\Models\Company;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
@ -54,6 +55,15 @@ class DocumentFilters extends QueryFilters
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}
public function company_documents($value = 'false')
{
if($value == 'true')
return $this->builder->where('documentable_type', Company::class);
return $this->builder;
}
/**
* Returns the base query.
*

View File

@ -44,6 +44,55 @@ class ExpenseFilters extends QueryFilters
});
}
/**
* Filter based on client status.
*
* Statuses we need to handle
* - all
* - logged
* - pending
* - invoiced
* - paid
* - unpaid
*
* @return Builder
*/
public function client_status(string $value = '') :Builder
{
if (strlen($value) == 0) {
return $this->builder;
}
$status_parameters = explode(',', $value);
if (in_array('all', $status_parameters)) {
return $this->builder;
}
if (in_array('logged', $status_parameters)) {
$this->builder->where('amount', '>', 0);
}
if (in_array('pending', $status_parameters)) {
$this->builder->whereNull('invoice_id')->whereNotNull('payment_date');
}
if (in_array('invoiced', $status_parameters)) {
$this->builder->whereNotNull('invoice_id');
}
if (in_array('paid', $status_parameters)) {
$this->builder->whereNotNull('payment_date');
}
if (in_array('unpaid', $status_parameters)) {
$this->builder->whereNull('payment_date');
}
return $this->builder;
}
/**
* Filters the list based on the status
* archived, active, deleted.

View File

@ -56,8 +56,6 @@ class InvoiceFilters extends QueryFilters
if (in_array('unpaid', $status_parameters)) {
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]);
}
//->where('due_date', '>', Carbon::now())
//->orWhere('partial_due_date', '>', Carbon::now());
if (in_array('overdue', $status_parameters)) {
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
@ -167,7 +165,7 @@ class InvoiceFilters extends QueryFilters
->orderBy('due_date', 'ASC');
}
public function payable(string $client_id)
public function payable(string $client_id = '')
{
if (strlen($client_id) == 0) {
return $this->builder;
@ -185,7 +183,7 @@ class InvoiceFilters extends QueryFilters
* @param string sort formatted as column|asc
* @return Builder
*/
public function sort(string $sort) : Builder
public function sort(string $sort = '') : Builder
{
$sort_col = explode('|', $sort);

View File

@ -102,6 +102,9 @@ class ProductFilters extends QueryFilters
{
$sort_col = explode('|', $sort);
if(!is_array($sort_col))
return $this->builder;
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}

View File

@ -17,19 +17,20 @@ use Illuminate\Database\Eloquent\Builder;
class PurchaseOrderFilters extends QueryFilters
{
/**
* Filter based on client status.
*
* Statuses we need to handle
* - all
* - paid
* - unpaid
* - overdue
* - reversed
* - draft
* - sent
* - accepted
* - cancelled
*
* @return Builder
*/
public function credit_status(string $value = '') :Builder
public function client_status(string $value = '') :Builder
{
if (strlen($value) == 0) {
return $this->builder;
@ -45,16 +46,17 @@ class PurchaseOrderFilters extends QueryFilters
$this->builder->where('status_id', PurchaseOrder::STATUS_DRAFT);
}
if (in_array('partial', $status_parameters)) {
$this->builder->where('status_id', PurchaseOrder::STATUS_PARTIAL);
if (in_array('sent', $status_parameters)) {
$this->builder->where('status_id', PurchaseOrder::STATUS_SENT);
}
if (in_array('applied', $status_parameters)) {
$this->builder->where('status_id', PurchaseOrder::STATUS_APPLIED);
if (in_array('accepted', $status_parameters)) {
$this->builder->where('status_id', PurchaseOrder::STATUS_ACCEPTED);
}
//->where('due_date', '>', Carbon::now())
//->orWhere('partial_due_date', '>', Carbon::now());
if (in_array('cancelled', $status_parameters)) {
$this->builder->where('status_id', PurchaseOrder::STATUS_CANCELLED);
}
return $this->builder;
}

View File

@ -169,7 +169,7 @@ abstract class QueryFilters
public function clientFilter()
{
if (auth()->guard('contact')->user()) {
return $this->builder->whereClientId(auth()->guard('contact')->user()->client->id);
return $this->builder->where('client_id', auth()->guard('contact')->user()->client->id);
}
}
@ -179,6 +179,15 @@ abstract class QueryFilters
$created_at = date('Y-m-d H:i:s', $value);
if(is_string($created_at)){
$created_at = strtotime(str_replace("/","-",$created_at));
if(!$created_at)
return $this->builder;
}
return $this->builder->where('created_at', '>=', $created_at);
}

View File

@ -11,6 +11,7 @@
namespace App\Filters;
use App\Models\Quote;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
@ -33,13 +34,59 @@ class QuoteFilters extends QueryFilters
}
return $this->builder->where(function ($query) use ($filter) {
$query->where('quotes.custom_value1', 'like', '%'.$filter.'%')
->orWhere('quotes.custom_value2', 'like', '%'.$filter.'%')
->orWhere('quotes.custom_value3', 'like', '%'.$filter.'%')
->orWhere('quotes.custom_value4', 'like', '%'.$filter.'%');
$query->where('quotes.number', 'like', '%'.$filter.'%')
->orwhere('quotes.custom_value1', 'like', '%'.$filter.'%')
->orWhere('quotes.custom_value2', 'like', '%'.$filter.'%')
->orWhere('quotes.custom_value3', 'like', '%'.$filter.'%')
->orWhere('quotes.custom_value4', 'like', '%'.$filter.'%');
});
}
/**
* Filter based on client status.
*
* Statuses we need to handle
* - all
* - active
* - paused
* - completed
*
* @param string client_status The invoice status as seen by the client
* @return Builder
*/
public function client_status(string $value = '') :Builder
{
if (strlen($value) == 0) {
return $this->builder;
}
$status_parameters = explode(',', $value);
if (in_array('all', $status_parameters)) {
return $this->builder;
}
if (in_array('draft', $status_parameters)) {
$this->builder->where('status_id', Quote::STATUS_DRAFT);
}
if (in_array('sent', $status_parameters)) {
$this->builder->where('status_id', Quote::STATUS_SENT);
}
if (in_array('approved', $status_parameters)) {
$this->builder->where('status_id', Quote::STATUS_APPROVED);
}
if (in_array('expired', $status_parameters)) {
$this->builder->where('status_id', Quote::STATUS_SENT)
->where('due_date', '<=', now()->toDateString());
}
return $this->builder;
}
/**
* Filters the list based on the status
* archived, active, deleted.

View File

@ -11,6 +11,7 @@
namespace App\Filters;
use App\Models\RecurringInvoice;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
@ -40,6 +41,46 @@ class RecurringInvoiceFilters extends QueryFilters
});
}
/**
* Filter based on client status.
*
* Statuses we need to handle
* - all
* - active
* - paused
* - completed
*
* @param string client_status The invoice status as seen by the client
* @return Builder
*/
public function client_status(string $value = '') :Builder
{
if (strlen($value) == 0) {
return $this->builder;
}
$status_parameters = explode(',', $value);
if (in_array('all', $status_parameters)) {
return $this->builder;
}
if (in_array('active', $status_parameters)) {
$this->builder->where('status_id', RecurringInvoice::STATUS_ACTIVE);
}
if (in_array('paused', $status_parameters)) {
$this->builder->where('status_id', RecurringInvoice::STATUS_PAUSED);
}
if (in_array('completed', $status_parameters)) {
$this->builder->where('status_id', RecurringInvoice::STATUS_COMPLETED);
}
return $this->builder;
}
/**
* Filters the list based on the status
* archived, active, deleted.

View File

@ -12,6 +12,7 @@
namespace App\Filters;
use App\Models\User;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Builder;
/**
@ -19,6 +20,8 @@ use Illuminate\Database\Eloquent\Builder;
*/
class TaskFilters extends QueryFilters
{
use MakesHash;
/**
* Filter based on search text.
*
@ -41,6 +44,37 @@ class TaskFilters extends QueryFilters
});
}
/**
* Filter based on client status.
*
* Statuses we need to handle
* - all
* - invoiced
*
* @param string client_status The invoice status as seen by the client
* @return Builder
*/
public function client_status(string $value = '') :Builder
{
if (strlen($value) == 0) {
return $this->builder;
}
$status_parameters = explode(',', $value);
if (in_array('all', $status_parameters)) {
return $this->builder;
}
if (in_array('invoiced', $status_parameters)) {
$this->builder->whereNotNull('invoice_id');
}
return $this->builder;
}
/**
* Filters the list based on the status
* archived, active, deleted.
@ -80,6 +114,16 @@ class TaskFilters extends QueryFilters
});
}
public function project_tasks($project)
{
if (strlen($project) == 0) {
return $this->builder;
}
return $this->builder->where('project_id', $this->decodePrimaryKey($project));
}
/**
* Sorts the list based on $sort.
*

View File

@ -0,0 +1,17 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\Bank;
interface AccountTransformerInterface
{
public function transform($accounts);
}

View File

@ -0,0 +1,17 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\Bank;
interface BankExpenseInterface
{
}

View File

@ -0,0 +1,17 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\Bank;
interface BankRevenueInterface
{
public function transform($transaction);
}

View File

@ -0,0 +1,104 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\Bank\Yodlee\Transformer;
use App\Helpers\Bank\AccountTransformerInterface;
/**
[0] => stdClass Object
(
[CONTAINER] => bank
[providerAccountId] => 11308693
[accountName] => My CD - 8878
[accountStatus] => ACTIVE
[accountNumber] => xxxx8878
[aggregationSource] => USER
[isAsset] => 1
[balance] => stdClass Object
(
[currency] => USD
[amount] => 49778.07
)
[id] => 12331861
[includeInNetWorth] => 1
[providerId] => 18769
[providerName] => Dag Site Captcha
[isManual] =>
[currentBalance] => stdClass Object
(
[currency] => USD
[amount] => 49778.07
)
[accountType] => CD
[displayedName] => LORETTA
[createdDate] => 2022-07-28T06:55:33Z
[lastUpdated] => 2022-07-28T06:56:09Z
[dataset] => Array
(
[0] => stdClass Object
(
[name] => BASIC_AGG_DATA
[additionalStatus] => AVAILABLE_DATA_RETRIEVED
[updateEligibility] => ALLOW_UPDATE
[lastUpdated] => 2022-07-28T06:55:50Z
[lastUpdateAttempt] => 2022-07-28T06:55:50Z
)
)
)
)
*/
class AccountTransformer implements AccountTransformerInterface
{
public function transform($yodlee_account)
{
$data = [];
if(!property_exists($yodlee_account, 'account'))
return $data;
foreach($yodlee_account->account as $account)
{
$data[] = $this->transformAccount($account);
}
return $data;
}
public function transformAccount($account)
{
return [
'id' => $account->id,
'account_type' => $account->CONTAINER,
// 'account_name' => $account->accountName,
'account_name' => property_exists($account, 'accountName') ? $account->accountName : $account->nickname,
'account_status' => $account->accountStatus,
'account_number' => property_exists($account, 'accountNumber') ? '**** ' . substr($account?->accountNumber, -7) : '',
'provider_account_id' => $account->providerAccountId,
'provider_id' => $account->providerId,
'provider_name' => $account->providerName,
'nickname' => property_exists($account, 'nickname') ? $account->nickname : '',
'current_balance' => property_exists($account, 'currentBalance') ? $account->currentBalance->amount : 0,
'account_currency' => property_exists($account, 'currency') ? $account->currentBalance->currency : '',
];
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\Bank\Yodlee\Transformer;
/**
"date": "string",
"sourceId": "string",
"symbol": "string",
"cusipNumber": "string",
"highLevelCategoryId": 0,
"detailCategoryId": 0,
"description": {},
"memo": "string",
"settleDate": "string",
"type": "string",
"intermediary": [],
"baseType": "CREDIT",
"categorySource": "SYSTEM",
"principal": {},
"lastUpdated": "string",
"interest": {},
"price": {},
"commission": {},
"id": 0,
"merchantType": "string",
"amount": {
"amount": 0,
"convertedAmount": 0,
"currency": "USD",
"convertedCurrency": "USD"
},
"checkNumber": "string",
"isPhysical": true,
"quantity": 0,
"valoren": "string",
"isManual": true,
"merchant": {
"website": "string",
"address": {},
"contact": {},
"categoryLabel": [],
"coordinates": {},
"name": "string",
"id": "string",
"source": "YODLEE",
"logoURL": "string"
},
"sedol": "string",
"transactionDate": "string",
"categoryType": "TRANSFER",
"accountId": 0,
"createdDate": "string",
"sourceType": "AGGREGATED",
"CONTAINER": "bank",
"postDate": "string",
"parentCategoryId": 0,
"subType": "OVERDRAFT_CHARGE",
"category": "string",
"runningBalance": {},
"categoryId": 0,
"holdingDescription": "string",
"isin": "string",
"status": "POSTED"
*/
class ExpenseTransformer
{
}

View File

@ -0,0 +1,187 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\Bank\Yodlee\Transformer;
use App\Helpers\Bank\BankRevenueInterface;
use App\Utils\Traits\AppSetup;
use Illuminate\Support\Facades\Cache;
/**
"date": "string",
"sourceId": "string",
"symbol": "string",
"cusipNumber": "string",
"highLevelCategoryId": 0,
"detailCategoryId": 0,
"description": {},
"memo": "string",
"settleDate": "string",
"type": "string",
"intermediary": [],
"baseType": "CREDIT",
"categorySource": "SYSTEM",
"principal": {},
"lastUpdated": "string",
"interest": {},
"price": {},
"commission": {},
"id": 0,
"merchantType": "string",
"amount": {
"amount": 0,
"convertedAmount": 0,
"currency": "USD",
"convertedCurrency": "USD"
},
"checkNumber": "string",
"isPhysical": true,
"quantity": 0,
"valoren": "string",
"isManual": true,
"merchant": {
"website": "string",
"address": {},
"contact": {},
"categoryLabel": [],
"coordinates": {},
"name": "string",
"id": "string",
"source": "YODLEE",
"logoURL": "string"
},
"sedol": "string",
"transactionDate": "string",
"categoryType": "TRANSFER",
"accountId": 0,
"createdDate": "string",
"sourceType": "AGGREGATED",
"CONTAINER": "bank",
"postDate": "string",
"parentCategoryId": 0,
"subType": "OVERDRAFT_CHARGE",
"category": "string",
"runningBalance": {},
"categoryId": 0,
"holdingDescription": "string",
"isin": "string",
"status": "POSTED"
(
[CONTAINER] => bank
[id] => 103953585
[amount] => stdClass Object
(
[amount] => 480.66
[currency] => USD
)
[categoryType] => UNCATEGORIZE
[categoryId] => 1
[category] => Uncategorized
[categorySource] => SYSTEM
[highLevelCategoryId] => 10000017
[createdDate] => 2022-08-04T21:50:17Z
[lastUpdated] => 2022-08-04T21:50:17Z
[description] => stdClass Object
(
[original] => CHEROKEE NATION TAX TA TAHLEQUAH OK
)
[isManual] =>
[sourceType] => AGGREGATED
[date] => 2022-08-03
[transactionDate] => 2022-08-03
[postDate] => 2022-08-03
[status] => POSTED
[accountId] => 12331794
[runningBalance] => stdClass Object
(
[amount] => 480.66
[currency] => USD
)
[checkNumber] => 998
)
*/
class IncomeTransformer implements BankRevenueInterface
{
use AppSetup;
public function transform($transaction)
{
$data = [];
if(!property_exists($transaction, 'transaction'))
return $data;
foreach($transaction->transaction as $transaction)
{
$data[] = $this->transformTransaction($transaction);
}
return $data;
}
public function transformTransaction($transaction)
{
return [
'transaction_id' => $transaction->id,
'amount' => $transaction->amount->amount,
'currency_id' => $this->convertCurrency($transaction->amount->currency),
'account_type' => $transaction->CONTAINER,
'category_id' => $transaction->highLevelCategoryId,
'category_type' => $transaction->categoryType,
'date' => $transaction->date,
'bank_account_id' => $transaction->accountId,
'description' => $transaction->description->original,
'base_type' => property_exists($transaction, 'baseType') ? $transaction->baseType : $this->calculateBaseType($transaction),
];
}
private function calculateBaseType($transaction)
{
//CREDIT / DEBIT
if(property_exists($transaction, 'highLevelCategoryId') && $transaction->highLevelCategoryId == 10000012)
return 'CREDIT';
return 'DEBIT';
}
private function convertCurrency(string $code)
{
$currencies = Cache::get('currencies');
if (! $currencies) {
$this->buildCache(true);
}
$currency = $currencies->filter(function ($item) use($code){
return $item->code == $code;
})->first();
if($currency)
return $currency->id;
return 1;
}
}

View File

@ -0,0 +1,298 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\Bank\Yodlee;
use App\Exceptions\YodleeApiException;
use App\Helpers\Bank\Yodlee\Transformer\AccountTransformer;
use App\Helpers\Bank\Yodlee\Transformer\IncomeTransformer;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
class Yodlee
{
public bool $test_mode = false;
private string $api_endpoint = 'https://production.api.yodlee.com/ysl';
private string $dev_api_endpoint = 'https://sandbox.api.yodlee.com/ysl';
private string $test_api_endpoint = 'https://development.api.yodlee.com/ysl';
public string $dev_fast_track_url = 'https://fl4.sandbox.yodlee.com/authenticate/restserver/fastlink';
public string $test_fast_track_url = 'https://fl4.preprod.yodlee.com/authenticate/USDevexPreProd3-449/fastlink?channelAppName=usdevexpreprod3';
public string $production_track_url = 'https://fl4.prod.yodlee.com/authenticate/USDevexProd3-331/fastlink?channelAppName=usdevexprod3';
protected string $client_id;
protected string $client_secret;
protected string $admin_name;
protected ?string $bank_account_id;
public function __construct(?string $bank_account_id = null)
{
$this->bank_account_id = $bank_account_id;
$this->client_id = config('ninja.yodlee.client_id');
$this->client_secret = config('ninja.yodlee.client_secret');
$this->admin_name = config('ninja.yodlee.admin_name');
$this->test_mode = config('ninja.yodlee.test_mode');
config('ninja.yodlee.dev_mode') ? $this->setDevUrl() : null;
}
public function getFastTrackUrl()
{
if(config('ninja.yodlee.dev_mode'))
return $this->dev_fast_track_url;
return $this->test_mode ? $this->test_fast_track_url : $this->production_track_url;
}
public function setTestMode()
{
$this->test_mode = true;
return $this;
}
public function setDevUrl()
{
$this->test_api_endpoint = $this->dev_api_endpoint;
$this->api_endpoint = $this->dev_api_endpoint;
return $this;
}
public function getEndpoint()
{
return $this->test_mode ? $this->test_api_endpoint : $this->api_endpoint;
}
/**
* If we do not pass in a user
* we pass in the admin username instead
*/
public function getAccessToken($is_admin = false)
{
if($is_admin)
$user = $this->admin_name;
else
$user = $this->bank_account_id ?: $this->admin_name;
$response = $this->bankFormRequest('/auth/token', 'post', [], ['loginName' => $user]);
return $response->token->accessToken;
}
public function createUser($company)
{
$token = $this->getAccessToken(true);
$user['user'] = [
'loginName' => Str::uuid(),
];
/*
{
"user": {
"preferences": {
"dateFormat": "string",
"timeZone": "string",
"currency": "USD",
"locale": "en_US"
},
"address": {
"zip": "string",
"country": "string",
"address3": "string",
"address2": "string",
"city": "string",
"address1": "string",
"state": "string"
},
"loginName": "string",
"name": {
"middle": "string",
"last": "string",
"fullName": "string",
"first": "string"
},
"email": "string",
"segmentName": "string"
}
}
*/
$response = Http::withHeaders($this->getHeaders(["Authorization" => "Bearer {$token}"]))->post($this->getEndpoint(). "/user/register", $user);
if($response->successful())
return $response->object();
if($response->failed())
throw new YodleeApiException($response->body());
}
public function getAccounts($params = [])
{
$token = $this->getAccessToken();
$response = Http::withHeaders($this->getHeaders(["Authorization" => "Bearer {$token}"]))->get($this->getEndpoint(). "/accounts", $params);
if($response->successful()){
$at = new AccountTransformer();
return $at->transform($response->object());
}
if($response->failed())
throw new YodleeApiException($response->body());
}
public function getAccount($account_id)
{
$token = $this->getAccessToken();
$response = Http::withHeaders($this->getHeaders(["Authorization" => "Bearer {$token}"]))->get($this->getEndpoint(). "/accounts/{$account_id}", []);
if($response->successful())
return true;
if($response->failed())
return false;
}
public function deleteAccount($account_id)
{
$token = $this->getAccessToken();
$response = Http::withHeaders($this->getHeaders(["Authorization" => "Bearer {$token}"]))->delete($this->getEndpoint(). "/accounts/{$account_id}", []);
if($response->successful()){
return true;
}
if($response->failed())
throw new YodleeApiException($response->body());
}
public function getTransactions($params = [])
{
$token = $this->getAccessToken();
$response = Http::withHeaders($this->getHeaders(["Authorization" => "Bearer {$token}"]))->get($this->getEndpoint(). "/transactions", $params);
if($response->successful()){
// return $response->object();
$it = new IncomeTransformer();
return $it->transform($response->object());
}
if($response->failed())
throw new YodleeApiException($response->body());
}
public function getTransactionCount($params = [])
{
$token = $this->getAccessToken();
$response = Http::withHeaders($this->getHeaders(["Authorization" => "Bearer {$token}"]))->get($this->getEndpoint(). "/transactions/count", $params);
if($response->successful()){
return $response->object();
}
if($response->failed())
throw new YodleeApiException($response->body());
}
public function getTransactionCategories($params = [])
{
$token = $this->getAccessToken();
$response = Http::withHeaders($this->getHeaders(["Authorization" => "Bearer {$token}"]))->get($this->getEndpoint(). "/transactions/categories", $params);
if($response->successful())
return $response->object();
if($response->failed())
throw new YodleeApiException($response->body());
}
private function bankFormRequest(string $uri, string $verb, array $data, array $headers)
{
$response = Http::withHeaders($this->getFormHeaders($headers))->asForm()->{$verb}($this->getEndpoint() . $uri, $this->buildBody());
if($response->successful())
return $response->object();
if($response->failed())
throw new YodleeApiException($response->body());
}
private function getHeaders($data = [])
{
return array_merge($data, [
'Api-Version' => '1.1',
'ContentType' => 'application/json'
]);
}
private function getFormHeaders($data = [])
{
return array_merge($data, [
'Api-Version' => '1.1',
]);
}
private function buildBody()
{
return [
'clientId' => $this->client_id,
'secret' => $this->client_secret,
];
}
}

View File

@ -0,0 +1,99 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\Epc;
use App\Models\Company;
use App\Models\Invoice;
use App\Utils\Ninja;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
/**
* EpcQrGenerator.
*/
class EpcQrGenerator
{
private array $sepa = [
'serviceTag' => 'BCD',
'version' => 2,
'characterSet' => 1,
'identification' => 'SCT',
'bic' => '',
'purpose' => '',
];
public function __construct(protected Company $company, protected Invoice $invoice, protected float $amount){}
public function getQrCode()
{
$renderer = new ImageRenderer(
new RendererStyle(200),
new SvgImageBackEnd()
);
$writer = new Writer($renderer);
$this->validateFields();
try {
$qr = $writer->writeString($this->encodeMessage(), 'utf-8');
}
catch(\Throwable $e){
return '';
}
catch(\Exception $e){
return '';
}
return "<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";
}
public function encodeMessage()
{
return rtrim(implode("\n", array(
$this->sepa['serviceTag'],
sprintf('%03d', $this->sepa['version']),
$this->sepa['characterSet'],
$this->sepa['identification'],
isset($this->company?->custom_fields?->company2) ? $this->company->settings->custom_value2 : '',
$this->company->present()->name(),
isset($this->company?->custom_fields?->company1) ? $this->company->settings->custom_value1 : '',
$this->formatMoney($this->amount),
$this->sepa['purpose'],
substr($this->invoice->number,0,34),
'',
''
)), "\n");
}
private function validateFields()
{
if(Ninja::isSelfHost() && isset($this->company?->custom_fields?->company2))
nlog('The BIC field is not present and _may_ be a required fields for EPC QR codes');
if(Ninja::isSelfHost() && isset($this->company?->custom_fields?->company1))
nlog('The IBAN field is required');
}
private function formatMoney($value) {
return sprintf('EUR%s', number_format($value, 2, '.', ''));
}
}

View File

@ -40,4 +40,7 @@ function nlog($output, $context = []): void
} else {
\Illuminate\Support\Facades\Log::channel('invoiceninja')->info($output, $context);
}
$output = null;
$context = null;
}

View File

@ -30,6 +30,8 @@ class InvoiceItemSum
private $gross_line_total;
private $tax_amount;
private $currency;
private $total_taxes;
@ -111,14 +113,10 @@ class InvoiceItemSum
$this->setLineTotal($this->getLineTotal() - $this->formatValue($this->item->discount, $this->currency->precision));
} else {
/*Test 16-08-2021*/
$discount = ($this->item->line_total * ($this->item->discount / 100));
$this->setLineTotal($this->formatValue(($this->getLineTotal() - $discount), $this->currency->precision));
/*Test 16-08-2021*/
//replaces the following
// $this->setLineTotal($this->getLineTotal() - $this->formatValue(round($this->item->line_total * ($this->item->discount / 100), 2), $this->currency->precision));
}
$this->item->is_amount_discount = $this->invoice->is_amount_discount;
@ -160,6 +158,8 @@ class InvoiceItemSum
$this->item->gross_line_total = $this->getLineTotal() + $item_tax;
$this->item->tax_amount = $item_tax;
return $this;
}

View File

@ -40,6 +40,8 @@ class InvoiceItemSumInclusive
private $tax_collection;
private $tax_amount;
public function __construct($invoice)
{
$this->tax_collection = collect([]);
@ -144,6 +146,8 @@ class InvoiceItemSumInclusive
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
}
$this->item->tax_amount = $this->formatValue($item_tax, $this->currency->precision);
$this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision));
return $this;

View File

@ -19,7 +19,7 @@ use Sprain\SwissQrBill as QrBill;
/**
* SwissQrGenerator.
*/
class SwissQrGenerator
class SwissQrGenerator
{
protected Company $company;
@ -33,7 +33,7 @@ class SwissQrGenerator
$this->company = $company;
$this->invoice = $invoice;
$this->client = $invoice->client;
}
@ -87,10 +87,10 @@ class SwissQrGenerator
$qrBill->setUltimateDebtor(
QrBill\DataGroup\Element\StructuredAddress::createWithStreet(
substr($this->client->present()->name(), 0 , 70),
$this->client->address1 ? substr($this->client->address1, 0 , 70) : '',
$this->client->address2 ? substr($this->client->address2, 0 , 16) : '',
$this->client->postal_code ? substr($this->client->postal_code, 0, 16) : '',
$this->client->city ? substr($this->client->postal_code, 0, 35) : '',
$this->client->address1 ? substr($this->client->address1, 0 , 70) : '_',
$this->client->address2 ? substr($this->client->address2, 0 , 16) : '_',
$this->client->postal_code ? substr($this->client->postal_code, 0, 16) : '_',
$this->client->city ? substr($this->client->city, 0, 35) : '_',
'CH'
));
@ -104,15 +104,43 @@ class SwissQrGenerator
// Add payment reference
// This is what you will need to identify incoming payments.
if(stripos($this->invoice->number, "Live") === 0)
{
// we're currently in preview status. Let's give a dummy reference for now
$invoice_number = "123456789";
}
else
{
$tempInvoiceNumber = $this->invoice->number;
$tempInvoiceNumber = preg_replace('/[^A-Za-z0-9]/', '', $tempInvoiceNumber);
$tempInvoiceNumber = substr($tempInvoiceNumber, 1);
$calcInvoiceNumber = "";
$array = str_split($tempInvoiceNumber);
foreach($array as $char)
{
if (is_numeric($char))
{
//
}
else
{
if ($char)
{
$char = strtolower($char);
$char = ord($char) - 96;
}
else
{
return 0;
}
}
$calcInvoiceNumber .= $char;
}
$invoice_number = $calcInvoiceNumber;
if(stripos($this->invoice->number, "Live-") === 0)
{
// we're currently in preview status. Let's give a dummy reference for now
$invoice_number = "123456789";
}
else
{
$invoice_number = $this->invoice->number;
}
if(strlen($this->company->present()->besr_id()) > 1)
@ -141,7 +169,7 @@ class SwissQrGenerator
// Optionally, add some human-readable information about what the bill is for.
$qrBill->setAdditionalInformation(
QrBill\DataGroup\Element\AdditionalInformation::create(
$this->invoice->public_notes ?: ''
$this->invoice->public_notes ? substr($this->invoice->public_notes, 0, 139) : ctrans('texts.invoice_number_placeholder', ['invoice' => $this->invoice->number])
)
);
@ -149,7 +177,7 @@ class SwissQrGenerator
// Now get the QR code image and save it as a file.
try {
$output = new QrBill\PaymentPart\Output\HtmlOutput\HtmlOutput($qrBill, 'en');
$output = new QrBill\PaymentPart\Output\HtmlOutput\HtmlOutput($qrBill, $this->client->locale() ?: 'en');
$html = $output
->setPrintable(false)
@ -173,4 +201,4 @@ class SwissQrGenerator
}
}
}

View File

@ -108,6 +108,10 @@ class ActivityController extends BaseController
'credit' => $activity->credit ? $activity->credit : '',
'task' => $activity->task ? $activity->task : '',
'vendor' => $activity->vendor ? $activity->vendor : '',
'vendor_contact' => $activity->vendor_contact ? $activity->vendor_contact : '',
'purchase_order' => $activity->purchase_order ? $activity->purchase_order : '',
'subscription' => $activity->subscription ? $activity->subscription : '',
'vendor_contact' => $activity->vendor_contact ? $activity->vendor_contact : '',
];
return array_merge($arr, $activity->toArray());
@ -180,9 +184,7 @@ class ActivityController extends BaseController
} else {
$html_backup = file_get_contents(Storage::disk(config('filesystems.default'))->path($backup->filename));
}
} elseif ($backup && $backup->html_backup) { //db
$html_backup = $backup->html_backup;
} elseif (! $backup || ! $backup->html_backup) { //failed
} else { //failed
return response()->json(['message'=> ctrans('texts.no_backup_exists'), 'errors' => new stdClass], 404);
}

View File

@ -592,7 +592,10 @@ class LoginController extends BaseController
$google = new Google();
$user = $google->getTokenResponse(request()->input('id_token'));
if(request()->has('id_token'))
$user = $google->getTokenResponse(request()->input('id_token'));
else
return response()->json(['message' => 'Illegal request'], 403);
if (is_array($user)) {
$query = [

View File

@ -0,0 +1,305 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers\Bank;
use App\Helpers\Bank\Yodlee\Yodlee;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Yodlee\YodleeAuthRequest;
use App\Jobs\Bank\ProcessBankTransactions;
use App\Models\BankIntegration;
use Illuminate\Http\Request;
class YodleeController extends BaseController
{
public function auth(YodleeAuthRequest $request)
{
// create a user at this point
// use the one time token here to pull in the actual user
// store the user_account_id on the accounts table
$yodlee = new Yodlee();
$company = $request->getCompany();
//ensure user is enterprise!!
if($company->account->bank_integration_account_id){
$flow = 'edit';
$token = $company->account->bank_integration_account_id;
}
else{
$flow = 'add';
$response = $yodlee->createUser($company);
$token = $response->user->loginName;
$company->account->bank_integration_account_id = $token;
$company->push();
}
$yodlee = new Yodlee($token);
if($request->has('window_closed') && $request->input("window_closed") == "true")
$this->getAccounts($company, $token);
$data = [
'access_token' => $yodlee->getAccessToken(),
'fasttrack_url' => $yodlee->getFastTrackUrl(),
'config_name' => config('ninja.yodlee.config_name'),
'flow' => $flow,
'company' => $company,
'account' => $company->account,
'completed' => $request->has('window_closed') ? true : false,
];
return view('bank.yodlee.auth', $data);
}
private function getAccounts($company, $token)
{
$yodlee = new Yodlee($token);
$accounts = $yodlee->getAccounts();
foreach($accounts as $account)
{
if(!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', $company->id)->exists())
{
$bank_integration = new BankIntegration();
$bank_integration->company_id = $company->id;
$bank_integration->account_id = $company->account_id;
$bank_integration->user_id = $company->owner()->id;
$bank_integration->bank_account_id = $account['id'];
$bank_integration->bank_account_type = $account['account_type'];
$bank_integration->bank_account_name = $account['account_name'];
$bank_integration->bank_account_status = $account['account_status'];
$bank_integration->bank_account_number = $account['account_number'];
$bank_integration->provider_id = $account['provider_id'];
$bank_integration->provider_name = $account['provider_name'];
$bank_integration->nickname = $account['nickname'];
$bank_integration->balance = $account['current_balance'];
$bank_integration->currency = $account['account_currency'];
$bank_integration->from_date = now()->subYear();
$bank_integration->save();
}
}
$company->account->bank_integrations->each(function ($bank_integration) use ($company){
ProcessBankTransactions::dispatch($company->account->bank_integration_account_id, $bank_integration);
});
}
/**
* Process Yodlee Refresh Webhook.
*
*
* @OA\Post(
* path="/api/v1/yodlee/refresh",
* operationId="yodleeRefreshWebhook",
* tags={"yodlee"},
* summary="Processing webhooks from Yodlee",
* description="Notifies the system when a data point can be refreshed",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Credit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
/*
{
"event":{
"info":"REFRESH.PROCESS_COMPLETED",
"loginName":"fri21",
"data":{
"providerAccount":[
{
"id":10995860,
"providerId":16441,
"isManual":false,
"createdDate":"2017-12-22T05:47:35Z",
"aggregationSource":"USER",
"status":"SUCCESS",
"requestId":"NSyMGo+R4dktywIu3hBIkc3PgWA=",
"dataset":[
{
"name":"BASIC_AGG_DATA",
"additionalStatus":"AVAILABLE_DATA_RETRIEVED",
"updateEligibility":"ALLOW_UPDATE",
"lastUpdated":"2017-12-22T05:48:16Z",
"lastUpdateAttempt":"2017-12-22T05:48:16Z"
}
]
}
]
}
}
}*/
public function refreshWebhook(Request $request)
{
//we should ignore this one
nlog("yodlee refresh");
nlog($request->all());
return response()->json(['message' => 'Success'], 200);
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
/*
{
"event":{
"notificationId":"63c73475-4db5-49ef-8553-8303337ca7c3",
"info":"LATEST_BALANCE_UPDATES",
"loginName":"user1",
"data":{
"providerAccountId":658552,
"latestBalanceEvent":[
{
"accountId":12345,
"status":"SUCCESS"
},
{
"accountId":12346,
"status":"FAILED"
}
]
}
}
}
*/
public function balanceWebhook(Request $request)
{
nlog("yodlee refresh");
nlog($request->all());
return response()->json(['message' => 'Success'], 200);
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
/*
{
"event":{
"data":[
{
"autoRefresh":{
"additionalStatus":"SCHEDULED",
"status":"ENABLED"
},
"accountIds":[
1112645899,
1112645898
],
"loginName":"YSL1555332811628",
"providerAccountId":11381459
}
],
"notificationTime":"2019-06-14T04:49:39Z",
"notificationId":"4e672150-156048777",
"info":"AUTO_REFRESH_UPDATES"
}
}
*/
public function refreshUpdatesWebhook(Request $request)
{
//notifies a user if there are problems with yodlee accessing the data
nlog("update refresh");
nlog($request->all());
return response()->json(['message' => 'Success'], 200);
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
/*
"event": {
"notificationId": "64b7ed1a-1530523285",
"info": "DATA_UPDATES.USER_DATA",
"data": {
"userCount": 1,
"fromDate": "2017-11-10T10:18:44Z",
"toDate": "2017-11-10T11:18:43Z",
"userData": [{
"user": {
"loginName": "YSL1484052178554"
},
"links": [{
"methodType": "GET",
"rel": "getUserData",
"href": "dataExtracts/userData?fromDate=2017-11-10T10:18:44Z&toDate=2017-11-10T11:18:43Z&loginName=YSL1484052178554"
}]
}]
}
}
*/
public function dataUpdatesWebhook(Request $request)
{
//this is the main hook we use for notifications
nlog("data refresh");
nlog($request->all());
return response()->json(['message' => 'Success'], 200);
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
}

View File

@ -0,0 +1,697 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers;
use App\Factory\BankIntegrationFactory;
use App\Filters\BankIntegrationFilters;
use App\Helpers\Bank\Yodlee\Yodlee;
use App\Http\Requests\BankIntegration\AdminBankIntegrationRequest;
use App\Http\Requests\BankIntegration\CreateBankIntegrationRequest;
use App\Http\Requests\BankIntegration\DestroyBankIntegrationRequest;
use App\Http\Requests\BankIntegration\EditBankIntegrationRequest;
use App\Http\Requests\BankIntegration\ShowBankIntegrationRequest;
use App\Http\Requests\BankIntegration\StoreBankIntegrationRequest;
use App\Http\Requests\BankIntegration\UpdateBankIntegrationRequest;
use App\Jobs\Bank\ProcessBankTransactions;
use App\Models\BankIntegration;
use App\Repositories\BankIntegrationRepository;
use App\Services\Bank\BankMatchingService;
use App\Transformers\BankIntegrationTransformer;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class BankIntegrationController extends BaseController
{
use MakesHash;
protected $entity_type = BankIntegration::class;
protected $entity_transformer = BankIntegrationTransformer::class;
protected $bank_integration_repo;
public function __construct(BankIntegrationRepository $bank_integration_repo)
{
parent::__construct();
$this->bank_integration_repo = $bank_integration_repo;
}
/**
* @OA\Get(
* path="/api/v1/bank_integrations",
* operationId="getBankIntegrations",
* tags={"bank_integrations"},
* summary="Gets a list of bank_integrations",
* description="Lists all bank integrations",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\Parameter(
* name="rows",
* in="query",
* description="The number of bank integrations to return",
* example="50",
* required=false,
* @OA\Schema(
* type="number",
* format="integer",
* ),
* ),
* @OA\Response(
* response=200,
* description="A list of bank integrations",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
* @param Request $request
* @return Response|mixed
*/
public function index(BankIntegrationFilters $filters)
{
$bank_integrations = BankIntegration::filter($filters);
return $this->listResponse($bank_integrations);
}
/**
* Display the specified resource.
*
* @param ShowBankIntegrationRequest $request
* @param BankIntegration $bank_integration
* @return Response
*
*
* @OA\Get(
* path="/api/v1/bank_integrations/{id}",
* operationId="showBankIntegration",
* tags={"bank_integrations"},
* summary="Shows a bank_integration",
* description="Displays a bank_integration by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The BankIntegration Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the bank_integration object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function show(ShowBankIntegrationRequest $request, BankIntegration $bank_integration)
{
return $this->itemResponse($bank_integration);
}
/**
* Show the form for editing the specified resource.
*
* @param EditBankIntegrationRequest $request
* @param BankIntegration $bank_integration
* @return Response
*
*
* @OA\Get(
* path="/api/v1/bank_integrations/{id}/edit",
* operationId="editBankIntegration",
* tags={"bank_integrations"},
* summary="Shows a bank_integration for editing",
* description="Displays a bank_integration by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The BankIntegration Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the bank_integration object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function edit(EditBankIntegrationRequest $request, BankIntegration $bank_integration)
{
return $this->itemResponse($bank_integration);
}
/**
* Update the specified resource in storage.
*
* @param UpdateBankIntegrationRequest $request
* @param BankIntegration $bank_integration
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/bank_integrations/{id}",
* operationId="updateBankIntegration",
* tags={"bank_integrations"},
* summary="Updates a bank_integration",
* description="Handles the updating of a bank_integration by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The BankIntegration Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the bank_integration object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function update(UpdateBankIntegrationRequest $request, BankIntegration $bank_integration)
{
//stubs for updating the model
$bank_integration = $this->bank_integration_repo->save($request->all(), $bank_integration);
return $this->itemResponse($bank_integration->fresh());
}
/**
* Show the form for creating a new resource.
*
* @param CreateBankIntegrationRequest $request
* @return Response
*
*
*
* @OA\Get(
* path="/api/v1/bank_integrations/create",
* operationId="getBankIntegrationsCreate",
* tags={"bank_integrations"},
* summary="Gets a new blank bank_integration object",
* description="Returns a blank object with default values",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="A blank bank_integration object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function create(CreateBankIntegrationRequest $request)
{
$bank_integration = BankIntegrationFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id);
return $this->itemResponse($bank_integration);
}
/**
* Store a newly created resource in storage.
*
* @param StoreBankIntegrationRequest $request
* @return Response
*
*
*
* @OA\Post(
* path="/api/v1/bank_integrations",
* operationId="storeBankIntegration",
* tags={"bank_integrations"},
* summary="Adds a bank_integration",
* description="Adds an bank_integration to a company",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="Returns the saved bank_integration object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function store(StoreBankIntegrationRequest $request)
{
//stub to store the model
$bank_integration = $this->bank_integration_repo->save($request->all(), BankIntegrationFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id));
return $this->itemResponse($bank_integration);
}
/**
* Remove the specified resource from storage.
*
* @param DestroyBankIntegrationRequest $request
* @param BankIntegration $bank_integration
* @return Response
*
*
* @throws \Exception
* @OA\Delete(
* path="/api/v1/bank_integrations/{id}",
* operationId="deleteBankIntegration",
* tags={"bank_integrations"},
* summary="Deletes a bank_integration",
* description="Handles the deletion of a bank_integration by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The BankIntegration Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns a HTTP status",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function destroy(DestroyBankIntegrationRequest $request, BankIntegration $bank_integration)
{
$this->bank_integration_repo->delete($bank_integration);
return $this->itemResponse($bank_integration->fresh());
}
/**
* Perform bulk actions on the list view.
*
* @return Collection
*
* @OA\Post(
* path="/api/v1/bank_integrations/bulk",
* operationId="bulkBankIntegrations",
* tags={"bank_integrations"},
* summary="Performs bulk actions on an array of bank_integrations",
* description="",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\RequestBody(
* description="Action paramters",
* required=true,
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* type="array",
* @OA\Items(
* type="integer",
* description="Array of hashed IDs to be bulk 'actioned",
* example="[0,1,2,3]",
* ),
* )
* )
* ),
* @OA\Response(
* response=200,
* description="The Bulk Action response",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function bulk()
{
$action = request()->input('action');
if(!in_array($action, ['archive', 'restore', 'delete']))
return response()->json(['message' => 'Unsupported action.'], 400);
$ids = request()->input('ids');
$bank_integrations = BankIntegration::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
$bank_integrations->each(function ($bank_integration, $key) use ($action) {
if (auth()->user()->can('edit', $bank_integration)) {
$this->bank_integration_repo->{$action}($bank_integration);
}
});
/* Need to understand which permission are required for the given bulk action ie. view / edit */
return $this->listResponse(BankIntegration::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
}
/**
* Return the remote list of accounts stored on the third party provider.
*
* @return Response
*
* @OA\Post(
* path="/api/v1/bank_integrations/refresh_accounts",
* operationId="getRefreshAccounts",
* tags={"bank_integrations"},
* summary="Gets the list of accounts from the remote server",
* description="Adds an bank_integration to a company",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="Returns the saved bank_integration object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function refreshAccounts(AdminBankIntegrationRequest $request)
{
// As yodlee is the first integration we don't need to perform switches yet, however
// if we add additional providers we can reuse this class
$bank_account_id = auth()->user()->account->bank_integration_account_id;
if(!$bank_account_id)
return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400);
$yodlee = new Yodlee($bank_account_id);
$accounts = $yodlee->getAccounts();
foreach($accounts as $account)
{
if(!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', auth()->user()->company()->id)->exists())
{
$bank_integration = new BankIntegration();
$bank_integration->company_id = auth()->user()->company()->id;
$bank_integration->account_id = auth()->user()->account_id;
$bank_integration->user_id = auth()->user()->id;
$bank_integration->bank_account_id = $account['id'];
$bank_integration->bank_account_type = $account['account_type'];
$bank_integration->bank_account_name = $account['account_name'];
$bank_integration->bank_account_status = $account['account_status'];
$bank_integration->bank_account_number = $account['account_number'];
$bank_integration->provider_id = $account['provider_id'];
$bank_integration->provider_name = $account['provider_name'];
$bank_integration->nickname = $account['nickname'];
$bank_integration->balance = $account['current_balance'];
$bank_integration->currency = $account['account_currency'];
$bank_integration->save();
}
}
$account = auth()->user()->account;
if(Cache::get("throttle_polling:{$account->key}"))
return response()->json(BankIntegration::query()->company(), 200);
$account->bank_integrations->each(function ($bank_integration) use ($account){
ProcessBankTransactions::dispatch($account->bank_integration_account_id, $bank_integration);
});
Cache::put("throttle_polling:{$account->key}", true, 300);
return response()->json(BankIntegration::query()->company(), 200);
}
/**
* Return the remote list of accounts stored on the third party provider
* and update our local cache.
*
* @return Response
*
* @OA\Post(
* path="/api/v1/bank_integrations/remove_account/account_id",
* operationId="getRemoveAccount",
* tags={"bank_integrations"},
* summary="Removes an account from the integration",
* description="Removes an account from the integration",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="Returns the bank_integration object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function removeAccount(AdminBankIntegrationRequest $request, $acc_id)
{
$bank_account_id = auth()->user()->account->bank_integration_account_id;
if(!$bank_account_id)
return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400);
$bi = BankIntegration::withTrashed()->where('bank_account_id', $acc_id)->where('company_id', auth()->user()->company()->id)->firstOrFail();
$yodlee = new Yodlee($bank_account_id);
$res = $yodlee->deleteAccount($acc_id);
$this->bank_integration_repo->delete($bi);
return $this->itemResponse($bi->fresh());
}
/**
* Return the remote list of accounts stored on the third party provider
* and update our local cache.
*
* @return Response
*
* @OA\Post(
* path="/api/v1/bank_integrations/get_transactions/account_id",
* operationId="getAccountTransactions",
* tags={"bank_integrations"},
* summary="Retrieve transactions for a account",
* description="Retrieve transactions for a account",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="Retrieve transactions for a account",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankIntegration"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function getTransactions(AdminBankIntegrationRequest $request)
{
auth()->user()->account->bank_integrations->each(function ($bank_integration) {
ProcessBankTransactions::dispatchSync(auth()->user()->account->bank_integration_account_id, $bank_integration);
});
return response()->json(['message' => 'Fetching transactions....'], 200);
}
}

View File

@ -0,0 +1,577 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers;
use App\Factory\BankTransactionFactory;
use App\Filters\BankTransactionFilters;
use App\Helpers\Bank\Yodlee\Yodlee;
use App\Http\Requests\BankTransaction\AdminBankTransactionRequest;
use App\Http\Requests\BankTransaction\CreateBankTransactionRequest;
use App\Http\Requests\BankTransaction\DestroyBankTransactionRequest;
use App\Http\Requests\BankTransaction\EditBankTransactionRequest;
use App\Http\Requests\BankTransaction\ImportBankTransactionsRequest;
use App\Http\Requests\BankTransaction\MatchBankTransactionRequest;
use App\Http\Requests\BankTransaction\ShowBankTransactionRequest;
use App\Http\Requests\BankTransaction\StoreBankTransactionRequest;
use App\Http\Requests\BankTransaction\UpdateBankTransactionRequest;
use App\Http\Requests\Import\PreImportRequest;
use App\Jobs\Bank\MatchBankTransactions;
use App\Models\BankTransaction;
use App\Repositories\BankTransactionRepository;
use App\Services\Bank\BankMatchingService;
use App\Transformers\BankTransactionTransformer;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class BankTransactionController extends BaseController
{
use MakesHash;
protected $entity_type = BankTransaction::class;
protected $entity_transformer = BankTransactionTransformer::class;
protected $bank_transaction_repo;
public function __construct(BankTransactionRepository $bank_transaction_repo)
{
parent::__construct();
$this->bank_transaction_repo = $bank_transaction_repo;
}
/**
* @OA\Get(
* path="/api/v1/bank_transactions",
* operationId="getBankTransactions",
* tags={"bank_transactions"},
* summary="Gets a list of bank_transactions",
* description="Lists all bank integrations",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\Parameter(
* name="rows",
* in="query",
* description="The number of bank integrations to return",
* example="50",
* required=false,
* @OA\Schema(
* type="number",
* format="integer",
* ),
* ),
* @OA\Response(
* response=200,
* description="A list of bank integrations",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransaction"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
* @param BankTransactionFilters $filter
* @return Response|mixed
*/
public function index(BankTransactionFilters $filters)
{
$bank_transactions = BankTransaction::filter($filters);
return $this->listResponse($bank_transactions);
}
/**
* Display the specified resource.
*
* @param ShowBankTransactionRequest $request
* @param BankTransaction $bank_transaction
* @return Response
*
*
* @OA\Get(
* path="/api/v1/bank_transactions/{id}",
* operationId="showBankTransaction",
* tags={"bank_transactions"},
* summary="Shows a bank_transaction",
* description="Displays a bank_transaction by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The BankTransaction Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the bank_transaction object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransaction"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function show(ShowBankTransactionRequest $request, BankTransaction $bank_transaction)
{
return $this->itemResponse($bank_transaction);
}
/**
* Show the form for editing the specified resource.
*
* @param EditBankTransactionRequest $request
* @param BankTransaction $bank_transaction
* @return Response
*
*
* @OA\Get(
* path="/api/v1/bank_transactions/{id}/edit",
* operationId="editBankTransaction",
* tags={"bank_transactions"},
* summary="Shows a bank_transaction for editing",
* description="Displays a bank_transaction by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The BankTransaction Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the bank_transaction object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransaction"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function edit(EditBankTransactionRequest $request, BankTransaction $bank_transaction)
{
return $this->itemResponse($bank_transaction);
}
/**
* Update the specified resource in storage.
*
* @param UpdateBankTransactionRequest $request
* @param BankTransaction $bank_transaction
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/bank_transactions/{id}",
* operationId="updateBankTransaction",
* tags={"bank_transactions"},
* summary="Updates a bank_transaction",
* description="Handles the updating of a bank_transaction by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The BankTransaction Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the bank_transaction object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransaction"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function update(UpdateBankTransactionRequest $request, BankTransaction $bank_transaction)
{
//stubs for updating the model
$bank_transaction = $this->bank_transaction_repo->save($request->all(), $bank_transaction);
return $this->itemResponse($bank_transaction->fresh());
}
/**
* Show the form for creating a new resource.
*
* @param CreateBankTransactionRequest $request
* @return Response
*
*
*
* @OA\Get(
* path="/api/v1/bank_transactions/create",
* operationId="getBankTransactionsCreate",
* tags={"bank_transactions"},
* summary="Gets a new blank bank_transaction object",
* description="Returns a blank object with default values",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="A blank bank_transaction object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransaction"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function create(CreateBankTransactionRequest $request)
{
$bank_transaction = BankTransactionFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id);
return $this->itemResponse($bank_transaction);
}
/**
* Store a newly created resource in storage.
*
* @param StoreBankTransactionRequest $request
* @return Response
*
*
*
* @OA\Post(
* path="/api/v1/bank_transactions",
* operationId="storeBankTransaction",
* tags={"bank_transactions"},
* summary="Adds a bank_transaction",
* description="Adds an bank_transaction to a company",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="Returns the saved bank_transaction object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransaction"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function store(StoreBankTransactionRequest $request)
{
//stub to store the model
$bank_transaction = $this->bank_transaction_repo->save($request->all(), BankTransactionFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id));
return $this->itemResponse($bank_transaction);
}
/**
* Remove the specified resource from storage.
*
* @param DestroyBankTransactionRequest $request
* @param BankTransaction $bank_transaction
* @return Response
*
*
* @throws \Exception
* @OA\Delete(
* path="/api/v1/bank_transactions/{id}",
* operationId="deleteBankTransaction",
* tags={"bank_transactions"},
* summary="Deletes a bank_transaction",
* description="Handles the deletion of a bank_transaction by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The BankTransaction Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns a HTTP status",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function destroy(DestroyBankTransactionRequest $request, BankTransaction $bank_transaction)
{
$this->bank_transaction_repo->delete($bank_transaction);
return $this->itemResponse($bank_transaction->fresh());
}
/**
* Perform bulk actions on the list view.
*
* @return Collection
*
* @OA\Post(
* path="/api/v1/bank_transations/bulk",
* operationId="bulkBankTransactions",
* tags={"bank_transactions"},
* summary="Performs bulk actions on an array of bank_transations",
* description="",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\RequestBody(
* description="Action paramters",
* required=true,
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* type="array",
* @OA\Items(
* type="integer",
* description="Array of hashed IDs to be bulk 'actioned",
* example="[0,1,2,3]",
* ),
* )
* )
* ),
* @OA\Response(
* response=200,
* description="The Bulk Action response",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function bulk()
{
$action = request()->input('action');
if(!in_array($action, ['archive', 'restore', 'delete', 'convert_matched']))
return response()->json(['message' => 'Unsupported action.'], 400);
$ids = request()->input('ids');
$bank_transactions = BankTransaction::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if($action == 'convert_matched') //catch this action
{
if(auth()->user()->isAdmin())
{
$this->bank_transaction_repo->convert_matched($bank_transactions);
}
else
return;
}
else {
$bank_transactions->each(function ($bank_transaction, $key) use ($action) {
if (auth()->user()->can('edit', $bank_transaction)) {
$this->bank_transaction_repo->{$action}($bank_transaction);
}
});
}
/* Need to understand which permission are required for the given bulk action ie. view / edit */
return $this->listResponse(BankTransaction::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
}
/**
* Perform bulk actions on the list view.
*
* @return Collection
*
* @OA\Post(
* path="/api/v1/bank_transations/match",
* operationId="matchBankTransactions",
* tags={"bank_transactions"},
* summary="Performs match actions on an array of bank_transactions",
* description="",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\RequestBody(
* description="Action paramters",
* required=true,
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* type="array",
* @OA\Items(
* type="integer",
* description="Array of hashed IDs to be bulk 'actioned",
* example="[0,1,2,3]",
* ),
* )
* )
* ),
* @OA\Response(
* response=200,
* description="The Bulk Action response",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function match(MatchBankTransactionRequest $request)
{
// MatchBankTransactions::dispatch(auth()->user()->company()->id, auth()->user()->company()->db, $request->all());
$bts = (new MatchBankTransactions(auth()->user()->company()->id, auth()->user()->company()->db, $request->all()))->handle();
return $this->listResponse($bts);
}
}

View File

@ -0,0 +1,505 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers;
use App\Factory\BankTransactionFactory;
use App\Factory\BankTransactionRuleFactory;
use App\Filters\BankTransactionFilters;
use App\Filters\BankTransactionRuleFilters;
use App\Helpers\Bank\Yodlee\Yodlee;
use App\Http\Requests\BankTransactionRule\CreateBankTransactionRuleRequest;
use App\Http\Requests\BankTransactionRule\DestroyBankTransactionRuleRequest;
use App\Http\Requests\BankTransactionRule\EditBankTransactionRuleRequest;
use App\Http\Requests\BankTransactionRule\ShowBankTransactionRuleRequest;
use App\Http\Requests\BankTransactionRule\StoreBankTransactionRuleRequest;
use App\Http\Requests\BankTransactionRule\UpdateBankTransactionRuleRequest;
use App\Http\Requests\BankTransaction\AdminBankTransactionRuleRequest;
use App\Http\Requests\Import\PreImportRequest;
use App\Jobs\Bank\MatchBankTransactionRules;
use App\Models\BankTransaction;
use App\Models\BankTransactionRule;
use App\Repositories\BankTransactionRepository;
use App\Repositories\BankTransactionRuleRepository;
use App\Services\Bank\BankMatchingService;
use App\Transformers\BankTransactionRuleTransformer;
use App\Transformers\BankTransactionTransformer;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class BankTransactionRuleController extends BaseController
{
use MakesHash;
protected $entity_type = BankTransactionRule::class;
protected $entity_transformer = BankTransactionRuleTransformer::class;
protected $bank_transaction_repo;
public function __construct(BankTransactionRuleRepository $bank_transaction_repo)
{
parent::__construct();
$this->bank_transaction_repo = $bank_transaction_repo;
}
/**
* @OA\Get(
* path="/api/v1/bank_transaction_rules",
* operationId="getBankTransactionRules",
* tags={"bank_transaction_rules"},
* summary="Gets a list of bank_transaction_rules",
* description="Lists all bank transaction rules",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\Parameter(
* name="rows",
* in="query",
* description="The number of bank integrations to return",
* example="50",
* required=false,
* @OA\Schema(
* type="number",
* format="integer",
* ),
* ),
* @OA\Response(
* response=200,
* description="A list of bank integrations",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
* @param BankTransactionFilters $filter
* @return Response|mixed
*/
public function index(BankTransactionRuleFilters $filters)
{
$bank_transaction_rules = BankTransactionRule::filter($filters);
return $this->listResponse($bank_transaction_rules);
}
/**
* Display the specified resource.
*
* @param ShowBankTransactionRuleRequest $request
* @param BankTransactionRule $bank_transaction_rule
* @return Response
*
*
* @OA\Get(
* path="/api/v1/bank_transaction_rules/{id}",
* operationId="showBankTransactionRule",
* tags={"bank_transaction_rules"},
* summary="Shows a bank_transaction",
* description="Displays a bank_transaction by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Bank Transaction RuleHashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the bank_transaction rule object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function show(ShowBankTransactionRuleRequest $request, BankTransactionRule $bank_transaction_rule)
{
return $this->itemResponse($bank_transaction_rule);
}
/**
* Show the form for editing the specified resource.
*
* @param EditBankTransactionRuleRequest $request
* @param BankTransactionRule $bank_transaction_rule
* @return Response
*
*
* @OA\Get(
* path="/api/v1/bank_transaction_rules/{id}/edit",
* operationId="editBankTransactionRule",
* tags={"bank_transaction_rules"},
* summary="Shows a bank_transaction for editing",
* description="Displays a bank_transaction by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Bank Transaction Rule Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the bank_transaction rule object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function edit(EditBankTransactionRuleRequest $request, BankTransactionRule $bank_transaction_rule)
{
return $this->itemResponse($bank_transaction_rule);
}
/**
* Update the specified resource in storage.
*
* @param UpdateBankTransactionRuleRequest $request
* @param BankTransactionRule $bank_transaction_rule
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/bank_transaction_rules/{id}",
* operationId="updateBankTransactionRule",
* tags={"bank_transaction_rules"},
* summary="Updates a bank_transaction Rule",
* description="Handles the updating of a bank_transaction rule by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Bank Transaction Rule Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the bank_transaction rule object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function update(UpdateBankTransactionRuleRequest $request, BankTransactionRule $bank_transaction_rule)
{
//stubs for updating the model
$bank_transaction = $this->bank_transaction_repo->save($request->all(), $bank_transaction_rule);
return $this->itemResponse($bank_transaction_rule->fresh());
}
/**
* Show the form for creating a new resource.
*
* @param CreateBankTransactionRuleRequest $request
* @return Response
*
*
*
* @OA\Get(
* path="/api/v1/bank_transaction_rules/create",
* operationId="getBankTransactionRulesCreate",
* tags={"bank_transaction_rules"},
* summary="Gets a new blank bank_transaction rule object",
* description="Returns a blank object with default values",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="A blank bank_transaction rule object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function create(CreateBankTransactionRuleRequest $request)
{
$bank_transaction_rule = BankTransactionRuleFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id);
return $this->itemResponse($bank_transaction_rule);
}
/**
* Store a newly created resource in storage.
*
* @param StoreBankTransactionRuleRequest $request
* @return Response
*
*
*
* @OA\Post(
* path="/api/v1/bank_transaction_rules",
* operationId="storeBankTransaction",
* tags={"bank_transaction_rules"},
* summary="Adds a bank_transaction rule",
* description="Adds an bank_transaction to a company",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="Returns the saved bank_transaction rule object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/BankTransactionRule"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function store(StoreBankTransactionRuleRequest $request)
{
//stub to store the model
$bank_transaction_rule = $this->bank_transaction_repo->save($request->all(), BankTransactionRuleFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id));
return $this->itemResponse($bank_transaction_rule);
}
/**
* Remove the specified resource from storage.
*
* @param DestroyBankTransactionRuleRequest $request
* @param BankTransactionRule $bank_transaction_rule
* @return Response
*
*
* @throws \Exception
* @OA\Delete(
* path="/api/v1/bank_transaction_rules/{id}",
* operationId="deleteBankTransactionRule",
* tags={"bank_transaction_rules"},
* summary="Deletes a bank_transaction rule",
* description="Handles the deletion of a bank_transaction rule by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Bank Transaction Rule Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns a HTTP status",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function destroy(DestroyBankTransactionRuleRequest $request, BankTransactionRule $bank_transaction_rule)
{
$this->bank_transaction_repo->delete($bank_transaction_rule);
return $this->itemResponse($bank_transaction_rule->fresh());
}
/**
* Perform bulk actions on the list view.
*
* @return Collection
*
* @OA\Post(
* path="/api/v1/bank_transation_rules/bulk",
* operationId="bulkBankTransactionRules",
* tags={"bank_transaction_rules"},
* summary="Performs bulk actions on an array of bank_transation rules",
* description="",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\RequestBody(
* description="Action paramters",
* required=true,
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* type="array",
* @OA\Items(
* type="integer",
* description="Array of hashed IDs to be bulk 'actioned",
* example="[0,1,2,3]",
* ),
* )
* )
* ),
* @OA\Response(
* response=200,
* description="The Bulk Action response",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function bulk()
{
$action = request()->input('action');
if(!in_array($action, ['archive', 'restore', 'delete']))
return response()->json(['message' => 'Unsupported action.'], 400);
$ids = request()->input('ids');
$bank_transaction_rules = BankTransactionRule::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
$bank_transaction_rules->each(function ($bank_transaction_rule, $key) use ($action) {
if (auth()->user()->can('edit', $bank_transaction_rule)) {
$this->bank_transaction_repo->{$action}($bank_transaction_rule);
}
});
/* Need to understand which permission are required for the given bulk action ie. view / edit */
return $this->listResponse(BankTransactionRule::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
}
}

View File

@ -12,6 +12,7 @@
namespace App\Http\Controllers;
use App\Models\Account;
use App\Models\BankTransaction;
use App\Models\Company;
use App\Models\User;
use App\Transformers\ArraySerializer;
@ -105,6 +106,9 @@ class BaseController extends Controller
'company.vendors.documents',
'company.webhooks',
'company.system_logs',
'company.bank_integrations',
'company.bank_transactions',
'company.bank_transaction_rules',
];
private $mini_load = [
@ -122,6 +126,8 @@ class BaseController extends Controller
'company.designs.company',
'company.expense_categories',
'company.subscriptions',
'company.bank_integrations',
'company.bank_transaction_rules',
];
public function __construct()
@ -438,11 +444,33 @@ class BaseController extends Controller
$query->where('subscriptions.user_id', $user->id);
}
},
'company.bank_integrations'=> function ($query) use ($updated_at, $user) {
$query->whereNotNull('updated_at');
if (! $user->isAdmin()) {
$query->where('bank_integrations.user_id', $user->id);
}
},
'company.bank_transactions'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
if (! $user->isAdmin()) {
$query->where('bank_transactions.user_id', $user->id);
}
},
'company.bank_transaction_rules'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
if (! $user->isAdmin()) {
$query->where('bank_transaction_rules.user_id', $user->id);
}
},
]
);
if ($query instanceof Builder) {
$limit = request()->input('per_page', 20);
//27-10-2022 - enforce unsigned integer
$limit = $this->resolveQueryLimit();
$paginator = $query->paginate($limit);
$query = $paginator->getCollection();
@ -455,6 +483,14 @@ class BaseController extends Controller
return $this->response($this->manager->createData($resource)->toArray());
}
private function resolveQueryLimit()
{
if(request()->has('per_page'))
return abs((int)request()->input('per_page', 20));
return 20;
}
protected function miniLoadResponse($query)
{
$user = auth()->user();
@ -497,11 +533,23 @@ class BaseController extends Controller
$query->where('activities.user_id', $user->id);
}
},
'company.bank_integrations'=> function ($query) use ($created_at, $user) {
if (! $user->isAdmin()) {
$query->where('bank_integrations.user_id', $user->id);
}
},
'company.bank_transaction_rules'=> function ($query) use ($user) {
if (! $user->isAdmin()) {
$query->where('bank_transaction_rules.user_id', $user->id);
}
},
]
);
if ($query instanceof Builder) {
$limit = request()->input('per_page', 20);
$limit = $this->resolveQueryLimit();
$paginator = $query->paginate($limit);
$query = $paginator->getCollection();
@ -741,11 +789,25 @@ class BaseController extends Controller
}
},
'company.bank_integrations'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if (! $user->isAdmin()) {
$query->where('bank_integrations.user_id', $user->id);
}
},
'company.bank_transactions'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
if (! $user->isAdmin()) {
$query->where('bank_transactions.user_id', $user->id);
}
},
]
);
if ($query instanceof Builder) {
$limit = request()->input('per_page', 20);
$limit = $this->resolveQueryLimit();
$paginator = $query->paginate($limit);
$query = $paginator->getCollection();
@ -773,8 +835,19 @@ class BaseController extends Controller
// 10-01-2022 need to ensure we snake case properly here to ensure permissions work as expected
// 28-03-2022 this is definitely correct here, do not append _ to the view, it resolved correctly when snake cased
if (auth()->user() && ! auth()->user()->hasPermission('view'.lcfirst(class_basename(Str::snake($this->entity_type))))) {
//03-09-2022
$query->where('user_id', '=', auth()->user()->id)->orWhere('assigned_user_id', auth()->user()->id);
//06-10-2022 - some entities do not have assigned_user_id - this becomes an issue when we have a large company and low permission users
if(lcfirst(class_basename(Str::snake($this->entity_type))) == 'user')
$query->where('id', auth()->user()->id);
elseif($this->entity_type == BankTransaction::class){ //table without assigned_user_id
$query->where('user_id', '=', auth()->user()->id);
}
elseif(in_array(lcfirst(class_basename(Str::snake($this->entity_type))),['design','group_setting','payment_term'])){
//need to pass these back regardless
nlog($this->entity_type);
}
else
$query->where('user_id', '=', auth()->user()->id)->orWhere('assigned_user_id', auth()->user()->id);
}
if (request()->has('updated_at') && request()->input('updated_at') > 0) {
@ -786,7 +859,7 @@ class BaseController extends Controller
}
if ($query instanceof Builder) {
$limit = request()->input('per_page', 20);
$limit = $this->resolveQueryLimit();
$paginator = $query->paginate($limit);
$query = $paginator->getCollection();
$resource = new Collection($query, $transformer, $this->entity_type);

View File

@ -640,7 +640,14 @@ class ClientController extends BaseController
{
//delete all documents
$client->documents->each(function ($document) {
Storage::disk(config('filesystems.default'))->delete($document->url);
try{
Storage::disk(config('filesystems.default'))->delete($document->url);
}
catch(\Exception $e){
nlog($e->getMessage());
}
});
//force delete the client
@ -662,7 +669,7 @@ class ClientController extends BaseController
*
*
* @OA\Post(
* path="/api/v1/clients/{id}/{mergaeble_client_hashed_id}/merge",
* path="/api/v1/clients/{id}/{mergeable_client_hashed_id}/merge",
* operationId="mergeClient",
* tags={"clients"},
* summary="Merges two clients",
@ -683,7 +690,7 @@ class ClientController extends BaseController
* ),
* ),
* @OA\Parameter(
* name="mergeable_client_hashedid",
* name="mergeable_client_hashed_id",
* in="path",
* description="The Mergeable Client Hashed ID",
* example="D2J234DFA",

View File

@ -56,8 +56,6 @@ class InvoiceController extends Controller
{
set_time_limit(0);
// $invoice->service()->removeUnpaidGatewayFees()->save();
$invitation = $invoice->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
if ($invitation && auth()->guard('contact') && ! session()->get('is_silent') && ! $invitation->viewed_date) {

View File

@ -41,6 +41,25 @@ class SubscriptionPurchaseController extends Controller
]);
}
public function upgrade(Subscription $subscription, Request $request)
{
/* Make sure the contact is logged into the correct company for this subscription */
if (auth()->guard('contact')->user() && auth()->guard('contact')->user()->company_id != $subscription->company_id) {
auth()->guard('contact')->logout();
$request->session()->invalidate();
}
if ($request->has('locale')) {
$this->setLocale($request->query('locale'));
}
return view('billing-portal.purchasev2', [
'subscription' => $subscription,
'hash' => Str::uuid()->toString(),
'request_data' => $request->all(),
]);
}
/**
* Set locale for incoming request.
*
@ -56,4 +75,7 @@ class SubscriptionPurchaseController extends Controller
App::setLocale($record->locale);
}
}
}

View File

@ -164,7 +164,8 @@ class CompanyController extends BaseController
*/
public function create(CreateCompanyRequest $request)
{
$company = CompanyFactory::create(auth()->user()->company()->account->id);
$cf = new \App\Factory\CompanyFactory;
$company = $cf->create(auth()->user()->company()->account->id);
return $this->itemResponse($company);
}

View File

@ -110,6 +110,9 @@ class ConnectedAccountController extends BaseController
$email = $user->getMail() ?: $user->getUserPrincipalName();
nlog("microsoft");
nlog($email);
if(auth()->user()->email != $email && MultiDB::checkUserEmailExists($email))
return response()->json(['message' => ctrans('texts.email_already_register')], 400);

View File

@ -33,6 +33,7 @@ use App\Models\Client;
use App\Models\Credit;
use App\Models\Invoice;
use App\Repositories\CreditRepository;
use App\Services\PdfMaker\PdfMerge;
use App\Transformers\CreditTransformer;
use App\Utils\Ninja;
use App\Utils\TempFile;
@ -534,6 +535,20 @@ class CreditController extends BaseController
return response()->json(['message' => ctrans('texts.sent_message')], 200);
}
if($action == 'bulk_print' && auth()->user()->can('view', $credits->first())){
$paths = $credits->map(function ($credit){
return $credit->service()->getCreditPdf($credit->invitations->first());
});
$merge = (new PdfMerge($paths->toArray()))->run();
return response()->streamDownload(function () use ($merge) {
echo ($merge);
}, 'print.pdf', ['Content-Type' => 'application/pdf']);
}
$credits->each(function ($credit, $key) use ($action) {
if (auth()->user()->can('edit', $credit)) {
$this->performAction($credit, $action, true);

View File

@ -131,22 +131,22 @@ class EmailController extends BaseController
if(Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified)
return response(['message' => 'Please verify your account to send emails.'], 400);
nlog($entity);
if($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order' || $entity == 'App\Models\PurchaseOrder'){
return $this->sendPurchaseOrder($entity_obj, $data, $template);
}
$entity_obj->invitations->each(function ($invitation) use ($data, $entity_string, $entity_obj, $template) {
if (! $invitation->contact->trashed() && $invitation->contact->email) {
$entity_obj->service()->markSent()->save();
EmailEntity::dispatch($invitation->fresh(), $invitation->company, $template, $data);
EmailEntity::dispatch($invitation->fresh(), $invitation->company, $template, $data)->delay(now()->addSeconds(2));
}
});
$entity_obj = $entity_obj->fresh();
$entity_obj->last_sent_date = now();
$entity_obj->save();
/*Only notify the admin ONCE, not once per contact/invite*/
@ -194,7 +194,7 @@ class EmailController extends BaseController
$data['template'] = $template;
PurchaseOrderEmail::dispatch($entity_obj, $entity_obj->company, $data);
PurchaseOrderEmail::dispatch($entity_obj, $entity_obj->company, $data)->delay(now()->addSeconds(2));
return $this->itemResponse($entity_obj);

View File

@ -126,7 +126,7 @@ class ImportController extends Controller
private function getEntityMap($entity_type)
{
return sprintf('App\\Import\\Definitions\%sMap', ucfirst($entity_type));
return sprintf('App\\Import\\Definitions\%sMap', ucfirst(Str::camel($entity_type)));
}
private function getCsvData($csvfile)
@ -136,6 +136,8 @@ class ImportController extends Controller
}
$csv = Reader::createFromString($csvfile);
$csvdelimiter = self::detectDelimiter($csvfile);
$csv->setDelimiter($csvdelimiter);
$stmt = new Statement();
$data = iterator_to_array($stmt->process($csv));
@ -156,4 +158,17 @@ class ImportController extends Controller
return $data;
}
public function detectDelimiter($csvfile)
{
$delimiters = array(',', '.', ';');
$bestDelimiter = false;
$count = 0;
foreach ($delimiters as $delimiter)
if (substr_count($csvfile, $delimiter) > $count) {
$count = substr_count($csvfile, $delimiter);
$bestDelimiter = $delimiter;
}
return $bestDelimiter;
}
}

View File

@ -19,6 +19,7 @@ use App\Factory\CloneInvoiceToQuoteFactory;
use App\Factory\InvoiceFactory;
use App\Filters\InvoiceFilters;
use App\Http\Requests\Invoice\ActionInvoiceRequest;
use App\Http\Requests\Invoice\BulkInvoiceRequest;
use App\Http\Requests\Invoice\CreateInvoiceRequest;
use App\Http\Requests\Invoice\DestroyInvoiceRequest;
use App\Http\Requests\Invoice\EditInvoiceRequest;
@ -27,6 +28,7 @@ use App\Http\Requests\Invoice\StoreInvoiceRequest;
use App\Http\Requests\Invoice\UpdateInvoiceRequest;
use App\Http\Requests\Invoice\UpdateReminderRequest;
use App\Http\Requests\Invoice\UploadInvoiceRequest;
use App\Jobs\Cron\AutoBill;
use App\Jobs\Entity\EmailEntity;
use App\Jobs\Invoice\BulkInvoiceJob;
use App\Jobs\Invoice\StoreInvoice;
@ -40,6 +42,7 @@ use App\Models\Invoice;
use App\Models\Quote;
use App\Models\TransactionEvent;
use App\Repositories\InvoiceRepository;
use App\Services\PdfMaker\PdfMerge;
use App\Transformers\InvoiceTransformer;
use App\Transformers\QuoteTransformer;
use App\Utils\Ninja;
@ -237,7 +240,7 @@ class InvoiceController extends BaseController
'metadata' => [],
];
TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db);
// TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db);
return $this->itemResponse($invoice);
}
@ -425,15 +428,15 @@ class InvoiceController extends BaseController
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$transaction = [
'invoice' => $invoice->transaction_event(),
'payment' => [],
'client' => $invoice->client->transaction_event(),
'credit' => [],
'metadata' => [],
];
// $transaction = [
// 'invoice' => $invoice->transaction_event(),
// 'payment' => [],
// 'client' => $invoice->client->transaction_event(),
// 'credit' => [],
// 'metadata' => [],
// ];
TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db);
// TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db);
return $this->itemResponse($invoice);
}
@ -545,11 +548,11 @@ class InvoiceController extends BaseController
* ),
* )
*/
public function bulk()
public function bulk(BulkInvoiceRequest $request)
{
$action = request()->input('action');
$action = $request->input('action');
$ids = request()->input('ids');
$ids = $request->input('ids');
if(Ninja::isHosted() && (stripos($action, 'email') !== false) && !auth()->user()->company()->account->account_sms_verified)
return response(['message' => 'Please verify your account to send emails.'], 400);
@ -588,6 +591,20 @@ class InvoiceController extends BaseController
}
if($action == 'bulk_print' && auth()->user()->can('view', $invoices->first())){
$paths = $invoices->map(function ($invoice){
return $invoice->service()->getInvoicePdf();
});
$merge = (new PdfMerge($paths->toArray()))->run();
return response()->streamDownload(function () use ($merge) {
echo ($merge);
}, 'print.pdf', ['Content-Type' => 'application/pdf']);
}
/*
* Send the other actions to the switch
*/
@ -680,11 +697,14 @@ class InvoiceController extends BaseController
{
/*If we are using bulk actions, we don't want to return anything */
switch ($action) {
case 'auto_bill':
AutoBill::dispatch($invoice->id, $invoice->company->db);
return $this->itemResponse($invoice);
case 'clone_to_invoice':
$invoice = CloneInvoiceFactory::create($invoice, auth()->user()->id);
return $this->itemResponse($invoice);
break;
case 'clone_to_quote':
$quote = CloneInvoiceToQuoteFactory::create($invoice, auth()->user()->id);
@ -751,7 +771,7 @@ class InvoiceController extends BaseController
}
break;
case 'cancel':
$invoice = $invoice->service()->handleCancellation()->deletePdf()->touchPdf()->save();
$invoice = $invoice->service()->handleCancellation()->touchPdf()->save();
if (! $bulk) {
$this->itemResponse($invoice);
@ -761,7 +781,7 @@ class InvoiceController extends BaseController
case 'email':
//check query parameter for email_type and set the template else use calculateTemplate
if (request()->has('email_type') && property_exists($invoice->company->settings, request()->input('email_type'))) {
if (request()->has('email_type') && in_array(request()->input('email_type'), ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'custom1', 'custom2', 'custom3'])) {
$this->reminder_template = $invoice->client->getSetting(request()->input('email_type'));
} else {
$this->reminder_template = $invoice->calculateTemplate('invoice');

View File

@ -181,7 +181,10 @@ class MigrationController extends BaseController
$company->tasks()->forceDelete();
$company->vendors()->forceDelete();
$company->expenses()->forceDelete();
$company->purchase_orders()->forceDelete();
$company->bank_transaction_rules()->forceDelete();
$company->bank_transactions()->forceDelete();
// $company->bank_integrations()->forceDelete();
$company->all_activities()->forceDelete();
$settings = $company->settings;

View File

@ -3,20 +3,21 @@
* @OA\Schema(
* schema="Activity",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="activity_type_id", type="string", example="2", description="______"),
* @OA\Property(property="client_id", type="string", example="2", description="______"),
* @OA\Property(property="company_id", type="string", example="2", description="______"),
* @OA\Property(property="user_id", type="string", example="2", description="______"),
* @OA\Property(property="invoice_id", type="string", example="2", description="______"),
* @OA\Property(property="payment_id", type="string", example="2", description="______"),
* @OA\Property(property="credit_id", type="string", example="2", description="______"),
* @OA\Property(property="updated_at", type="string", example="2", description="______"),
* @OA\Property(property="expense_id", type="string", example="2", description="______"),
* @OA\Property(property="is_system", type="boolean", example=true, description="______"),
* @OA\Property(property="contact_id", type="string", example="2", description="______"),
* @OA\Property(property="task_id", type="string", example="2", description="______"),
* @OA\Property(property="notes", type="string", example="2", description="______"),
* @OA\Property(property="ip", type="string", example="2", description="______"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The id field of the activity"),
* @OA\Property(property="activity_type_id", type="string", example="Opnel5aKBz", description="The activity type id"),
* @OA\Property(property="client_id", type="string", example="Opnel5aKBz", description="The client hashed id"),
* @OA\Property(property="company_id", type="string", example="Opnel5aKBz", description="The company hashed id"),
* @OA\Property(property="user_id", type="string", example="Opnel5aKBz", description="The user hashed id"),
* @OA\Property(property="invoice_id", type="string", example="Opnel5aKBz", description="The invoice hashed id"),
* @OA\Property(property="payment_id", type="string", example="Opnel5aKBz", description="The payment hashed id"),
* @OA\Property(property="credit_id", type="string", example="Opnel5aKBz", description="The credit hashed id"),
* @OA\Property(property="updated_at", type="integer", example="343421434", description="Unixtimestamp the last time the record was updated"),
* @OA\Property(property="expense_id", type="string", example="Opnel5aKBz", description="The expense hashed id"),
* @OA\Property(property="is_system", type="boolean", example=true, description="Defines is the activity was performed by the system"),
* @OA\Property(property="contact_id", type="string", example="Opnel5aKBz", description="The contact hashed id"),
* @OA\Property(property="task_id", type="string", example="Opnel5aKBz", description="The task hashed id"),
* @OA\Property(property="notes", type="string", example="Opnel5aKBz", description="Activity Notes"),
* @OA\Property(property="token_id", type="string", example="Opnel5aKBz", description="The hashed ID of the token who performed the action"),
* @OA\Property(property="ip", type="string", example="192.168.1.252", description="The IP Address of the user who performed the action"),
* )
*/

View File

@ -0,0 +1,10 @@
<?php
/**
* @OA\Schema(
* schema="BTRules",
* type="object",
* @OA\Property(property="data_key", type="string", example="description,amount", description="The key to search"),
* @OA\Property(property="operator", type="string", example=">", description="The operator flag of the search"),
* @OA\Property(property="value", type="string" ,example="bob", description="The value to search for"),
* )
*/

View File

@ -0,0 +1,18 @@
<?php
/**
* @OA\Schema(
* schema="BankIntegration",
* type="object",
* @OA\Property(property="id", type="string", example="AS3df3A", description="The bank integration hashed id"),
* @OA\Property(property="company_id", type="string", example="AS3df3A", description="The company hashed id"),
* @OA\Property(property="user_id", type="string", example="AS3df3A", description="The user hashed id"),
* @OA\Property(property="provider_bank_name", type="string", example="Chase Bank", description="The providers bank name"),
* @OA\Property(property="bank_account_id", type="integer", example="1233434", description="The bank account id"),
* @OA\Property(property="bank_account_name", type="string", example="My Checking Acc", description="The name of the account"),
* @OA\Property(property="bank_account_number", type="string", example="111 234 2332", description="The account number"),
* @OA\Property(property="bank_account_status", type="string", example="ACTIVE", description="The status of the bank account"),
* @OA\Property(property="bank_account_type", type="string", example="CREDITCARD", description="The type of account"),
* @OA\Property(property="balance", type="number", example="1000000", description="The current bank balance if available"),
* @OA\Property(property="currency", type="string", example="USD", description="iso_3166_3 code"),
* )
*/

View File

@ -0,0 +1,20 @@
<?php
/**
* @OA\Schema(
* schema="BankTransaction",
* type="object",
* @OA\Property(property="id", type="string", example="AS3df3A", description="The bank integration hashed id"),
* @OA\Property(property="company_id", type="string", example="AS3df3A", description="The company hashed id"),
* @OA\Property(property="user_id", type="string", example="AS3df3A", description="The user hashed id"),
* @OA\Property(property="transaction_id", type="integer", example=343434, description="The id of the transaction rule"),
* @OA\Property(property="amount", type="number", example=10.00, description="The transaction amount"),
* @OA\Property(property="currency_id", type="string", example="1", description="The currency ID of the currency"),
* @OA\Property(property="account_type", type="string", example="creditCard", description="The account type"),
* @OA\Property(property="description", type="string", example="Potato purchases for kevin", description="The description of the transaction"),
* @OA\Property(property="category_id", type="integer", example=1, description="The category id"),
* @OA\Property(property="category_type", type="string", example="Expenses", description="The category description"),
* @OA\Property(property="base_type", type="string", example="CREDIT", description="Either CREDIT or DEBIT"),
* @OA\Property(property="date", type="string", example="2022-09-01", description="The date of the transaction"),
* @OA\Property(property="bank_account_id", type="integer", example="1", description="The ID number of the bank account"),
* )
*/

View File

@ -0,0 +1,25 @@
<?php
/**
* @OA\Schema(
* schema="BankTransactionRule",
* type="object",
* @OA\Property(property="id", type="string", example="AS3df3A", description="The bank transaction rules hashed id"),
* @OA\Property(property="company_id", type="string", example="AS3df3A", description="The company hashed id"),
* @OA\Property(property="user_id", type="string", example="AS3df3A", description="The user hashed id"),
* @OA\Property(property="name", type="string", example="Rule 1", description="The name of the transaction"),
* @OA\Property(
* property="rules",
* type="array",
* description="A mapped collection of the sub rules for the BankTransactionRule",
* @OA\Items(
* ref="#/components/schemas/BTRules",
* ),
* ),
* @OA\Property(property="auto_convert", type="boolean", example=true, description="Flags whether the rule converts the transaction automatically"),
* @OA\Property(property="matches_on_all", type="boolean", example=true, description="Flags whether all subrules are required for the match"),
* @OA\Property(property="applies_to", type="string", example="CREDIT", description="Flags whether the rule applies to a CREDIT or DEBIT"),
* @OA\Property(property="client_id", type="string", example="AS3df3A", description="The client hashed id"),
* @OA\Property(property="vendor_id", type="string", example="AS3df3A", description="The vendor hashed id"),
* @OA\Property(property="category_id", type="string", example="AS3df3A", description="The category hashed id"),
* )
*/

View File

@ -3,29 +3,28 @@
* @OA\Schema(
* schema="ClientContact",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="company_id", type="string", example="", description="________"),
* @OA\Property(property="client_id", type="string", example="", description="________"),
* @OA\Property(property="first_name", type="string", example="", description="________"),
* @OA\Property(property="last_name", type="string", example="", description="________"),
* @OA\Property(property="phone", type="string", example="", description="________"),
* @OA\Property(property="custom_value1", type="string", example="", description="________"),
* @OA\Property(property="custom_value2", type="string", example="", description="________"),
* @OA\Property(property="custom_value3", type="string", example="", description="________"),
* @OA\Property(property="custom_value4", type="string", example="", description="________"),
* @OA\Property(property="email", type="string", example="", description="________"),
* @OA\Property(property="accepted_terms_version", type="string", example="", description="________"),
* @OA\Property(property="password", type="string", example="", description="________"),
* @OA\Property(property="confirmation-code", type="string", example="", description="________"),
* @OA\Property(property="token", type="string", example="", description="________"),
* @OA\Property(property="is_primary", type="boolean", example=true, description="________"),
* @OA\Property(property="confirmed", type="boolean", example=true, description="________"),
* @OA\Property(property="is_locked", type="boolean", example=true, description="________"),
* @OA\Property(property="send_email", type="boolean", example=true, description="________"),
* @OA\Property(property="failed_logins", type="number", format="integer", example="3", description="________"),
* @OA\Property(property="email_verified_at", type="number", format="integer", example="134341234234", description="________"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="________"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The hashed if of the contact"),
* @OA\Property(property="user_id", type="string", example="Opnel5aKBz", description="The hashed id of the user who created the contact"),
* @OA\Property(property="company_id", type="string", example="Opnel5aKBz", description="The hashed id of the company"),
* @OA\Property(property="client_id", type="string", example="Opnel5aKBz", description="The hashed id of the client"),
* @OA\Property(property="first_name", type="string", example="John", description="The first name of the contact"),
* @OA\Property(property="last_name", type="string", example="Doe", description="The last name of the contact"),
* @OA\Property(property="phone", type="string", example="555-152-4524", description="The phone number of the contact"),
* @OA\Property(property="custom_value1", type="string", example="", description="A Custom field value"),
* @OA\Property(property="custom_value2", type="string", example="", description="A Custom field value"),
* @OA\Property(property="custom_value3", type="string", example="", description="A Custom field value"),
* @OA\Property(property="custom_value4", type="string", example="", description="A Custom field value"),
* @OA\Property(property="email", type="string", example="", description="The email of the contact"),
* @OA\Property(property="accepted_terms_version", type="string", example="A long set of ToS", description="The terms of service which the contact has accpeted"),
* @OA\Property(property="password", type="string", example="*****", description="The hashed password of the contact"),
* @OA\Property(property="confirmation-code", type="string", example="333-sdjkh34gbasd", description="The confirmation code used to authenticate the contacts email address"),
* @OA\Property(property="token", type="string", example="333-sdjkh34gbasd", description="A uuid based token."),
* @OA\Property(property="is_primary", type="boolean", example=true, description="Defines is this contact is the primary contact for the client"),
* @OA\Property(property="confirmed", type="boolean", example=true, description="Boolean value confirms the user has confirmed their account."),
* @OA\Property(property="is_locked", type="boolean", example=true, description="Boolean value defines if the contact has been locked out."),
* @OA\Property(property="send_email", type="boolean", example=true, description="Boolean value determines is this contact should receive emails"),
* @OA\Property(property="failed_logins", type="number", format="integer", example="3", description="The number of failed logins the contact has had"),
* @OA\Property(property="email_verified_at", type="number", format="integer", example="134341234234", description="The date which the contact confirmed their email"),
* @OA\Property(property="last_login", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="updated_at", type="number", format="integer", example="134341234234", description="Timestamp"),

View File

@ -3,13 +3,13 @@
* @OA\Schema(
* schema="ClientGatewayToken",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="company_id", type="string", example="2", description="______"),
* @OA\Property(property="client_id", type="string", example="2", description="______"),
* @OA\Property(property="token", type="string", example="2", description="______"),
* @OA\Property(property="routing_number", type="string", example="2", description="______"),
* @OA\Property(property="company_gateway_id", type="string", example="2", description="______"),
* @OA\Property(property="is_default", type="boolean", example="true", description="______"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The hashed id of the client gateway token"),
* @OA\Property(property="company_id", type="string", example="2", description="The hashed id of the company"),
* @OA\Property(property="client_id", type="string", example="2", description="The hashed_id of the client"),
* @OA\Property(property="token", type="string", example="2", description="The payment token"),
* @OA\Property(property="routing_number", type="string", example="2", description="THe bank account routing number"),
* @OA\Property(property="company_gateway_id", type="string", example="2", description="The hashed id of the company gateway"),
* @OA\Property(property="is_default", type="boolean", example="true", description="Flag determining if the token is the default payment method"),
*
* )
*/

View File

@ -3,9 +3,9 @@
* @OA\Schema(
* schema="Client",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="company_id", type="string", example="", description="________"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The hashed id of the client"),
* @OA\Property(property="user_id", type="string", example="", description="The hashed id of the user"),
* @OA\Property(property="company_id", type="string", example="", description="The hashed id of the company"),
* @OA\Property(
* property="contacts",
* type="array",
@ -13,35 +13,35 @@
* ref="#/components/schemas/ClientContact",
* ),
* ),
* @OA\Property(property="name", type="string", example="", description="________"),
* @OA\Property(property="website", type="string", example="", description="________"),
* @OA\Property(property="private_notes", type="string", example="", description="________"),
* @OA\Property(property="client_hash", type="string", example="", description="________"),
* @OA\Property(property="industry_id", type="string", example="", description="________"),
* @OA\Property(property="size_id", type="string", example="", description="________"),
* @OA\Property(property="address1", type="string", example="", description="________"),
* @OA\Property(property="address2", type="string", example="", description="________"),
* @OA\Property(property="city", type="string", example="", description="________"),
* @OA\Property(property="state", type="string", example="", description="________"),
* @OA\Property(property="postal_code", type="string", example="", description="________"),
* @OA\Property(property="name", type="string", example="Jims housekeeping", description="The client name"),
* @OA\Property(property="website", type="string", example="www.jims.com", description="The client website"),
* @OA\Property(property="private_notes", type="string", example="These are very private notes", description="Private notes on the client"),
* @OA\Property(property="client_hash", type="string", example="asdfkjhk342hjhbfdvmnfb1", description="The client hash"),
* @OA\Property(property="industry_id", type="number", example="5", description="The industry id of the client"),
* @OA\Property(property="size_id", type="number", example="2", description="The size id of the client"),
* @OA\Property(property="address1", type="string", example="", description="Address line 1"),
* @OA\Property(property="address2", type="string", example="", description="Address line 2"),
* @OA\Property(property="city", type="string", example="Beverley Hills", description="City"),
* @OA\Property(property="state", type="string", example="Californnia", description="State/Locality"),
* @OA\Property(property="postal_code", type="string", example="90210", description="Zip / Postal code"),
* @OA\Property(property="phone", type="string", example="555-3434-3434", description="The client phone number"),
* @OA\Property(property="country_id", type="number", format="integer", example="1", description="________"),
* @OA\Property(property="custom_value1", type="string", example="", description="________"),
* @OA\Property(property="custom_value2", type="string", example="", description="________"),
* @OA\Property(property="custom_value3", type="string", example="", description="________"),
* @OA\Property(property="custom_value4", type="string", example="", description="________"),
* @OA\Property(property="vat_number", type="string", example="", description="________"),
* @OA\Property(property="id_number", type="string", example="", description="________"),
* @OA\Property(property="number", type="string", example="", description="________"),
* @OA\Property(property="shipping_address1", type="string", example="", description="________"),
* @OA\Property(property="shipping_address2", type="string", example="", description="________"),
* @OA\Property(property="shipping_city", type="string", example="", description="________"),
* @OA\Property(property="shipping_state", type="string", example="", description="________"),
* @OA\Property(property="shipping_postal_code", type="string", example="", description="________"),
* @OA\Property(property="shipping_country_id", type="number", format="integer", example="", description="________"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="________"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="________"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="________"),
* @OA\Property(property="country_id", type="number", format="integer", example="1", description="The client country id"),
* @OA\Property(property="custom_value1", type="string", example="Something custom", description="A custom value"),
* @OA\Property(property="custom_value2", type="string", example="2002-01-01", description="A custom value"),
* @OA\Property(property="custom_value3", type="string", example="Something custom", description="A custom value"),
* @OA\Property(property="custom_value4", type="string", example="Something custom", description="A custom value"),
* @OA\Property(property="vat_number", type="string", example="VAT123", description="The client VAT number"),
* @OA\Property(property="id_number", type="string", example="CLIENT_ID_NUMBER", description=" The client id number"),
* @OA\Property(property="number", type="string", example="", description="The client number - assigned by the system typically"),
* @OA\Property(property="shipping_address1", type="string", example="5 Wallaby Way", description="The shipping address line 1"),
* @OA\Property(property="shipping_address2", type="string", example="Suite 5", description="The shipping address line 2"),
* @OA\Property(property="shipping_city", type="string", example="Perth", description="The shipping city"),
* @OA\Property(property="shipping_state", type="string", example="Western Australia", description="The shipping state"),
* @OA\Property(property="shipping_postal_code", type="string", example="6110", description="The shipping postal code"),
* @OA\Property(property="shipping_country_id", type="number", format="integer", example="4", description="The shipping country id"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Boolean flagged determining if the client has been deleted"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="The client balance"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="The amount the client has paid to date."),
* @OA\Property(property="credit_balance", type="number", format="float", example="10.00", description="An amount which is available to the client for future use."),
* @OA\Property(property="last_login", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),

View File

@ -3,14 +3,14 @@
* @OA\Schema(
* schema="CompanyGateway",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="company_id", type="string", example="2", description="______"),
* @OA\Property(property="gateway_key", type="string", example="2", description="______"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The hashed id of the company gateway"),
* @OA\Property(property="company_id", type="string", example="2", description="The company hashed id"),
* @OA\Property(property="gateway_key", type="string", example="2", description="The gateway key (hash)"),
* @OA\Property(property="accepted_credit_cards", type="integer", example="32", description="Bitmask representation of cards"),
* @OA\Property(property="require_billing_address", type="boolean", example=true, description="______"),
* @OA\Property(property="require_shipping_address", type="boolean", example=true, description="______"),
* @OA\Property(property="require_billing_address", type="boolean", example=true, description="Determines if the the billing address is required prior to payment."),
* @OA\Property(property="require_shipping_address", type="boolean", example=true, description="Determines if the the billing address is required prior to payment."),
* @OA\Property(property="config", type="string", example="dfadsfdsafsafd", description="The configuration map for the gateway"),
* @OA\Property(property="update_details", type="boolean", example=true, description="______"),
* @OA\Property(property="update_details", type="boolean", example=true, description="Determines if the client details should be updated."),
* @OA\Property(
* property="fees_and_limits",
* type="array",

View File

@ -17,9 +17,9 @@
* @OA\Property(property="update_products", type="boolean", example=true, description="Toggles updating a product description which description changes"),
* @OA\Property(property="show_product_details", type="boolean", example=true, description="Toggles showing a product description which description changes"),
* @OA\Property(property="custom_fields", type="object", description="Custom fields map"),
* @OA\Property(property="enable_product_cost", type="boolean", example=true, description="______________"),
* @OA\Property(property="enable_product_quantity", type="boolean", example=true, description="______________"),
* @OA\Property(property="default_quantity", type="boolean", example=true, description="______________"),
* @OA\Property(property="enable_product_cost", type="boolean", example=true, description="Show/Hide the product cost field in the UI"),
* @OA\Property(property="enable_product_quantity", type="boolean", example=true, description="Show/hide the product quantity field (used in the UI to show the default quantity)"),
* @OA\Property(property="default_quantity", type="boolean", example=true, description="Enable/Disable whether to use a default quantity"),
* @OA\Property(property="custom_surcharge_taxes1", type="boolean", example=true, description="Toggles charging taxes on custom surcharge amounts"),
* @OA\Property(property="custom_surcharge_taxes2", type="boolean", example=true, description="Toggles charging taxes on custom surcharge amounts"),
* @OA\Property(property="custom_surcharge_taxes3", type="boolean", example=true, description="Toggles charging taxes on custom surcharge amounts"),

View File

@ -4,20 +4,20 @@
* schema="CompanySettings",
* type="object",
* @OA\Property(property="timezone_id", type="string", example="15", description="The timezone id"),
* @OA\Property(property="date_format_id", type="string", example="15", description="____________"),
* @OA\Property(property="military_time", type="boolean", example=true, description="____________"),
* @OA\Property(property="language_id", type="string", example="1", description="____________"),
* @OA\Property(property="show_currency_code", type="boolean", example=true, description="____________"),
* @OA\Property(property="date_format_id", type="string", example="15", description="The date format id"),
* @OA\Property(property="military_time", type="boolean", example=true, description="Toggles 12/24 hour time"),
* @OA\Property(property="language_id", type="string", example="1", description="The language id"),
* @OA\Property(property="show_currency_code", type="boolean", example=true, description="Toggles whether the currency symbol or code is shown"),
* @OA\Property(property="currency_id", type="string", example=true, description="The default currency id"),
* @OA\Property(property="payment_terms", type="integer", example="1", description="-1 sets no payment term, 0 sets payment due immediately, positive integers indicates payment terms in days"),
* @OA\Property(property="company_gateway_ids", type="string", example="1,2,3,4", description="A commad separate list of available gateways"),
* @OA\Property(property="custom_value1", type="string", example="Custom Label", description="____________"),
* @OA\Property(property="custom_value2", type="string", example="Custom Label", description="____________"),
* @OA\Property(property="custom_value3", type="string", example="Custom Label", description="____________"),
* @OA\Property(property="custom_value4", type="string", example="Custom Label", description="____________"),
* @OA\Property(property="default_task_rate", type="number", format="float", example="10.00", description="____________"),
* @OA\Property(property="send_reminders", type="boolean", example=true, description="____________"),
* @OA\Property(property="enable_client_portal_tasks", type="boolean", example=true, description="____________"),
* @OA\Property(property="custom_value1", type="string", example="Custom Label", description="A Custom Label"),
* @OA\Property(property="custom_value2", type="string", example="Custom Label", description="A Custom Label"),
* @OA\Property(property="custom_value3", type="string", example="Custom Label", description="A Custom Label"),
* @OA\Property(property="custom_value4", type="string", example="Custom Label", description="A Custom Label"),
* @OA\Property(property="default_task_rate", type="number", format="float", example="10.00", description="The default task rate"),
* @OA\Property(property="send_reminders", type="boolean", example=true, description="Toggles whether reminders are sent"),
* @OA\Property(property="enable_client_portal_tasks", type="boolean", example=true, description="Show/hide the tasks panel in the client portal"),
* @OA\Property(property="email_style", type="string", example="light", description="options include plain,light,dark,custom"),
* @OA\Property(property="reply_to_email", type="string", example="email@gmail.com", description="The reply to email address"),
* @OA\Property(property="bcc_email", type="string", example="email@gmail.com, contact@gmail.com", description="A comma separate list of BCC emails"),
@ -26,35 +26,35 @@
* @OA\Property(property="email_style_custom", type="string", example="<HTML></HTML>", description="The custom template"),
* @OA\Property(property="counter_number_applied", type="string", example="when_sent", description="enum when the invoice number counter is set, ie when_saved, when_sent, when_paid"),
* @OA\Property(property="quote_number_applied", type="string", example="when_sent", description="enum when the quote number counter is set, ie when_saved, when_sent"),
* @OA\Property(property="custom_message_dashboard", type="string", example="Please pay invoices immediately", description="____________"),
* @OA\Property(property="custom_message_unpaid_invoice", type="string", example="Please pay invoices immediately", description="____________"),
* @OA\Property(property="custom_message_paid_invoice", type="string", example="Thanks for paying this invoice!", description="____________"),
* @OA\Property(property="custom_message_unapproved_quote", type="string", example="Please approve quote", description="____________"),
* @OA\Property(property="lock_invoices", type="boolean", example=true, description="____________"),
* @OA\Property(property="auto_archive_invoice", type="boolean", example=true, description="____________"),
* @OA\Property(property="auto_archive_quote", type="boolean", example=true, description="____________"),
* @OA\Property(property="auto_convert_quote", type="boolean", example=true, description="____________"),
* @OA\Property(property="inclusive_taxes", type="boolean", example=true, description="____________"),
* @OA\Property(property="custom_message_dashboard", type="string", example="Please pay invoices immediately", description="A custom message which is displayed on the dashboard"),
* @OA\Property(property="custom_message_unpaid_invoice", type="string", example="Please pay invoices immediately", description="A custom message which is displayed in the client portal when a client is viewing a unpaid invoice."),
* @OA\Property(property="custom_message_paid_invoice", type="string", example="Thanks for paying this invoice!", description="A custom message which is displayed in the client portal when a client is viewing a paid invoice."),
* @OA\Property(property="custom_message_unapproved_quote", type="string", example="Please approve quote", description="A custom message which is displayed in the client portal when a client is viewing a unapproved quote."),
* @OA\Property(property="lock_invoices", type="boolean", example=true, description="Toggles whether invoices are locked once sent and cannot be modified further"),
* @OA\Property(property="auto_archive_invoice", type="boolean", example=true, description="Toggles whether a invoice is archived immediately following payment"),
* @OA\Property(property="auto_archive_quote", type="boolean", example=true, description="Toggles whether a quote is archived after being converted to a invoice"),
* @OA\Property(property="auto_convert_quote", type="boolean", example=true, description="Toggles whether a quote is converted to a invoice when approved"),
* @OA\Property(property="inclusive_taxes", type="boolean", example=true, description="Boolean flag determining whether inclusive or exclusive taxes are used"),
* @OA\Property(property="translations", type="object", example="", description="JSON payload of customized translations"),
* @OA\Property(property="task_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the task number pattern"),
* @OA\Property(property="task_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="task_number_counter", type="integer", example="1", description="The incrementing counter for tasks"),
* @OA\Property(property="reminder_send_time", type="integer", example="32400", description="Time from UTC +0 when the email will be sent to the client"),
* @OA\Property(property="expense_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the expense number pattern"),
* @OA\Property(property="expense_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="expense_number_counter", type="integer", example="1", description="The incrementing counter for expenses"),
* @OA\Property(property="vendor_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the vendor number pattern"),
* @OA\Property(property="vendor_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="vendor_number_counter", type="integer", example="1", description="The incrementing counter for vendors"),
* @OA\Property(property="ticket_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the ticket number pattern"),
* @OA\Property(property="ticket_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="ticket_number_counter", type="integer", example="1", description="The incrementing counter for tickets"),
* @OA\Property(property="payment_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the payment number pattern"),
* @OA\Property(property="payment_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="payment_number_counter", type="integer", example="1", description="The incrementing counter for payments"),
* @OA\Property(property="invoice_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the invoice number pattern"),
* @OA\Property(property="invoice_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="invoice_number_counter", type="integer", example="1", description="The incrementing counter for invoices"),
* @OA\Property(property="quote_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the quote number pattern"),
* @OA\Property(property="quote_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="quote_number_counter", type="integer", example="1", description="The incrementing counter for quotes"),
* @OA\Property(property="client_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the client number pattern"),
* @OA\Property(property="client_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="client_number_counter", type="integer", example="1", description="The incrementing counter for clients"),
* @OA\Property(property="credit_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the credit number pattern"),
* @OA\Property(property="credit_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="credit_number_counter", type="integer", example="1", description="The incrementing counter for credits"),
* @OA\Property(property="recurring_invoice_number_prefix", type="string", example="R", description="This string is prepended to the recurring invoice number"),
* @OA\Property(property="reset_counter_frequency_id", type="integer", example="1", description="CONSTANT which is used to apply the frequency which the counters are reset"),
* @OA\Property(property="reset_counter_date", type="string", example="2019-01-01", description="The explicit date which is used to reset counters"),
@ -103,52 +103,52 @@
* @OA\Property(property="name", type="string", example="Acme Co", description="The company name"),
* @OA\Property(property="company_logo", type="object", example="logo.png", description="The company logo file"),
* @OA\Property(property="website", type="string", example="www.acme.com", description="The company website URL"),
* @OA\Property(property="address1", type="string", example="Suite 888", description="____________"),
* @OA\Property(property="address2", type="string", example="5 Jimbo Way", description="____________"),
* @OA\Property(property="city", type="string", example="Sydney", description="____________"),
* @OA\Property(property="state", type="string", example="Florisa", description="____________"),
* @OA\Property(property="postal_code", type="string", example="90210", description="____________"),
* @OA\Property(property="phone", type="string", example="555-213-3948", description="____________"),
* @OA\Property(property="email", type="string", example="joe@acme.co", description="____________"),
* @OA\Property(property="address1", type="string", example="Suite 888", description="The company address line 1"),
* @OA\Property(property="address2", type="string", example="5 Jimbo Way", description="The company address line 2"),
* @OA\Property(property="city", type="string", example="Sydney", description="The company city"),
* @OA\Property(property="state", type="string", example="Florisa", description="The company state"),
* @OA\Property(property="postal_code", type="string", example="90210", description="The company zip/postal code"),
* @OA\Property(property="phone", type="string", example="555-213-3948", description="The company phone"),
* @OA\Property(property="email", type="string", example="joe@acme.co", description="The company email"),
* @OA\Property(property="country_id", type="string", example="1", description="The country ID"),
* @OA\Property(property="vat_number", type="string", example="32 120 377 720", description="____________"),
* @OA\Property(property="vat_number", type="string", example="32 120 377 720", description="The company VAT/TAX ID number"),
* @OA\Property(property="page_size", type="string", example="A4", description="The default page size"),
* @OA\Property(property="font_size", type="number", example="9", description="The font size"),
* @OA\Property(property="primary_font", type="string", example="roboto", description="The primary font"),
* @OA\Property(property="secondary_font", type="string", example="roboto", description="The secondary font"),
* @OA\Property(property="hide_paid_to_date", type="boolean", example=false, description="____________"),
* @OA\Property(property="embed_documents", type="boolean", example=false, description="____________"),
* @OA\Property(property="all_pages_header", type="boolean", example=false, description="____________"),
* @OA\Property(property="all_pages_footer", type="boolean", example=false, description="____________"),
* @OA\Property(property="document_email_attachment", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_client_portal_password", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_email_markup", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_client_portal_dashboard", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_client_portal", type="boolean", example=false, description="____________"),
* @OA\Property(property="email_template_statement", type="string", example="template matter", description="____________"),
* @OA\Property(property="email_subject_statement", type="string", example="subject matter", description="____________"),
* @OA\Property(property="signature_on_pdf", type="boolean", example=false, description="____________"),
* @OA\Property(property="quote_footer", type="string", example="the quote footer", description="____________"),
* @OA\Property(property="email_subject_custom1", type="string", example="Custom Subject 1", description="____________"),
* @OA\Property(property="email_subject_custom2", type="string", example="Custom Subject 2", description="____________"),
* @OA\Property(property="email_subject_custom3", type="string", example="Custom Subject 3", description="____________"),
* @OA\Property(property="email_template_custom1", type="string", example="<HTML>", description="____________"),
* @OA\Property(property="email_template_custom2", type="string", example="<HTML>", description="____________"),
* @OA\Property(property="email_template_custom3", type="string", example="<HTML>", description="____________"),
* @OA\Property(property="enable_reminder1", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_reminder2", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_reminder3", type="boolean", example=false, description="____________"),
* @OA\Property(property="hide_paid_to_date", type="boolean", example=false, description="Flags whether to hide the paid to date field"),
* @OA\Property(property="embed_documents", type="boolean", example=false, description="Toggled whether to embed documents in the PDF"),
* @OA\Property(property="all_pages_header", type="boolean", example=false, description="The header for the PDF"),
* @OA\Property(property="all_pages_footer", type="boolean", example=false, description="The footer for the PDF"),
* @OA\Property(property="document_email_attachment", type="boolean", example=false, description="Toggles whether to attach documents in the email"),
* @OA\Property(property="enable_client_portal_password", type="boolean", example=false, description="Toggles password protection of the client portal"),
* @OA\Property(property="enable_email_markup", type="boolean", example=false, description="Toggles the use of markdown in emails"),
* @OA\Property(property="enable_client_portal_dashboard", type="boolean", example=false, description="Toggles whether the client dashboard is shown in the client portal"),
* @OA\Property(property="enable_client_portal", type="boolean", example=false, description="Toggles whether the entire client portal is displayed to the client, or only the context"),
* @OA\Property(property="email_template_statement", type="string", example="template matter", description="The body of the email for statements"),
* @OA\Property(property="email_subject_statement", type="string", example="subject matter", description="The subject of the email for statements"),
* @OA\Property(property="signature_on_pdf", type="boolean", example=false, description="Toggles whether the signature (if available) is displayed on the PDF"),
* @OA\Property(property="quote_footer", type="string", example="the quote footer", description="The default quote footer"),
* @OA\Property(property="email_subject_custom1", type="string", example="Custom Subject 1", description="Custom reminder template subject"),
* @OA\Property(property="email_subject_custom2", type="string", example="Custom Subject 2", description="Custom reminder template subject"),
* @OA\Property(property="email_subject_custom3", type="string", example="Custom Subject 3", description="Custom reminder template subject"),
* @OA\Property(property="email_template_custom1", type="string", example="<HTML>", description="Custom reminder template body"),
* @OA\Property(property="email_template_custom2", type="string", example="<HTML>", description="Custom reminder template body"),
* @OA\Property(property="email_template_custom3", type="string", example="<HTML>", description="Custom reminder template body"),
* @OA\Property(property="enable_reminder1", type="boolean", example=false, description="Toggles whether this reminder is enabled"),
* @OA\Property(property="enable_reminder2", type="boolean", example=false, description="Toggles whether this reminder is enabled"),
* @OA\Property(property="enable_reminder3", type="boolean", example=false, description="Toggles whether this reminder is enabled"),
* @OA\Property(property="num_days_reminder1", type="number", example="9", description="The Reminder interval"),
* @OA\Property(property="num_days_reminder2", type="number", example="9", description="The Reminder interval"),
* @OA\Property(property="num_days_reminder3", type="number", example="9", description="The Reminder interval"),
* @OA\Property(property="schedule_reminder1", type="string", example="after_invoice_date", description="(enum: after_invoice_date, before_due_date, after_due_date)"),
* @OA\Property(property="schedule_reminder2", type="string", example="after_invoice_date", description="(enum: after_invoice_date, before_due_date, after_due_date)"),
* @OA\Property(property="schedule_reminder3", type="string", example="after_invoice_date", description="(enum: after_invoice_date, before_due_date, after_due_date)"),
* @OA\Property(property="late_fee_amount1", type="number", example=10.00, description="____________"),
* @OA\Property(property="late_fee_amount2", type="number", example=20.00, description="____________"),
* @OA\Property(property="late_fee_amount3", type="number", example=100.00, description="____________"),
* @OA\Property(property="endless_reminder_frequency_id", type="string", example="1", description="____________"),
* @OA\Property(property="client_online_payment_notification", type="boolean", example=false, description="____________"),
* @OA\Property(property="client_manual_payment_notification", type="boolean", example=false, description="____________"),
* @OA\Property(property="late_fee_amount1", type="number", example=10.00, description="The late fee amount for reminder 1"),
* @OA\Property(property="late_fee_amount2", type="number", example=20.00, description="The late fee amount for reminder 2"),
* @OA\Property(property="late_fee_amount3", type="number", example=100.00, description="The late fee amount for reminder 2"),
* @OA\Property(property="endless_reminder_frequency_id", type="string", example="1", description="The frequency id of the endless reminder"),
* @OA\Property(property="client_online_payment_notification", type="boolean", example=false, description="Determines if a client should receive the notification for a online payment"),
* @OA\Property(property="client_manual_payment_notification", type="boolean", example=false, description="Determines if a client should receive the notification for a manually entered payment"),
* )
*/

View File

@ -3,8 +3,8 @@
* @OA\Schema(
* schema="CreditPaymentable",
* type="object",
* @OA\Property(property="credit_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="amount", type="string", example="2", description="______"),
* @OA\Property(property="credit_id", type="string", example="Opnel5aKBz", description="The credit hashed id"),
* @OA\Property(property="amount", type="string", example="2", description="The credit amount"),
*
* )
*/

View File

@ -3,33 +3,33 @@
* @OA\Schema(
* schema="Credit",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
* @OA\Property(property="company_id", type="string", example="", description="________"),
* @OA\Property(property="client_id", type="string", example="", description="________"),
* @OA\Property(property="status_id", type="string", example="", description="________"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The credit hashed id"),
* @OA\Property(property="user_id", type="string", example="", description="The user hashed id"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="The assigned user hashed id"),
* @OA\Property(property="company_id", type="string", example="", description="The company hashed id"),
* @OA\Property(property="client_id", type="string", example="", description="The client hashed id"),
* @OA\Property(property="status_id", type="string", example="", description="The status field id infors of the current status of the credit"),
* @OA\Property(property="invoice_id", type="string", example="", description="The linked invoice this credit is applied to"),
* @OA\Property(property="number", type="string", example="QUOTE_101", description="The credit number - is a unique alpha numeric number per credit per company"),
* @OA\Property(property="po_number", type="string", example="", description="________"),
* @OA\Property(property="terms", type="string", example="", description="________"),
* @OA\Property(property="public_notes", type="string", example="", description="________"),
* @OA\Property(property="private_notes", type="string", example="", description="________"),
* @OA\Property(property="footer", type="string", example="", description="________"),
* @OA\Property(property="custom_value1", type="string", example="", description="________"),
* @OA\Property(property="custom_value2", type="string", example="", description="________"),
* @OA\Property(property="custom_value3", type="string", example="", description="________"),
* @OA\Property(property="custom_value4", type="string", example="", description="________"),
* @OA\Property(property="tax_name1", type="string", example="", description="________"),
* @OA\Property(property="tax_name2", type="string", example="", description="________"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_name3", type="string", example="", description="________"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="po_number", type="string", example="", description="The purchase order number this credit refers to"),
* @OA\Property(property="terms", type="string", example="", description="The credit terms field"),
* @OA\Property(property="public_notes", type="string", example="", description="The public notes field of the credit"),
* @OA\Property(property="private_notes", type="string", example="", description="The private notes field of the credit"),
* @OA\Property(property="footer", type="string", example="", description="The credit footer text"),
* @OA\Property(property="custom_value1", type="string", example="", description="A Custom value"),
* @OA\Property(property="custom_value2", type="string", example="", description="A Custom value"),
* @OA\Property(property="custom_value3", type="string", example="", description="A Custom value"),
* @OA\Property(property="custom_value4", type="string", example="", description="A Custom value"),
* @OA\Property(property="tax_name1", type="string", example="", description="The tax name"),
* @OA\Property(property="tax_name2", type="string", example="", description="The tax rate"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="The tax name"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="tax_name3", type="string", example="", description="The tax name"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="total_taxes", type="number", format="float", example="10.00", description="The total taxes for the credit"),
* @OA\Property(property="line_items", type="object", example="", description="_________"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="line_items", type="object", example="", description="The line items array containing the line items of the credit"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="The total credit amount"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="The credit balance"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),

View File

@ -3,18 +3,18 @@
* @OA\Schema(
* schema="Document",
* type="object",
* @OA\Property(property="id", type="string", example="AS3df3A", description="The design hashed id"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
* @OA\Property(property="project_id", type="string", example="", description="__________"),
* @OA\Property(property="vendor_id", type="string", example="", description="__________"),
* @OA\Property(property="name", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="url", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="preview", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="type", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="disk", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="hash", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Flag to determine if the design is deleted"),
* @OA\Property(property="id", type="string", example="AS3df3A", description="The document hashed id"),
* @OA\Property(property="user_id", type="string", example="", description="The user hashed id"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="The assigned user hashed id"),
* @OA\Property(property="project_id", type="string", example="", description="The project associated with this document"),
* @OA\Property(property="vendor_id", type="string", example="", description="The vendor associated with this documents"),
* @OA\Property(property="name", type="string", example="Beauty", description="The document name"),
* @OA\Property(property="url", type="string", example="Beauty", description="The document url"),
* @OA\Property(property="preview", type="string", example="Beauty", description="The document preview url"),
* @OA\Property(property="type", type="string", example="Beauty", description="The document type"),
* @OA\Property(property="disk", type="string", example="Beauty", description="The document disk"),
* @OA\Property(property="hash", type="string", example="Beauty", description="The document hashed"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Flag to determine if the document is deleted"),
* @OA\Property(property="is_default", type="boolean", example=true, description="Flag to determine if the document is a default doc"),
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="updated_at", type="number", format="integer", example="134341234234", description="Timestamp"),

View File

@ -3,11 +3,11 @@
* @OA\Schema(
* schema="ExpenseCategory",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="name", type="string", example="Accounting", description="______"),
* @OA\Property(property="user_id", type="string", example="XS987sD", description="______"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="______"),
* @OA\Property(property="updated_at", type="string", example="2", description="______"),
* @OA\Property(property="created_at", type="string", example="2", description="______"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The expense hashed id"),
* @OA\Property(property="name", type="string", example="Accounting", description="The expense category name"),
* @OA\Property(property="user_id", type="string", example="XS987sD", description="The user hashed id"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Flag determining whether the expense category has been deleted"),
* @OA\Property(property="updated_at", type="integer", example="2", description="The updated at timestamp"),
* @OA\Property(property="created_at", type="integer", example="2", description="The created at timestamp"),
* )
*/

View File

@ -3,40 +3,40 @@
* @OA\Schema(
* schema="Expense",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
* @OA\Property(property="company_id", type="string", example="", description="________"),
* @OA\Property(property="client_id", type="string", example="", description="________"),
* @OA\Property(property="invoice_id", type="string", example="", description="________"),
* @OA\Property(property="bank_id", type="string", example="", description="________"),
* @OA\Property(property="invoice_currency_id", type="string", example="", description="________"),
* @OA\Property(property="expense_currency_id", type="string", example="", description="________"),
* @OA\Property(property="invoice_category_id", type="string", example="", description="________"),
* @OA\Property(property="payment_type_id", type="string", example="", description="________"),
* @OA\Property(property="recurring_expense_id", type="string", example="", description="________"),
* @OA\Property(property="private_notes", type="string", example="", description="________"),
* @OA\Property(property="public_notes", type="string", example="", description="________"),
* @OA\Property(property="transaction_reference", type="string", example="", description="________"),
* @OA\Property(property="transcation_id", type="string", example="", description="________"),
* @OA\Property(property="custom_value1", type="string", example="", description="________"),
* @OA\Property(property="custom_value2", type="string", example="", description="________"),
* @OA\Property(property="custom_value3", type="string", example="", description="________"),
* @OA\Property(property="custom_value4", type="string", example="", description="________"),
* @OA\Property(property="tax_name1", type="string", example="", description="________"),
* @OA\Property(property="tax_name2", type="string", example="", description="________"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_name3", type="string", example="", description="________"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="foreign_amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="exchange_rate", type="number", format="float", example="0.80", description="_________"),
* @OA\Property(property="date", type="string", example="", description="________"),
* @OA\Property(property="payment_date", type="string", example="", description="________"),
* @OA\Property(property="should_be_invoiced", type="boolean", example=true, description="_________"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"),
* @OA\Property(property="invoice_documents", type="boolean", example=true, description=""),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The expense hashed id"),
* @OA\Property(property="user_id", type="string", example="", description="The user hashed id"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="The assigned user hashed id"),
* @OA\Property(property="company_id", type="string", example="", description="The company hashed id"),
* @OA\Property(property="client_id", type="string", example="", description="The client hashed id"),
* @OA\Property(property="invoice_id", type="string", example="", description="The related invoice hashed id"),
* @OA\Property(property="bank_id", type="string", example="", description="The bank id related to this expense"),
* @OA\Property(property="invoice_currency_id", type="string", example="", description="The currency id of the related invoice"),
* @OA\Property(property="expense_currency_id", type="string", example="", description="The currency id of the expense"),
* @OA\Property(property="invoice_category_id", type="string", example="", description="The invoice category id"),
* @OA\Property(property="payment_type_id", type="string", example="", description="The payment type id"),
* @OA\Property(property="recurring_expense_id", type="string", example="", description="The related recurring expense this expense was created from"),
* @OA\Property(property="private_notes", type="string", example="", description="The private notes of the expense"),
* @OA\Property(property="public_notes", type="string", example="", description="The public notes of the expense"),
* @OA\Property(property="transaction_reference", type="string", example="", description="The transaction references of the expense"),
* @OA\Property(property="transcation_id", type="string", example="", description="The transaction id of the expense"),
* @OA\Property(property="custom_value1", type="string", example="", description="A custom value"),
* @OA\Property(property="custom_value2", type="string", example="", description="A custom value"),
* @OA\Property(property="custom_value3", type="string", example="", description="A custom value"),
* @OA\Property(property="custom_value4", type="string", example="", description="A custom value"),
* @OA\Property(property="tax_name1", type="string", example="", description="Tax name"),
* @OA\Property(property="tax_name2", type="string", example="", description="Tax name"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="Tax rate"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="Tax rate"),
* @OA\Property(property="tax_name3", type="string", example="", description="Tax name"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="Tax rate"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="The total expense amont"),
* @OA\Property(property="foreign_amount", type="number", format="float", example="10.00", description="The total foreign amount of the expense"),
* @OA\Property(property="exchange_rate", type="number", format="float", example="0.80", description="The exchange rate at the time of the expense"),
* @OA\Property(property="date", type="string", example="2022-12-01", description="The expense date formate Y-m-d"),
* @OA\Property(property="payment_date", type="string", example="", description="The date of payment for the expense, format Y-m-d"),
* @OA\Property(property="should_be_invoiced", type="boolean", example=true, description="Flag whether the expense should be invoiced"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Boolean determining whether the expense has been deleted"),
* @OA\Property(property="invoice_documents", type="boolean", example=true, description="Passing the expense documents over to the invoice"),
* @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"),
* @OA\Property(property="archived_at", type="number", format="integer", example="1434342123", description="Timestamp"),
* )

View File

@ -3,17 +3,17 @@
* @OA\Schema(
* schema="FeesAndLimits",
* type="object",
* @OA\Property(property="min_limit", type="string", example="2", description="______"),
* @OA\Property(property="max_limit", type="string", example="2", description="______"),
* @OA\Property(property="fee_amount", type="number", format="float", example="2.0", description="______"),
* @OA\Property(property="fee_percent", type="number", format="float", example="2.0", description="______"),
* @OA\Property(property="fee_tax_name1", type="string", example="2", description="______"),
* @OA\Property(property="fee_tax_name2", type="string", example="2", description="______"),
* @OA\Property(property="fee_tax_name3", type="string", example="2", description="______"),
* @OA\Property(property="fee_tax_rate1", type="number", format="float", example="2.0", description="______"),
* @OA\Property(property="fee_tax_rate2", type="number", format="float", example="2.0", description="______"),
* @OA\Property(property="fee_tax_rate3", type="number", format="float", example="2.0", description="______"),
* @OA\Property(property="fee_cap", type="number", format="float", example="2.0", description="______"),
* @OA\Property(property="adjust_fee_percent", type="boolean", example=true, description="______"),
* @OA\Property(property="min_limit", type="string", example="2", description="The minimum amount accepted for this gateway"),
* @OA\Property(property="max_limit", type="string", example="2", description="The maximum amount accepted for this gateway"),
* @OA\Property(property="fee_amount", type="number", format="float", example="2.0", description="The gateway fee amount"),
* @OA\Property(property="fee_percent", type="number", format="float", example="2.0", description="The gateway fee percentage"),
* @OA\Property(property="fee_tax_name1", type="string", example="GST", description="Fee tax name"),
* @OA\Property(property="fee_tax_name2", type="string", example="VAT", description="Fee tax name"),
* @OA\Property(property="fee_tax_name3", type="string", example="CA Sales Tax", description="Fee tax name"),
* @OA\Property(property="fee_tax_rate1", type="number", format="float", example="10.0", description="The tax rate"),
* @OA\Property(property="fee_tax_rate2", type="number", format="float", example="17.5", description="The tax rate"),
* @OA\Property(property="fee_tax_rate3", type="number", format="float", example="25.0", description="The tax rate"),
* @OA\Property(property="fee_cap", type="number", format="float", example="2.0", description="If set the fee amount will be no higher than this amount"),
* @OA\Property(property="adjust_fee_percent", type="boolean", example=true, description="Adjusts the fee to match the exact gateway fee."),
* )
*/

View File

@ -3,10 +3,10 @@
* @OA\Schema(
* schema="GroupSetting",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="company_id", type="string", example="", description="________"),
* @OA\Property(property="name", type="string", example="", description="________"),
* @OA\Property(property="settings", type="object", example="", description="________"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The group setting hashed id"),
* @OA\Property(property="user_id", type="string", example="Opnel5aKBz", description="The user hashed id"),
* @OA\Property(property="company_id", type="string", example="Opnel5aKBz", description="The company hashed id"),
* @OA\Property(property="name", type="string", example="A groupies group", description="The name of the group"),
* @OA\Property(property="settings", type="object", example="", description="The settings object"),
* )
*/

View File

@ -3,43 +3,43 @@
* @OA\Schema(
* schema="Invoice",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
* @OA\Property(property="company_id", type="string", example="", description="________"),
* @OA\Property(property="client_id", type="string", example="", description="________"),
* @OA\Property(property="status_id", type="string", example="", description="________"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The invoice hashed id"),
* @OA\Property(property="user_id", type="string", example="Opnel5aKBz", description="The user hashed id"),
* @OA\Property(property="assigned_user_id", type="string", example="Opnel5aKBz", description="The assigned user hashed id"),
* @OA\Property(property="company_id", type="string", example="Opnel5aKBz", description="The company hashed id"),
* @OA\Property(property="client_id", type="string", example="Opnel5aKBz", description="The client hashed id"),
* @OA\Property(property="status_id", type="string", example="4", description="The invoice status variable"),
* @OA\Property(property="number", type="string", example="INV_101", description="The invoice number - is a unique alpha numeric number per invoice per company"),
* @OA\Property(property="po_number", type="string", example="", description="________"),
* @OA\Property(property="terms", type="string", example="", description="________"),
* @OA\Property(property="public_notes", type="string", example="", description="________"),
* @OA\Property(property="private_notes", type="string", example="", description="________"),
* @OA\Property(property="footer", type="string", example="", description="________"),
* @OA\Property(property="custom_value1", type="string", example="", description="________"),
* @OA\Property(property="custom_value2", type="string", example="", description="________"),
* @OA\Property(property="custom_value3", type="string", example="", description="________"),
* @OA\Property(property="custom_value4", type="string", example="", description="________"),
* @OA\Property(property="tax_name1", type="string", example="", description="________"),
* @OA\Property(property="tax_name2", type="string", example="", description="________"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_name3", type="string", example="", description="________"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="po_number", type="string", example="PO-1234", description="The purchase order associated with this invoice"),
* @OA\Property(property="terms", type="string", example="These are invoice terms", description="The invoice terms"),
* @OA\Property(property="public_notes", type="string", example="These are some public notes", description="The public notes of the invoice"),
* @OA\Property(property="private_notes", type="string", example="These are some private notes", description="The private notes of the invoice"),
* @OA\Property(property="footer", type="string", example="", description="The invoice footer notes"),
* @OA\Property(property="custom_value1", type="string", example="2022-10-01", description="A custom field value"),
* @OA\Property(property="custom_value2", type="string", example="Something custom", description="A custom field value"),
* @OA\Property(property="custom_value3", type="string", example="", description="A custom field value"),
* @OA\Property(property="custom_value4", type="string", example="", description="A custom field value"),
* @OA\Property(property="tax_name1", type="string", example="", description="The tax name"),
* @OA\Property(property="tax_name2", type="string", example="", description="The tax name"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="tax_name3", type="string", example="", description="The tax name"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="total_taxes", type="number", format="float", example="10.00", description="The total taxes for the invoice"),
* @OA\Property(property="line_items", type="object", example="", description="_________"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"),
* @OA\Property(property="line_items", type="object", example="", description="An array of objects which define the line items of the invoice"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="The invoice amount"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="The invoice balance"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="The amount paid on the invoice to date"),
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="The invoice discount, can be an amount or a percentage"),
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="The deposit/partial amount"),
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="Flag determining if the discount is an amount or a percentage"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Defines if the invoice has been deleted"),
* @OA\Property(property="uses_inclusive_taxes", type="boolean", example=true, description="Defines the type of taxes used as either inclusive or exclusive"),
* @OA\Property(property="date", type="string", format="date", example="1994-07-30", description="The Invoice Date"),
* @OA\Property(property="last_sent_date", type="string", format="date", example="1994-07-30", description="The last date the invoice was sent out"),
* @OA\Property(property="next_send_date", type="string", format="date", example="1994-07-30", description="The Next date for a reminder to be sent"),
* @OA\Property(property="partial_due_date", type="string", format="date", example="1994-07-30", description="_________"),
* @OA\Property(property="due_date", type="string", format="date", example="1994-07-30", description="_________"),
* @OA\Property(property="partial_due_date", type="string", format="date", example="1994-07-30", description="The due date for the deposit/partial amount"),
* @OA\Property(property="due_date", type="string", format="date", example="1994-07-30", description="The due date of the invoice"),
* @OA\Property(property="settings",ref="#/components/schemas/CompanySettings"),
* @OA\Property(property="last_viewed", type="number", format="integer", example="1434342123", description="Timestamp"),
* @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"),

View File

@ -3,18 +3,18 @@
* @OA\Schema(
* schema="Payment",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="client_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="invitation_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="client_contact_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="user_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The payment hashed id"),
* @OA\Property(property="client_id", type="string", example="Opnel5aKBz", description="The client hashed id"),
* @OA\Property(property="invitation_id", type="string", example="Opnel5aKBz", description="The invitation hashed id"),
* @OA\Property(property="client_contact_id", type="string", example="Opnel5aKBz", description="The client contact hashed id"),
* @OA\Property(property="user_id", type="string", example="Opnel5aKBz", description="The user hashed id"),
* @OA\Property(property="type_id", type="string", example="1", description="The Payment Type ID"),
* @OA\Property(property="date", type="string", example="1-1-2014", description="The Payment date"),
* @OA\Property(property="transaction_reference", type="string", example="xcsSxcs124asd", description="The transaction reference as defined by the payment gateway"),
* @OA\Property(property="assigned_user_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="private_notes", type="string", example="The payment was refunded due to error", description="______"),
* @OA\Property(property="is_manual", type="boolean", example=true, description="______"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="______"),
* @OA\Property(property="assigned_user_id", type="string", example="Opnel5aKBz", description="The assigned user hashed id"),
* @OA\Property(property="private_notes", type="string", example="The payment was refunded due to error", description="The private notes of the payment"),
* @OA\Property(property="is_manual", type="boolean", example=true, description="Flags whether the payment was made manually or processed via a gateway"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Defines if the payment has been deleted"),
* @OA\Property(property="amount", type="number", example=10.00, description="The amount of this payment"),
* @OA\Property(property="refunded", type="number", example=10.00, description="The refunded amount of this payment"),
* @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"),

View File

@ -6,8 +6,8 @@
* @OA\Property(property="id", type="string", example="AS3df3A", description="The paymentable hashed id"),
* @OA\Property(property="invoice_id", type="string", example="AS3df3A", description="The invoice hashed id"),
* @OA\Property(property="credit_id", type="string", example="AS3df3A", description="The credit hashed id"),
* @OA\Property(property="refunded", type="number", format="float", example="10.00", description="______"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="______"),
* @OA\Property(property="refunded", type="number", format="float", example="10.00", description="The amount that has been refunded for this payment"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="The amount that has been applied to the payment"),
* @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"),
* @OA\Property(property="created_at", type="number", format="integer", example="1434342123", description="Timestamp"),*
* )

View File

@ -3,6 +3,6 @@
* @OA\Schema(
* schema="Product",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The product hashed id"),
* )
*/

View File

@ -3,7 +3,7 @@
* @OA\Schema(
* schema="Project",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="name", type="string", example="New Project", description="______"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The project hashed id"),
* @OA\Property(property="name", type="string", example="New Project", description="The project name"),
* )
*/

View File

@ -3,43 +3,43 @@
* @OA\Schema(
* schema="Quote",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
* @OA\Property(property="company_id", type="string", example="", description="________"),
* @OA\Property(property="client_id", type="string", example="", description="________"),
* @OA\Property(property="status_id", type="string", example="", description="________"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The quote hashed id"),
* @OA\Property(property="user_id", type="string", example="", description="The user hashed id"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="The assigned user hashed id"),
* @OA\Property(property="company_id", type="string", example="", description="The company hashed id"),
* @OA\Property(property="client_id", type="string", example="", description="The client hashed id"),
* @OA\Property(property="status_id", type="string", example="", description="The status of the quote"),
* @OA\Property(property="number", type="string", example="QUOTE_101", description="The quote number - is a unique alpha numeric number per quote per company"),
* @OA\Property(property="po_number", type="string", example="", description="________"),
* @OA\Property(property="terms", type="string", example="", description="________"),
* @OA\Property(property="public_notes", type="string", example="", description="________"),
* @OA\Property(property="private_notes", type="string", example="", description="________"),
* @OA\Property(property="footer", type="string", example="", description="________"),
* @OA\Property(property="custom_value1", type="string", example="", description="________"),
* @OA\Property(property="custom_value2", type="string", example="", description="________"),
* @OA\Property(property="custom_value3", type="string", example="", description="________"),
* @OA\Property(property="custom_value4", type="string", example="", description="________"),
* @OA\Property(property="tax_name1", type="string", example="", description="________"),
* @OA\Property(property="tax_name2", type="string", example="", description="________"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_name3", type="string", example="", description="________"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="po_number", type="string", example="PO-1234", description="The purchase order number associated with this quote"),
* @OA\Property(property="terms", type="string", example="These are some quote terms. Valid for 14 days.", description="The quote terms"),
* @OA\Property(property="public_notes", type="string", example="These are public notes which the client may see", description="Public notes for the quote"),
* @OA\Property(property="private_notes", type="string", example="These are private notes, not to be disclosed to the client", description="Private notes for the quote"),
* @OA\Property(property="footer", type="string", example="The text goes in the footer of the quote", description="Footer text of quote"),
* @OA\Property(property="custom_value1", type="string", example="A custom value", description="Custom value field"),
* @OA\Property(property="custom_value2", type="string", example="A custom value", description="Custom value field"),
* @OA\Property(property="custom_value3", type="string", example="A custom value", description="Custom value field"),
* @OA\Property(property="custom_value4", type="string", example="A custom value", description="Custom value field"),
* @OA\Property(property="tax_name1", type="string", example="GST", description="The tax name"),
* @OA\Property(property="tax_name2", type="string", example="VAT", description="The tax name"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="tax_name3", type="string", example="", description="The tax name"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="total_taxes", type="number", format="float", example="10.00", description="The total taxes for the quote"),
* @OA\Property(property="line_items", type="object", example="", description="_________"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"),
* @OA\Property(property="line_items", type="object", example="", description="An array of line items of the quote"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="The total amount of the quote"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="The balance due of the quote"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="The amount that has been paid to date on the quote"),
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="The quote discount"),
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="The partial/deposit amount"),
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="Boolean flag determining if the quote is an amount or percentage"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Boolean flag determining if the quote has been deleted"),
* @OA\Property(property="uses_inclusive_taxes", type="boolean", example=true, description="Defines the type of taxes used as either inclusive or exclusive"),
* @OA\Property(property="date", type="string", format="date", example="1994-07-30", description="The Quote Date"),
* @OA\Property(property="last_sent_date", type="string", format="date", example="1994-07-30", description="The last date the quote was sent out"),
* @OA\Property(property="next_send_date", type="string", format="date", example="1994-07-30", description="The Next date for a reminder to be sent"),
* @OA\Property(property="partial_due_date", type="string", format="date", example="1994-07-30", description="_________"),
* @OA\Property(property="due_date", type="string", format="date", example="1994-07-30", description="_________"),
* @OA\Property(property="partial_due_date", type="string", format="date", example="1994-07-30", description="The date when the partial/deposit is due"),
* @OA\Property(property="due_date", type="string", format="date", example="1994-07-30", description="The date the quote is valid until"),
* @OA\Property(property="settings",ref="#/components/schemas/CompanySettings"),
* @OA\Property(property="last_viewed", type="number", format="integer", example="1434342123", description="Timestamp"),
* @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"),

View File

@ -3,44 +3,43 @@
* @OA\Schema(
* schema="RecurringExpense",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
* @OA\Property(property="company_id", type="string", example="", description="________"),
* @OA\Property(property="client_id", type="string", example="", description="________"),
* @OA\Property(property="invoice_id", type="string", example="", description="________"),
* @OA\Property(property="bank_id", type="string", example="", description="________"),
* @OA\Property(property="invoice_currency_id", type="string", example="", description="________"),
* @OA\Property(property="expense_currency_id", type="string", example="", description="________"),
* @OA\Property(property="invoice_category_id", type="string", example="", description="________"),
* @OA\Property(property="payment_type_id", type="string", example="", description="________"),
* @OA\Property(property="recurring_expense_id", type="string", example="", description="________"),
* @OA\Property(property="private_notes", type="string", example="", description="________"),
* @OA\Property(property="public_notes", type="string", example="", description="________"),
* @OA\Property(property="transaction_reference", type="string", example="", description="________"),
* @OA\Property(property="transcation_id", type="string", example="", description="________"),
* @OA\Property(property="custom_value1", type="string", example="", description="________"),
* @OA\Property(property="custom_value2", type="string", example="", description="________"),
* @OA\Property(property="custom_value3", type="string", example="", description="________"),
* @OA\Property(property="custom_value4", type="string", example="", description="________"),
* @OA\Property(property="tax_name1", type="string", example="", description="________"),
* @OA\Property(property="tax_name2", type="string", example="", description="________"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="tax_name3", type="string", example="", description="________"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="frequency_id", type="number", format="int", example="1", description="_________"),
* @OA\Property(property="remaining_cycles", type="number", format="int", example="1", description="_________"),
* @OA\Property(property="foreign_amount", type="number", format="float", example="10.00", description="_________"),
* @OA\Property(property="exchange_rate", type="number", format="float", example="0.80", description="_________"),
* @OA\Property(property="date", type="string", example="", description="________"),
* @OA\Property(property="payment_date", type="string", example="", description="________"),
* @OA\Property(property="should_be_invoiced", type="boolean", example=true, description="_________"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"),
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="The hashed id of the recurring expense"),
* @OA\Property(property="user_id", type="string", example="Opnel5aKBz", description="The hashed id of the user who created the recurring expense"),
* @OA\Property(property="assigned_user_id", type="string", example="Opnel5aKBz", description="The hashed id of the user assigned to this recurring expense"),
* @OA\Property(property="company_id", type="string", example="Opnel5aKBz", description="The hashed id of the company"),
* @OA\Property(property="client_id", type="string", example="Opnel5aKBz", description="The hashed id of the client"),
* @OA\Property(property="invoice_id", type="string", example="Opnel5aKBz", description="The hashed id of the invoice"),
* @OA\Property(property="bank_id", type="string", example="22", description="The id of the bank associated with this recurring expense"),
* @OA\Property(property="invoice_currency_id", type="string", example="1", description="The currency id of the invoice associated with this recurring expense"),
* @OA\Property(property="expense_currency_id", type="string", example="1", description="The currency id of the expense associated with this recurring expense"),
* @OA\Property(property="invoice_category_id", type="string", example="1", description="The category id of the invoice"),
* @OA\Property(property="payment_type_id", type="string", example="1", description="The payment type id"),
* @OA\Property(property="private_notes", type="string", example="Private and confidential", description="The recurring expense private notes"),
* @OA\Property(property="public_notes", type="string", example="This is the best client in the world", description="The recurring expense public notes"),
* @OA\Property(property="transaction_reference", type="string", example="EXP-1223-2333", description="The recurring expense transaction reference"),
* @OA\Property(property="transcation_id", type="string", example="1233312312", description="The transaction id of the recurring expense"),
* @OA\Property(property="custom_value1", type="string", example="$1000", description="Custom value field"),
* @OA\Property(property="custom_value2", type="string", example="2022-10-10", description="Custom value field"),
* @OA\Property(property="custom_value3", type="string", example="short text", description="Custom value field"),
* @OA\Property(property="custom_value4", type="string", example="very long text", description="Custom value field"),
* @OA\Property(property="tax_name1", type="string", example="GST", description="The tax name"),
* @OA\Property(property="tax_name2", type="string", example="VAT", description="The tax name"),
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="tax_name3", type="string", example="", description="The tax name"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="The total amount of the recurring expense"),
* @OA\Property(property="frequency_id", type="number", format="int", example="1", description="The frequency this recurring expense fires"),
* @OA\Property(property="remaining_cycles", type="number", format="int", example="1", description="The number of remaining cycles for this recurring expense"),
* @OA\Property(property="foreign_amount", type="number", format="float", example="10.00", description="The foreign currency amount of the recurring expense"),
* @OA\Property(property="exchange_rate", type="number", format="float", example="0.80", description="The exchange rate for the expernse"),
* @OA\Property(property="date", type="string", example="", description="The date of the expense"),
* @OA\Property(property="payment_date", type="string", example="", description="The date the expense was paid"),
* @OA\Property(property="should_be_invoiced", type="boolean", example=true, description="Boolean flag determining if the expense should be invoiced"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Boolean flag determining if the recurring expense is deleted"),
* @OA\Property(property="last_sent_date", type="string", format="date", example="1994-07-30", description="The Date it was sent last"),
* @OA\Property(property="next_send_date", type="string", format="date", example="1994-07-30", description="The next send date"),
* @OA\Property(property="invoice_documents", type="boolean", example=true, description=""),
* @OA\Property(property="invoice_documents", type="boolean", example=true, description="Boolean flag determining if the documents associated with this expense should be passed onto the invoice if it is converted to an invoice"),
* @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"),
* @OA\Property(property="archived_at", type="number", format="integer", example="1434342123", description="Timestamp"),
* )

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