mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Merge branch 'v5-develop' into v5-develop
This commit is contained in:
commit
9b7845d4b5
60
README.md
60
README.md
@ -83,6 +83,66 @@ http://localhost:8000/client/login - For Client Portal
|
||||
user: user@example.com
|
||||
pass: password
|
||||
```
|
||||
## Developers Guide
|
||||
|
||||
|
||||
### App Design
|
||||
|
||||
The API and client portal have been developed using [Laravel](https://laravel.com) if you wish to contribute to this project familiarity with Laravel is essential.
|
||||
|
||||
When inspecting functionality of the API, the best place to start would be in the routes/api.php file which describes all of the availabe API endpoints. The controller methods then describe all the entry points into each domain of the application, ie InvoiceController / QuoteController
|
||||
|
||||
The average API request follows this path into the application.
|
||||
|
||||
* Middleware processes the request initially inspecting the domain being requested + provides the authentication layer.
|
||||
* The request then passes into a Form Request (Type hinted in the controller methods) which is used to provide authorization and also validation of the request. If successful, the request is then passed into the controller method where it is digested, here is an example:
|
||||
|
||||
```php
|
||||
public function store(StoreInvoiceRequest $request)
|
||||
{
|
||||
|
||||
$invoice = $this->invoice_repo->save($request->all(), InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
$invoice = $invoice->service()
|
||||
->fillDefaults()
|
||||
->triggeredActions($request)
|
||||
->adjustInventory()
|
||||
->save();
|
||||
|
||||
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($invoice);
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Here for example we are storing a new invoice, we pass the validated request along with a factory into the invoice repository where it is processed and saved.
|
||||
|
||||
The returned invoice then passes through its service class (app/Services/Invoice) where various actions are performed.
|
||||
|
||||
A event is then fired which notifies listeners in the application (app/Providers/EventServiceProvider) which perform non blocking sub tasks
|
||||
|
||||
Finally the invoice is transformed (app/Transformers/) and returned as a response via Fractal.
|
||||
|
||||
### Developer environment
|
||||
|
||||
Using the Quick Hosting Setup describe above you can quickly get started building out your development environment. Instead of using
|
||||
|
||||
```
|
||||
composer i -o --no-dev
|
||||
```
|
||||
|
||||
use
|
||||
|
||||
```
|
||||
composer i -o
|
||||
```
|
||||
|
||||
This provides the developer tools including phpunit which allows the test suite to be run.
|
||||
|
||||
If you are considering contributing back to the main repository, please add in any tests for new functionality / modifications. This will greatly increase the chances of your PR being accepted
|
||||
|
||||
Also, if you plan any additions for the main repository, you may want to discuss this with us first on Slack where we can assist with any technical information and provide advice.
|
||||
|
||||
## Credits
|
||||
* [Hillel Coren](https://hillelcoren.com/)
|
||||
|
@ -1 +1 @@
|
||||
5.5.88
|
||||
5.5.102
|
@ -190,14 +190,12 @@ class BackupUpdate extends Command
|
||||
->where('filename', '!=', '')
|
||||
->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);
|
||||
|
||||
nlog("Backups - Moving {$backup->filename} to {$this->option('disk')}");
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyLedger;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\Contact;
|
||||
use App\Models\Credit;
|
||||
@ -160,16 +161,33 @@ class CheckData extends Command
|
||||
|
||||
private function checkCompanyTokens()
|
||||
{
|
||||
CompanyUser::doesnthave('token')->cursor()->each(function ($cu) {
|
||||
if ($cu->user) {
|
||||
$this->logMessage("Creating missing company token for user # {$cu->user->id} for company id # {$cu->company->id}");
|
||||
(new CreateCompanyToken($cu->company, $cu->user, 'System'))->handle();
|
||||
} else {
|
||||
$this->logMessage("Dangling User ID # {$cu->id}");
|
||||
// CompanyUser::whereDoesntHave('token', function ($query){
|
||||
// return $query->where('is_system', 1);
|
||||
// })->cursor()->each(function ($cu){
|
||||
// if ($cu->user) {
|
||||
// $this->logMessage("Creating missing company token for user # {$cu->user->id} for company id # {$cu->company->id}");
|
||||
// (new CreateCompanyToken($cu->company, $cu->user, 'System'))->handle();
|
||||
// } else {
|
||||
// $this->logMessage("Dangling User ID # {$cu->id}");
|
||||
// }
|
||||
// });
|
||||
|
||||
CompanyUser::query()->cursor()->each(function ($cu) {
|
||||
if (CompanyToken::where('user_id', $cu->user_id)->where('company_id', $cu->company_id)->where('is_system', 1)->doesntExist()) {
|
||||
$this->logMessage("Creating missing company token for user # {$cu->user_id} for company id # {$cu->company_id}");
|
||||
|
||||
if ($cu->company && $cu->user) {
|
||||
(new CreateCompanyToken($cu->company, $cu->user, 'System'))->handle();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* checkOauthSanity
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function checkOauthSanity()
|
||||
{
|
||||
User::where('oauth_provider_id', '1')->cursor()->each(function ($user) {
|
||||
|
@ -188,6 +188,7 @@ class DemoMode extends Command
|
||||
$company_token->account_id = $account->id;
|
||||
$company_token->name = 'test token';
|
||||
$company_token->token = 'TOKEN';
|
||||
$company_token->is_system = true;
|
||||
$company_token->save();
|
||||
|
||||
$u2->companies()->attach($company->id, [
|
||||
|
@ -51,8 +51,8 @@ class ReactBuilder extends Command
|
||||
$directoryIterator = new \RecursiveDirectoryIterator(public_path('react'), \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
|
||||
foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {
|
||||
if (str_contains($file->getFileName(), '.js') && !strpos($file->getFileName(), '.json')) {
|
||||
if (str_contains($file->getFileName(), 'index.')) {
|
||||
if ($file->getExtension() == 'js') {
|
||||
if (str_contains($file->getFileName(), 'index-')) {
|
||||
$includes .= '<script type="module" crossorigin src="/react/'.$file->getFileName().'"></script>'."\n";
|
||||
} else {
|
||||
$includes .= '<link rel="modulepreload" href="/react/'.$file->getFileName().'">'."\n";
|
||||
|
@ -15,6 +15,7 @@ use App\Jobs\Cron\AutoBillCron;
|
||||
use App\Jobs\Cron\RecurringExpensesCron;
|
||||
use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use App\Jobs\Cron\SubscriptionCron;
|
||||
use App\Jobs\Cron\UpdateCalculatedFields;
|
||||
use App\Jobs\Invoice\InvoiceCheckLateWebhook;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
use App\Jobs\Ninja\BankTransactionSync;
|
||||
@ -27,7 +28,6 @@ use App\Jobs\Subscription\CleanStaleInvoiceOrder;
|
||||
use App\Jobs\Util\DiskCleanup;
|
||||
use App\Jobs\Util\ReminderJob;
|
||||
use App\Jobs\Util\SchedulerCheck;
|
||||
use App\Jobs\Util\SendFailedEmails;
|
||||
use App\Jobs\Util\UpdateExchangeRates;
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Models\Account;
|
||||
@ -48,14 +48,23 @@ class Kernel extends ConsoleKernel
|
||||
/* Check for the latest version of Invoice Ninja */
|
||||
$schedule->job(new VersionCheck)->daily();
|
||||
|
||||
/* Checks and cleans redundant files */
|
||||
$schedule->job(new DiskCleanup)->dailyAt('02:10')->withoutOverlapping()->name('disk-cleanup-job')->onOneServer();
|
||||
/* Returns the number of jobs in the queue */
|
||||
$schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping()->name('queue-size-job')->onOneServer();
|
||||
|
||||
/* Send reminders */
|
||||
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping()->name('reminder-job')->onOneServer();
|
||||
|
||||
/* Returns the number of jobs in the queue */
|
||||
$schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping()->name('queue-size-job')->onOneServer();
|
||||
/* Sends recurring invoices*/
|
||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping()->name('recurring-invoice-job')->onOneServer();
|
||||
|
||||
/* Checks for scheduled tasks */
|
||||
$schedule->job(new TaskScheduler())->hourlyAt(10)->withoutOverlapping()->name('task-scheduler-job')->onOneServer();
|
||||
|
||||
/* Stale Invoice Cleanup*/
|
||||
$schedule->job(new CleanStaleInvoiceOrder)->hourlyAt(30)->withoutOverlapping()->name('stale-invoice-job')->onOneServer();
|
||||
|
||||
/* Stale Invoice Cleanup*/
|
||||
$schedule->job(new UpdateCalculatedFields)->hourlyAt(40)->withoutOverlapping()->name('update-calculated-fields-job')->onOneServer();
|
||||
|
||||
/* Checks for large companies and marked them as is_large */
|
||||
$schedule->job(new CompanySizeCheck)->dailyAt('23:20')->withoutOverlapping()->name('company-size-job')->onOneServer();
|
||||
@ -66,33 +75,26 @@ class Kernel extends ConsoleKernel
|
||||
/* Runs cleanup code for subscriptions */
|
||||
$schedule->job(new SubscriptionCron)->dailyAt('00:01')->withoutOverlapping()->name('subscription-job')->onOneServer();
|
||||
|
||||
/* Sends recurring invoices*/
|
||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping()->name('recurring-invoice-job')->onOneServer();
|
||||
|
||||
/* Stale Invoice Cleanup*/
|
||||
$schedule->job(new CleanStaleInvoiceOrder)->hourlyAt(30)->withoutOverlapping()->name('stale-invoice-job')->onOneServer();
|
||||
|
||||
/* Sends recurring invoices*/
|
||||
$schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping()->name('recurring-expense-job')->onOneServer();
|
||||
|
||||
/* Fires notifications for expired Quotes */
|
||||
$schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping()->name('quote-expired-job')->onOneServer();
|
||||
|
||||
/* Fires webhooks for overdue Invoice */
|
||||
$schedule->job(new InvoiceCheckLateWebhook)->dailyAt('07:00')->withoutOverlapping()->name('invoice-overdue-job')->onOneServer();
|
||||
|
||||
/* Performs auto billing */
|
||||
$schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping()->name('auto-bill-job')->onOneServer();
|
||||
|
||||
/* Checks the status of the scheduler */
|
||||
$schedule->job(new SchedulerCheck)->dailyAt('01:10')->withoutOverlapping();
|
||||
|
||||
/* Checks for scheduled tasks */
|
||||
$schedule->job(new TaskScheduler())->hourlyAt(10)->withoutOverlapping()->name('task-scheduler-job')->onOneServer();
|
||||
/* Checks and cleans redundant files */
|
||||
$schedule->job(new DiskCleanup)->dailyAt('02:10')->withoutOverlapping()->name('disk-cleanup-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();
|
||||
|
||||
/* Fires notifications for expired Quotes */
|
||||
$schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping()->name('quote-expired-job')->onOneServer();
|
||||
|
||||
/* Performs auto billing */
|
||||
$schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping()->name('auto-bill-job')->onOneServer();
|
||||
|
||||
/* Fires webhooks for overdue Invoice */
|
||||
$schedule->job(new InvoiceCheckLateWebhook)->dailyAt('07:00')->withoutOverlapping()->name('invoice-overdue-job')->onOneServer();
|
||||
|
||||
if (Ninja::isSelfHost()) {
|
||||
$schedule->call(function () {
|
||||
@ -107,9 +109,6 @@ class Kernel extends ConsoleKernel
|
||||
/* Pulls in bank transactions from third party services */
|
||||
$schedule->job(new BankTransactionSync)->dailyAt('04:10')->withoutOverlapping()->name('bank-trans-sync-job')->onOneServer();
|
||||
|
||||
//not used @deprecate
|
||||
// $schedule->job(new SendFailedEmails)->daily()->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:20')->withoutOverlapping()->name('check-data-db-2-job')->onOneServer();
|
||||
|
@ -84,13 +84,13 @@ class ClientSettings extends BaseSettings
|
||||
*/
|
||||
public static function buildClientSettings($company_settings, $client_settings)
|
||||
{
|
||||
|
||||
if (! $client_settings) {
|
||||
return $company_settings;
|
||||
}
|
||||
|
||||
if(is_array($client_settings))
|
||||
if (is_array($client_settings)) {
|
||||
$client_settings = (object)$client_settings;
|
||||
}
|
||||
|
||||
foreach ($company_settings as $key => $value) {
|
||||
/* pseudo code
|
||||
|
@ -449,6 +449,8 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $mailgun_domain = '';
|
||||
|
||||
public $mailgun_endpoint = 'api.mailgun.net'; //api.eu.mailgun.net
|
||||
|
||||
public $auto_bill_standard_invoices = false;
|
||||
|
||||
public $email_alignment = 'center'; // center , left, right
|
||||
@ -467,7 +469,17 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $show_task_item_description = false;
|
||||
|
||||
public $client_initiated_payments = false;
|
||||
|
||||
public $client_initiated_payments_minimum = 0;
|
||||
|
||||
public $sync_invoice_quote_columns = true;
|
||||
|
||||
public static $casts = [
|
||||
'mailgun_endpoint' => 'string',
|
||||
'client_initiated_payments' => 'bool',
|
||||
'client_initiated_payments_minimum' => 'float',
|
||||
'sync_invoice_quote_columns' => 'bool',
|
||||
'show_task_item_description' => 'bool',
|
||||
'allow_billable_task_items' => 'bool',
|
||||
'accept_client_input_quote_approval' => 'bool',
|
||||
@ -493,7 +505,6 @@ class CompanySettings extends BaseSettings
|
||||
'purchase_order_design_id' => 'string',
|
||||
'purchase_order_footer' => 'string',
|
||||
'purchase_order_number_pattern' => 'string',
|
||||
'purchase_order_number_counter' => 'int',
|
||||
'page_numbering_alignment' => 'string',
|
||||
'page_numbering' => 'bool',
|
||||
'auto_archive_invoice_cancelled' => 'bool',
|
||||
@ -525,7 +536,6 @@ class CompanySettings extends BaseSettings
|
||||
'reminder_send_time' => 'int',
|
||||
'email_sending_method' => 'string',
|
||||
'gmail_sending_user_id' => 'string',
|
||||
'currency_id' => 'string',
|
||||
'counter_number_applied' => 'string',
|
||||
'quote_number_applied' => 'string',
|
||||
'email_subject_custom1' => 'string',
|
||||
@ -907,6 +917,15 @@ class CompanySettings extends BaseSettings
|
||||
'$product.tax',
|
||||
'$product.line_total',
|
||||
],
|
||||
'product_quote_columns' => [
|
||||
'$product.item',
|
||||
'$product.description',
|
||||
'$product.unit_cost',
|
||||
'$product.quantity',
|
||||
'$product.discount',
|
||||
'$product.tax',
|
||||
'$product.line_total',
|
||||
],
|
||||
'task_columns' =>[
|
||||
'$task.service',
|
||||
'$task.description',
|
||||
|
@ -59,7 +59,11 @@ class InvoiceItem
|
||||
|
||||
public $type_id = '1'; //1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee, 6 expense
|
||||
|
||||
public $tax_id = '';
|
||||
|
||||
|
||||
public static $casts = [
|
||||
'tax_id' => 'string',
|
||||
'type_id' => 'string',
|
||||
'quantity' => 'float',
|
||||
'cost' => 'float',
|
||||
|
36
app/DataMapper/Schedule/EmailRecord.php
Normal file
36
app/DataMapper/Schedule/EmailRecord.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Schedule;
|
||||
|
||||
class EmailRecord
|
||||
{
|
||||
/**
|
||||
* Defines the template name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $template = 'email_record';
|
||||
|
||||
/**
|
||||
* Defines the template name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $entity = ''; // invoice, credit, quote, purchase_order
|
||||
|
||||
/**
|
||||
* Defines the template name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $entity_id = '';
|
||||
}
|
156
app/DataMapper/Tax/BaseRule.php
Normal file
156
app/DataMapper/Tax/BaseRule.php
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Tax;
|
||||
|
||||
use App\DataMapper\Tax\ZipTax\Response;
|
||||
use App\Models\Client;
|
||||
|
||||
class BaseRule implements RuleInterface
|
||||
{
|
||||
/** EU TAXES */
|
||||
public bool $consumer_tax_exempt = false;
|
||||
|
||||
public bool $business_tax_exempt = true;
|
||||
|
||||
public bool $eu_business_tax_exempt = true;
|
||||
|
||||
public bool $foreign_business_tax_exempt = true;
|
||||
|
||||
public bool $foreign_consumer_tax_exempt = true;
|
||||
|
||||
public array $eu_country_codes = [
|
||||
'AT', // Austria
|
||||
'BE', // Belgium
|
||||
'BG', // Bulgaria
|
||||
'CY', // Cyprus
|
||||
'CZ', // Czech Republic
|
||||
'DE', // Germany
|
||||
'DK', // Denmark
|
||||
'EE', // Estonia
|
||||
'ES', // Spain
|
||||
'FI', // Finland
|
||||
'FR', // France
|
||||
'GR', // Greece
|
||||
'HR', // Croatia
|
||||
'HU', // Hungary
|
||||
'IE', // Ireland
|
||||
'IT', // Italy
|
||||
'LT', // Lithuania
|
||||
'LU', // Luxembourg
|
||||
'LV', // Latvia
|
||||
'MT', // Malta
|
||||
'NL', // Netherlands
|
||||
'PL', // Poland
|
||||
'PT', // Portugal
|
||||
'RO', // Romania
|
||||
'SE', // Sweden
|
||||
'SI', // Slovenia
|
||||
'SK', // Slovakia
|
||||
];
|
||||
|
||||
/** EU TAXES */
|
||||
|
||||
|
||||
/** US TAXES */
|
||||
/** US TAXES */
|
||||
|
||||
public string $tax_name1 = '';
|
||||
public float $tax_rate1 = 0;
|
||||
|
||||
public string $tax_name2 = '';
|
||||
public float $tax_rate2 = 0;
|
||||
|
||||
public string $tax_name3 = '';
|
||||
public float $tax_rate3 = 0;
|
||||
|
||||
protected ?Client $client;
|
||||
|
||||
protected ?Response $tax_data;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function init(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setClient(Client $client): self
|
||||
{
|
||||
$this->client = $client;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTaxData(Response $tax_data): self
|
||||
{
|
||||
$this->tax_data = $tax_data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function tax($product_tax_type): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxByType($product_tax_type): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxReduced(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxExempt(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxDigital(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxService(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxShipping(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxPhysical(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function default(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function override(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function calculateRates(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
233
app/DataMapper/Tax/DE/Rule.php
Normal file
233
app/DataMapper/Tax/DE/Rule.php
Normal file
@ -0,0 +1,233 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Tax\DE;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Product;
|
||||
use Illuminate\Support\Str;
|
||||
use App\DataMapper\Tax\BaseRule;
|
||||
use App\DataMapper\Tax\RuleInterface;
|
||||
use App\DataMapper\Tax\ZipTax\Response;
|
||||
|
||||
class Rule extends BaseRule implements RuleInterface
|
||||
{
|
||||
public string $vendor_country_code = 'DE';
|
||||
|
||||
public string $client_country_code = 'DE';
|
||||
|
||||
public bool $consumer_tax_exempt = false;
|
||||
|
||||
public bool $business_tax_exempt = false;
|
||||
|
||||
public bool $eu_business_tax_exempt = true;
|
||||
|
||||
public bool $foreign_business_tax_exempt = true;
|
||||
|
||||
public bool $foreign_consumer_tax_exempt = true;
|
||||
|
||||
public string $tax_name1 = '';
|
||||
|
||||
public float $tax_rate1 = 0;
|
||||
|
||||
public string $tax_name2 = '';
|
||||
|
||||
public float $tax_rate2 = 0;
|
||||
|
||||
public string $tax_name3 = '';
|
||||
|
||||
public float $tax_rate3 = 0;
|
||||
|
||||
public float $vat_rate = 0;
|
||||
|
||||
public float $reduced_vat_rate = 0;
|
||||
|
||||
protected ?Client $client;
|
||||
|
||||
protected ?Response $tax_data;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function init(): self
|
||||
{
|
||||
$this->client_country_code = $this->client->shipping_country ? $this->client->shipping_country->iso_3166_2 : $this->client->country->iso_3166_2;
|
||||
$this->calculateRates();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setClient(Client $client): self
|
||||
{
|
||||
$this->client = $client;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTaxData(Response $tax_data): self
|
||||
{
|
||||
$this->tax_data = $tax_data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//need to add logic here to capture if
|
||||
public function tax($type): self
|
||||
{
|
||||
|
||||
|
||||
if ($this->client->is_tax_exempt) {
|
||||
return $this->taxExempt();
|
||||
} elseif ($this->client->company->tax_data->regions->EU->tax_all) {
|
||||
|
||||
$this->tax_rate1 = $this->vat_rate;
|
||||
$this->tax_name1 = "MwSt.";
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($type)
|
||||
return $this->taxByType($type);
|
||||
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
public function taxByType($product_tax_type): self
|
||||
{
|
||||
|
||||
if ($this->client->is_tax_exempt) {
|
||||
return $this->taxExempt();
|
||||
}
|
||||
|
||||
if(!$product_tax_type)
|
||||
return $this;
|
||||
|
||||
match($product_tax_type){
|
||||
Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(),
|
||||
Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(),
|
||||
Product::PRODUCT_TYPE_SERVICE => $this->taxService(),
|
||||
Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(),
|
||||
Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(),
|
||||
Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(),
|
||||
Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(),
|
||||
default => $this->default(),
|
||||
};
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxReduced(): self
|
||||
{
|
||||
$this->tax_rate1 = $this->reduced_vat_rate;
|
||||
$this->tax_name1 = 'ermäßigte MwSt.';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxExempt(): self
|
||||
{
|
||||
$this->tax_name1 = '';
|
||||
$this->tax_rate1 = 0;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxDigital(): self
|
||||
{
|
||||
$this->tax();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxService(): self
|
||||
{
|
||||
$this->tax();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxShipping(): self
|
||||
{
|
||||
$this->tax();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxPhysical(): self
|
||||
{
|
||||
$this->tax();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function default(): self
|
||||
{
|
||||
|
||||
$this->tax_name1 = '';
|
||||
$this->tax_rate1 = 0;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function override(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function calculateRates(): self
|
||||
{
|
||||
|
||||
if ($this->client->is_tax_exempt) {
|
||||
$this->vat_rate = 0;
|
||||
$this->reduced_vat_rate = 0;
|
||||
}
|
||||
elseif($this->client_country_code != $this->vendor_country_code && in_array($this->client_country_code, $this->eu_country_codes) && $this->client->has_valid_vat_number && $this->eu_business_tax_exempt)
|
||||
{
|
||||
$this->vat_rate = 0;
|
||||
$this->reduced_vat_rate = 0;
|
||||
// nlog("euro zone and tax exempt");
|
||||
}
|
||||
elseif(!in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && ($this->foreign_consumer_tax_exempt || $this->foreign_business_tax_exempt)) //foreign + tax exempt
|
||||
{
|
||||
$this->vat_rate = 0;
|
||||
$this->reduced_vat_rate = 0;
|
||||
// nlog("foreign and tax exempt");
|
||||
}
|
||||
elseif(in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && !$this->client->has_valid_vat_number) //eu country / no valid vat
|
||||
{
|
||||
if(($this->vendor_country_code != $this->client_country_code) && $this->client->company->tax_data->regions->EU->has_sales_above_threshold)
|
||||
{
|
||||
$this->vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->vat_rate;
|
||||
$this->reduced_vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_vat_rate;
|
||||
// nlog("eu zone with sales above threshold");
|
||||
}
|
||||
else {
|
||||
$this->vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->vat_rate;
|
||||
$this->reduced_vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_vat_rate;
|
||||
// nlog("same eu country with");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->vat_rate;
|
||||
$this->reduced_vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_vat_rate;
|
||||
// nlog("default tax");
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
46
app/DataMapper/Tax/RuleInterface.php
Normal file
46
app/DataMapper/Tax/RuleInterface.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Tax;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\DataMapper\Tax\ZipTax\Response;
|
||||
|
||||
interface RuleInterface
|
||||
{
|
||||
public function init();
|
||||
|
||||
public function tax(mixed $type);
|
||||
|
||||
public function taxByType($type);
|
||||
|
||||
public function taxExempt();
|
||||
|
||||
public function taxDigital();
|
||||
|
||||
public function taxService();
|
||||
|
||||
public function taxShipping();
|
||||
|
||||
public function taxPhysical();
|
||||
|
||||
public function taxReduced();
|
||||
|
||||
public function default();
|
||||
|
||||
public function override();
|
||||
|
||||
public function setClient(Client $client);
|
||||
|
||||
public function setTaxData(Response $tax_data);
|
||||
|
||||
public function calculateRates();
|
||||
}
|
31
app/DataMapper/Tax/TaxData.php
Normal file
31
app/DataMapper/Tax/TaxData.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Tax;
|
||||
|
||||
use App\DataMapper\Tax\ZipTax\Response;
|
||||
|
||||
/**
|
||||
* InvoiceTaxData
|
||||
*
|
||||
* Definition for the invoice tax data structure
|
||||
*/
|
||||
class TaxData
|
||||
{
|
||||
public int $updated_at;
|
||||
|
||||
public function __construct(public Response $origin)
|
||||
{
|
||||
foreach($origin as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
}
|
316
app/DataMapper/Tax/TaxModel.php
Normal file
316
app/DataMapper/Tax/TaxModel.php
Normal file
@ -0,0 +1,316 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Tax;
|
||||
|
||||
class TaxModel
|
||||
{
|
||||
// public string $seller_region = 'US';
|
||||
|
||||
public string $seller_subregion = 'CA';
|
||||
|
||||
public object $regions;
|
||||
|
||||
public function __construct(public ?TaxModel $model = null)
|
||||
{
|
||||
|
||||
if(!$this->model)
|
||||
$this->regions = $this->init();
|
||||
else
|
||||
$this->regions = $model;
|
||||
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->regions = new \stdClass();
|
||||
$this->regions->US = new \stdClass();
|
||||
$this->regions->EU = new \stdClass();
|
||||
|
||||
$this->usRegion()
|
||||
->euRegion();
|
||||
|
||||
|
||||
return $this->regions;
|
||||
}
|
||||
|
||||
private function usRegion(): self
|
||||
{
|
||||
$this->regions->US->has_sales_above_threshold = false;
|
||||
$this->regions->US->tax_all_subregions = false;
|
||||
$this->usSubRegions();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function euRegion(): self
|
||||
{
|
||||
|
||||
$this->regions->EU->has_sales_above_threshold = false;
|
||||
$this->regions->EU->tax_all_subregions = false;
|
||||
$this->regions->EU->vat_threshold = 10000;
|
||||
$this->euSubRegions();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function usSubRegions(): self
|
||||
{
|
||||
$this->regions->US->subregions = new \stdClass();
|
||||
$this->regions->US->subregions->AL = new \stdClass();
|
||||
$this->regions->US->subregions->AL->apply_tax = false;
|
||||
$this->regions->US->subregions->AK = new \stdClass();
|
||||
$this->regions->US->subregions->AK->apply_tax = false;
|
||||
$this->regions->US->subregions->AZ = new \stdClass();
|
||||
$this->regions->US->subregions->AZ->apply_tax = false;
|
||||
$this->regions->US->subregions->AR = new \stdClass();
|
||||
$this->regions->US->subregions->AR->apply_tax = false;
|
||||
$this->regions->US->subregions->CA = new \stdClass();
|
||||
$this->regions->US->subregions->CA->apply_tax = false;
|
||||
$this->regions->US->subregions->CO = new \stdClass();
|
||||
$this->regions->US->subregions->CO->apply_tax = false;
|
||||
$this->regions->US->subregions->CT = new \stdClass();
|
||||
$this->regions->US->subregions->CT->apply_tax = false;
|
||||
$this->regions->US->subregions->DE = new \stdClass();
|
||||
$this->regions->US->subregions->DE->apply_tax = false;
|
||||
$this->regions->US->subregions->FL = new \stdClass();
|
||||
$this->regions->US->subregions->FL->apply_tax = false;
|
||||
$this->regions->US->subregions->GA = new \stdClass();
|
||||
$this->regions->US->subregions->GA->apply_tax = false;
|
||||
$this->regions->US->subregions->HI = new \stdClass();
|
||||
$this->regions->US->subregions->HI->apply_tax = false;
|
||||
$this->regions->US->subregions->ID = new \stdClass();
|
||||
$this->regions->US->subregions->ID->apply_tax = false;
|
||||
$this->regions->US->subregions->IL = new \stdClass();
|
||||
$this->regions->US->subregions->IL->apply_tax = false;
|
||||
$this->regions->US->subregions->IN = new \stdClass();
|
||||
$this->regions->US->subregions->IN->apply_tax = false;
|
||||
$this->regions->US->subregions->IA = new \stdClass();
|
||||
$this->regions->US->subregions->IA->apply_tax = false;
|
||||
$this->regions->US->subregions->KS = new \stdClass();
|
||||
$this->regions->US->subregions->KS->apply_tax = false;
|
||||
$this->regions->US->subregions->KY = new \stdClass();
|
||||
$this->regions->US->subregions->KY->apply_tax = false;
|
||||
$this->regions->US->subregions->LA = new \stdClass();
|
||||
$this->regions->US->subregions->LA->apply_tax = false;
|
||||
$this->regions->US->subregions->ME = new \stdClass();
|
||||
$this->regions->US->subregions->ME->apply_tax = false;
|
||||
$this->regions->US->subregions->MD = new \stdClass();
|
||||
$this->regions->US->subregions->MD->apply_tax = false;
|
||||
$this->regions->US->subregions->MA = new \stdClass();
|
||||
$this->regions->US->subregions->MA->apply_tax = false;
|
||||
$this->regions->US->subregions->MI = new \stdClass();
|
||||
$this->regions->US->subregions->MI->apply_tax = false;
|
||||
$this->regions->US->subregions->MN = new \stdClass();
|
||||
$this->regions->US->subregions->MN->apply_tax = false;
|
||||
$this->regions->US->subregions->MS = new \stdClass();
|
||||
$this->regions->US->subregions->MS->apply_tax = false;
|
||||
$this->regions->US->subregions->MO = new \stdClass();
|
||||
$this->regions->US->subregions->MO->apply_tax = false;
|
||||
$this->regions->US->subregions->MT = new \stdClass();
|
||||
$this->regions->US->subregions->MT->apply_tax = false;
|
||||
$this->regions->US->subregions->NE = new \stdClass();
|
||||
$this->regions->US->subregions->NE->apply_tax = false;
|
||||
$this->regions->US->subregions->NV = new \stdClass();
|
||||
$this->regions->US->subregions->NV->apply_tax = false;
|
||||
$this->regions->US->subregions->NH = new \stdClass();
|
||||
$this->regions->US->subregions->NH->apply_tax = false;
|
||||
$this->regions->US->subregions->NJ = new \stdClass();
|
||||
$this->regions->US->subregions->NJ->apply_tax = false;
|
||||
$this->regions->US->subregions->NM = new \stdClass();
|
||||
$this->regions->US->subregions->NM->apply_tax = false;
|
||||
$this->regions->US->subregions->NY = new \stdClass();
|
||||
$this->regions->US->subregions->NY->apply_tax = false;
|
||||
$this->regions->US->subregions->NC = new \stdClass();
|
||||
$this->regions->US->subregions->NC->apply_tax = false;
|
||||
$this->regions->US->subregions->ND = new \stdClass();
|
||||
$this->regions->US->subregions->ND->apply_tax = false;
|
||||
$this->regions->US->subregions->OH = new \stdClass();
|
||||
$this->regions->US->subregions->OH->apply_tax = false;
|
||||
$this->regions->US->subregions->OK = new \stdClass();
|
||||
$this->regions->US->subregions->OK->apply_tax = false;
|
||||
$this->regions->US->subregions->OR = new \stdClass();
|
||||
$this->regions->US->subregions->OR->apply_tax = false;
|
||||
$this->regions->US->subregions->PA = new \stdClass();
|
||||
$this->regions->US->subregions->PA->apply_tax = false;
|
||||
$this->regions->US->subregions->RI = new \stdClass();
|
||||
$this->regions->US->subregions->RI->apply_tax = false;
|
||||
$this->regions->US->subregions->SC = new \stdClass();
|
||||
$this->regions->US->subregions->SC->apply_tax = false;
|
||||
$this->regions->US->subregions->SD = new \stdClass();
|
||||
$this->regions->US->subregions->SD->apply_tax = false;
|
||||
$this->regions->US->subregions->TN = new \stdClass();
|
||||
$this->regions->US->subregions->TN->apply_tax = false;
|
||||
$this->regions->US->subregions->TX = new \stdClass();
|
||||
$this->regions->US->subregions->TX->apply_tax = false;
|
||||
$this->regions->US->subregions->UT = new \stdClass();
|
||||
$this->regions->US->subregions->UT->apply_tax = false;
|
||||
$this->regions->US->subregions->VT = new \stdClass();
|
||||
$this->regions->US->subregions->VT->apply_tax = false;
|
||||
$this->regions->US->subregions->VA = new \stdClass();
|
||||
$this->regions->US->subregions->VA->apply_tax = false;
|
||||
$this->regions->US->subregions->WA = new \stdClass();
|
||||
$this->regions->US->subregions->WA->apply_tax = false;
|
||||
$this->regions->US->subregions->WV = new \stdClass();
|
||||
$this->regions->US->subregions->WV->apply_tax = false;
|
||||
$this->regions->US->subregions->WI = new \stdClass();
|
||||
$this->regions->US->subregions->WI->apply_tax = false;
|
||||
$this->regions->US->subregions->WY = new \stdClass();
|
||||
$this->regions->US->subregions->WY->apply_tax = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function euSubRegions(): self
|
||||
{
|
||||
|
||||
$this->regions->EU->subregions = new \stdClass();
|
||||
|
||||
$this->regions->EU->subregions->AT = new \stdClass();
|
||||
$this->regions->EU->subregions->AT->vat_rate = 21;
|
||||
$this->regions->EU->subregions->AT->reduced_vat_rate = 11;
|
||||
$this->regions->EU->subregions->AT->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->BE = new \stdClass();
|
||||
$this->regions->EU->subregions->BE->vat_rate = 21;
|
||||
$this->regions->EU->subregions->BE->reduced_vat_rate = 6;
|
||||
$this->regions->EU->subregions->BE->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->BG = new \stdClass();
|
||||
$this->regions->EU->subregions->BG->vat_rate = 20;
|
||||
$this->regions->EU->subregions->BG->reduced_vat_rate = 9;
|
||||
$this->regions->EU->subregions->BG->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->CY = new \stdClass();
|
||||
$this->regions->EU->subregions->CY->vat_rate = 19;
|
||||
$this->regions->EU->subregions->CY->reduced_vat_rate = 9;
|
||||
$this->regions->EU->subregions->CY->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->CZ = new \stdClass();
|
||||
$this->regions->EU->subregions->CZ->vat_rate = 21;
|
||||
$this->regions->EU->subregions->CZ->reduced_vat_rate = 15;
|
||||
$this->regions->EU->subregions->CZ->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->DE = new \stdClass();
|
||||
$this->regions->EU->subregions->DE->vat_rate = 19;
|
||||
$this->regions->EU->subregions->DE->reduced_vat_rate = 7;
|
||||
$this->regions->EU->subregions->DE->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->DK = new \stdClass();
|
||||
$this->regions->EU->subregions->DK->vat_rate = 25;
|
||||
$this->regions->EU->subregions->DK->reduced_vat_rate = 0;
|
||||
$this->regions->EU->subregions->DK->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->EE = new \stdClass();
|
||||
$this->regions->EU->subregions->EE->vat_rate = 20;
|
||||
$this->regions->EU->subregions->EE->reduced_vat_rate = 9;
|
||||
$this->regions->EU->subregions->EE->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->ES = new \stdClass();
|
||||
$this->regions->EU->subregions->ES->vat_rate = 21;
|
||||
$this->regions->EU->subregions->ES->reduced_vat_rate = 10;
|
||||
$this->regions->EU->subregions->ES->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->FI = new \stdClass();
|
||||
$this->regions->EU->subregions->FI->vat_rate = 24;
|
||||
$this->regions->EU->subregions->FI->reduced_vat_rate = 14;
|
||||
$this->regions->EU->subregions->FI->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->FR = new \stdClass();
|
||||
$this->regions->EU->subregions->FR->vat_rate = 20;
|
||||
$this->regions->EU->subregions->FR->reduced_vat_rate = 5.5;
|
||||
$this->regions->EU->subregions->FR->apply_tax = false;
|
||||
|
||||
// $this->regions->EU->subregions->GB = new \stdClass();
|
||||
// $this->regions->EU->subregions->GB->vat_rate = 20;
|
||||
// $this->regions->EU->subregions->GB->reduced_vat_rate = 0;
|
||||
// $this->regions->EU->subregions->GB->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->GR = new \stdClass();
|
||||
$this->regions->EU->subregions->GR->vat_rate = 24;
|
||||
$this->regions->EU->subregions->GR->reduced_vat_rate = 13;
|
||||
$this->regions->EU->subregions->GR->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->HR = new \stdClass();
|
||||
$this->regions->EU->subregions->HR->vat_rate = 25;
|
||||
$this->regions->EU->subregions->HR->reduced_vat_rate = 5;
|
||||
$this->regions->EU->subregions->HR->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->HU = new \stdClass();
|
||||
$this->regions->EU->subregions->HU->vat_rate = 27;
|
||||
$this->regions->EU->subregions->HU->reduced_vat_rate = 5;
|
||||
$this->regions->EU->subregions->HU->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->IE = new \stdClass();
|
||||
$this->regions->EU->subregions->IE->vat_rate = 23;
|
||||
$this->regions->EU->subregions->IE->reduced_vat_rate = 0;
|
||||
$this->regions->EU->subregions->IE->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->IT = new \stdClass();
|
||||
$this->regions->EU->subregions->IT->vat_rate = 22;
|
||||
$this->regions->EU->subregions->IT->reduced_vat_rate = 10;
|
||||
$this->regions->EU->subregions->IT->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->LT = new \stdClass();
|
||||
$this->regions->EU->subregions->LT->vat_rate = 21;
|
||||
$this->regions->EU->subregions->LT->reduced_vat_rate = 9;
|
||||
$this->regions->EU->subregions->LT->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->LU = new \stdClass();
|
||||
$this->regions->EU->subregions->LU->vat_rate = 17;
|
||||
$this->regions->EU->subregions->LU->reduced_vat_rate = 3;
|
||||
$this->regions->EU->subregions->LU->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->LV = new \stdClass();
|
||||
$this->regions->EU->subregions->LV->vat_rate = 21;
|
||||
$this->regions->EU->subregions->LV->reduced_vat_rate = 12;
|
||||
$this->regions->EU->subregions->LV->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->MT = new \stdClass();
|
||||
$this->regions->EU->subregions->MT->vat_rate = 18;
|
||||
$this->regions->EU->subregions->MT->reduced_vat_rate = 5;
|
||||
$this->regions->EU->subregions->MT->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->NL = new \stdClass();
|
||||
$this->regions->EU->subregions->NL->vat_rate = 21;
|
||||
$this->regions->EU->subregions->NL->reduced_vat_rate = 9;
|
||||
$this->regions->EU->subregions->NL->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->PT = new \stdClass();
|
||||
$this->regions->EU->subregions->PT->vat_rate = 23;
|
||||
$this->regions->EU->subregions->PT->reduced_vat_rate = 6;
|
||||
$this->regions->EU->subregions->PT->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->RO = new \stdClass();
|
||||
$this->regions->EU->subregions->RO->vat_rate = 19;
|
||||
$this->regions->EU->subregions->RO->reduced_vat_rate = 5;
|
||||
$this->regions->EU->subregions->RO->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->SE = new \stdClass();
|
||||
$this->regions->EU->subregions->SE->vat_rate = 25;
|
||||
$this->regions->EU->subregions->SE->reduced_vat_rate = 12;
|
||||
$this->regions->EU->subregions->SE->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->SI = new \stdClass();
|
||||
$this->regions->EU->subregions->SI->vat_rate = 22;
|
||||
$this->regions->EU->subregions->SI->reduced_vat_rate = 9.5;
|
||||
$this->regions->EU->subregions->SI->apply_tax = false;
|
||||
|
||||
$this->regions->EU->subregions->SK = new \stdClass();
|
||||
$this->regions->EU->subregions->SK->vat_rate = 20;
|
||||
$this->regions->EU->subregions->SK->reduced_vat_rate = 10;
|
||||
$this->regions->EU->subregions->SK->apply_tax = false;
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
}
|
161
app/DataMapper/Tax/US/Rule.php
Normal file
161
app/DataMapper/Tax/US/Rule.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Tax\US;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Product;
|
||||
use App\DataMapper\Tax\RuleInterface;
|
||||
use App\DataMapper\Tax\ZipTax\Response;
|
||||
|
||||
class Rule implements RuleInterface
|
||||
{
|
||||
|
||||
public string $tax_name1 = '';
|
||||
public float $tax_rate1 = 0;
|
||||
|
||||
public string $tax_name2 = '';
|
||||
public float $tax_rate2 = 0;
|
||||
|
||||
public string $tax_name3 = '';
|
||||
public float $tax_rate3 = 0;
|
||||
|
||||
public ?Client $client;
|
||||
|
||||
public ?Response $tax_data;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function override()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTaxData(Response $tax_data): self
|
||||
{
|
||||
$this->tax_data = $tax_data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setClient(Client $client):self
|
||||
{
|
||||
$this->client = $client;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function tax($type): self
|
||||
{
|
||||
|
||||
if ($this->client->is_tax_exempt) {
|
||||
return $this->taxExempt();
|
||||
}
|
||||
else if($this->client->company->tax_data->regions->US->tax_all){
|
||||
|
||||
$this->tax_rate1 = $this->tax_data->taxSales * 100;
|
||||
$this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if($type)
|
||||
return $this->taxByType($type);
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
public function taxByType($product_tax_type): self
|
||||
{
|
||||
if(!$product_tax_type)
|
||||
return $this;
|
||||
|
||||
match($product_tax_type){
|
||||
Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(),
|
||||
Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(),
|
||||
Product::PRODUCT_TYPE_SERVICE => $this->taxService(),
|
||||
Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(),
|
||||
Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(),
|
||||
Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(),
|
||||
Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(),
|
||||
default => $this->default(),
|
||||
};
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxExempt(): self
|
||||
{
|
||||
$this->tax_name1 = '';
|
||||
$this->tax_rate1 = 0;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxDigital(): self
|
||||
{
|
||||
$this->default();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxService(): self
|
||||
{
|
||||
if($this->tax_data->txbService == 'Y')
|
||||
$this->default();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxShipping(): self
|
||||
{
|
||||
if($this->tax_data->txbFreight == 'Y')
|
||||
$this->default();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxPhysical(): self
|
||||
{
|
||||
$this->default();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function default(): self
|
||||
{
|
||||
|
||||
$this->tax_rate1 = $this->tax_data->taxSales * 100;
|
||||
$this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function taxReduced(): self
|
||||
{
|
||||
$this->default();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function init(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function calculateRates(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
108
app/DataMapper/Tax/ZipTax/Response.php
Normal file
108
app/DataMapper/Tax/ZipTax/Response.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataMapper\Tax\ZipTax;
|
||||
|
||||
class Response
|
||||
{
|
||||
public string $version = 'v40';
|
||||
|
||||
public int $rCode = 100;
|
||||
|
||||
/**
|
||||
* [
|
||||
* "geoPostalCode" => "92582",
|
||||
* "geoCity" => "SAN JACINTO",
|
||||
* "geoCounty" => "RIVERSIDE",
|
||||
* "geoState" => "CA",
|
||||
* "taxSales" => 0.0875,
|
||||
* "taxUse" => 0.0875,
|
||||
* "txbService" => "N",
|
||||
* "txbFreight" => "N",
|
||||
* "stateSalesTax" => 0.06,
|
||||
* "stateUseTax" => 0.06,
|
||||
* "citySalesTax" => 0.01,
|
||||
* "cityUseTax" => 0.01,
|
||||
* "cityTaxCode" => "874",
|
||||
* "countySalesTax" => 0.0025,
|
||||
* "countyUseTax" => 0.0025,
|
||||
* "countyTaxCode" => "",
|
||||
* "districtSalesTax" => 0.015,
|
||||
* "districtUseTax" => 0.015,
|
||||
* "district1Code" => "26",
|
||||
* "district1SalesTax" => 0,
|
||||
* "district1UseTax" => 0,
|
||||
* "district2Code" => "26",
|
||||
* "district2SalesTax" => 0.005,
|
||||
* "district2UseTax" => 0.005,
|
||||
* "district3Code" => "",
|
||||
* "district3SalesTax" => 0,
|
||||
* "district3UseTax" => 0,
|
||||
* "district4Code" => "33",
|
||||
* "district4SalesTax" => 0.01,
|
||||
* "district4UseTax" => 0.01,
|
||||
* "district5Code" => "",
|
||||
* "district5SalesTax" => 0,
|
||||
* "district5UseTax" => 0,
|
||||
* "originDestination" => "D",
|
||||
*
|
||||
* ];
|
||||
*
|
||||
*/
|
||||
public string $seller_region = "";
|
||||
//US
|
||||
|
||||
public string $geoPostalCode = "";
|
||||
public string $geoCity = "";
|
||||
public string $geoCounty = "";
|
||||
public string $geoState = "";
|
||||
public float $taxSales = 0;
|
||||
public float $taxUse = 0;
|
||||
public string $txbService = ""; // N = No, Y = Yes
|
||||
public string $txbFreight = ""; // N = No, Y = Yes
|
||||
public float $stateSalesTax = 0;
|
||||
public float $stateUseTax = 0;
|
||||
public float $citySalesTax = 0;
|
||||
public float $cityUseTax = 0;
|
||||
public string $cityTaxCode = "";
|
||||
public float $countySalesTax = 0;
|
||||
public float $countyUseTax = 0;
|
||||
public string $countyTaxCode = "";
|
||||
public float $districtSalesTax = 0;
|
||||
public float $districtUseTax = 0;
|
||||
public string $district1Code = "";
|
||||
public float $district1SalesTax = 0;
|
||||
public float $district1UseTax = 0;
|
||||
public string $district2Code = "";
|
||||
public float $district2SalesTax = 0;
|
||||
public float $district2UseTax = 0;
|
||||
public string $district3Code = "";
|
||||
public float $district3SalesTax = 0;
|
||||
public float $district3UseTax = 0;
|
||||
public string $district4Code = "";
|
||||
public float $district4SalesTax = 0;
|
||||
public float $district4UseTax = 0;
|
||||
public string $district5Code = "";
|
||||
public float $district5SalesTax = 0;
|
||||
public float $district5UseTax = 0;
|
||||
public string $originDestination = "";
|
||||
|
||||
public function __construct($data)
|
||||
{
|
||||
|
||||
foreach($data as $key => $value){
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
220
app/DataMapper/Tax/tax_model.yaml
Normal file
220
app/DataMapper/Tax/tax_model.yaml
Normal file
@ -0,0 +1,220 @@
|
||||
region:
|
||||
US:
|
||||
tax_all_subregions: false
|
||||
seller_subregion: CA
|
||||
has_sales_above_threshold: false
|
||||
subregions:
|
||||
AL:
|
||||
apply_tax: false
|
||||
AK:
|
||||
apply_tax: false
|
||||
AZ:
|
||||
apply_tax: false
|
||||
AR:
|
||||
apply_tax: false
|
||||
CA:
|
||||
apply_tax: false
|
||||
CO:
|
||||
apply_tax: false
|
||||
CT:
|
||||
apply_tax: false
|
||||
DE:
|
||||
apply_tax: false
|
||||
FL:
|
||||
apply_tax: false
|
||||
GA:
|
||||
apply_tax: false
|
||||
HI:
|
||||
apply_tax: false
|
||||
ID:
|
||||
apply_tax: false
|
||||
IL:
|
||||
apply_tax: false
|
||||
IN:
|
||||
apply_tax: false
|
||||
IA:
|
||||
apply_tax: false
|
||||
KS:
|
||||
apply_tax: false
|
||||
KY:
|
||||
apply_tax: false
|
||||
LA:
|
||||
apply_tax: false
|
||||
ME:
|
||||
apply_tax: false
|
||||
MD:
|
||||
apply_tax: false
|
||||
MA:
|
||||
apply_tax: false
|
||||
MI:
|
||||
apply_tax: false
|
||||
MN:
|
||||
apply_tax: false
|
||||
MS:
|
||||
apply_tax: false
|
||||
MO:
|
||||
apply_tax: false
|
||||
MT:
|
||||
apply_tax: false
|
||||
NE:
|
||||
apply_tax: false
|
||||
NV:
|
||||
apply_tax: false
|
||||
NH:
|
||||
apply_tax: false
|
||||
NJ:
|
||||
apply_tax: false
|
||||
NM:
|
||||
apply_tax: false
|
||||
NY:
|
||||
apply_tax: false
|
||||
NC:
|
||||
apply_tax: false
|
||||
ND:
|
||||
apply_tax: false
|
||||
OH:
|
||||
apply_tax: false
|
||||
OK:
|
||||
apply_tax: false
|
||||
OR:
|
||||
apply_tax: false
|
||||
PA:
|
||||
apply_tax: false
|
||||
RI:
|
||||
apply_tax: false
|
||||
SC:
|
||||
apply_tax: false
|
||||
SD:
|
||||
apply_tax: false
|
||||
TN:
|
||||
apply_tax: false
|
||||
TX:
|
||||
apply_tax: false
|
||||
UT:
|
||||
apply_tax: false
|
||||
VT:
|
||||
apply_tax: false
|
||||
VA:
|
||||
apply_tax: false
|
||||
WA:
|
||||
apply_tax: false
|
||||
WV:
|
||||
apply_tax: false
|
||||
WI:
|
||||
apply_tax: false
|
||||
WY:
|
||||
apply_tax: false
|
||||
EU:
|
||||
tax_all: false
|
||||
vat_threshold: 10000
|
||||
has_sales_above_threshold: false
|
||||
seller_region: DE
|
||||
subregions:
|
||||
AT:
|
||||
vat: 21
|
||||
reduced_vat: 11
|
||||
apply_tax: false
|
||||
BE:
|
||||
vat: 21
|
||||
reduced_vat: 6
|
||||
apply_tax: false
|
||||
BG:
|
||||
vat: 20
|
||||
reduced_vat: 9
|
||||
apply_tax: false
|
||||
CY:
|
||||
vat: 19
|
||||
reduced_vat: 9
|
||||
apply_tax: false
|
||||
CZ:
|
||||
vat: 21
|
||||
reduced_vat: 15
|
||||
apply_tax: false
|
||||
DE:
|
||||
vat: 19
|
||||
reduced_vat: 7
|
||||
apply_tax: false
|
||||
DK:
|
||||
vat: 25
|
||||
reduced_vat: 0
|
||||
apply_tax: false
|
||||
EE:
|
||||
vat: 20
|
||||
reduced_vat: 9
|
||||
apply_tax: false
|
||||
ES:
|
||||
vat: 21
|
||||
reduced_vat: 10
|
||||
apply_tax: false
|
||||
FI:
|
||||
vat: 24
|
||||
reduced_vat: 14
|
||||
apply_tax: false
|
||||
FR:
|
||||
vat: 20
|
||||
reduced_vat: 5.5
|
||||
apply_tax: false
|
||||
GB:
|
||||
vat: 20
|
||||
reduced_vat: 0
|
||||
apply_tax: false
|
||||
GR:
|
||||
vat: 24
|
||||
reduced_vat: 13
|
||||
apply_tax: false
|
||||
HR:
|
||||
vat: 25
|
||||
reduced_vat: 5
|
||||
apply_tax: false
|
||||
HU:
|
||||
vat: 27
|
||||
reduced_vat: 5
|
||||
apply_tax: false
|
||||
IE:
|
||||
vat: 23
|
||||
reduced_vat: 0
|
||||
apply_tax: false
|
||||
IT:
|
||||
vat: 22
|
||||
reduced_vat: 10
|
||||
apply_tax: false
|
||||
LT:
|
||||
vat: 21
|
||||
reduced_vat: 9
|
||||
apply_tax: false
|
||||
LU:
|
||||
vat: 17
|
||||
reduced_vat: 3
|
||||
apply_tax: false
|
||||
LV:
|
||||
vat: 21
|
||||
reduced_vat: 12
|
||||
apply_tax: false
|
||||
MT:
|
||||
vat: 18
|
||||
reduced_vat: 5
|
||||
apply_tax: false
|
||||
NL:
|
||||
vat: 21
|
||||
reduced_vat: 9
|
||||
apply_tax: false
|
||||
PT:
|
||||
vat: 23
|
||||
reduced_vat: 6
|
||||
apply_tax: false
|
||||
RO:
|
||||
vat: 19
|
||||
reduced_vat: 5
|
||||
apply_tax: false
|
||||
SE:
|
||||
vat: 25
|
||||
reduced_vat: 12
|
||||
apply_tax: false
|
||||
SI:
|
||||
vat: 22
|
||||
reduced_vat: 9.5
|
||||
apply_tax: false
|
||||
SK:
|
||||
vat: 20
|
||||
reduced_vat: 10
|
||||
apply_tax: false
|
24
app/DataProviders/DesignBlocks.php
Normal file
24
app/DataProviders/DesignBlocks.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?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\DataProviders;
|
||||
|
||||
class DesignBlocks
|
||||
{
|
||||
public function __construct(
|
||||
public string $includes = '',
|
||||
public string $header = '',
|
||||
public string $body = '',
|
||||
public string $footer = ''
|
||||
) {
|
||||
}
|
||||
}
|
@ -13,14 +13,18 @@ namespace App\Events\Invoice;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class InvoiceWasCreated.
|
||||
*/
|
||||
class InvoiceWasCreated
|
||||
class InvoiceWasCreated implements ShouldBroadcast
|
||||
{
|
||||
use SerializesModels;
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Invoice
|
||||
@ -44,4 +48,25 @@ class InvoiceWasCreated
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channels the event should broadcast on.
|
||||
*
|
||||
* @return PrivateChannel|array
|
||||
*/
|
||||
public function broadcastOn()
|
||||
{
|
||||
return ['simple-channel'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the data to broadcast.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function broadcastWith(): array
|
||||
{
|
||||
return ['id' => 'value'];
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@
|
||||
namespace App\Export\CSV;
|
||||
|
||||
use App\Models\Client;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class BaseExport
|
||||
{
|
||||
@ -34,7 +34,6 @@ class BaseExport
|
||||
protected function filterByClients($query)
|
||||
{
|
||||
if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') {
|
||||
|
||||
$client = Client::withTrashed()->find($this->input['client_id']);
|
||||
$this->client_description = $client->present()->name;
|
||||
return $query->where('client_id', $this->input['client_id']);
|
||||
|
@ -34,6 +34,8 @@ class InvoiceItemExport extends BaseExport
|
||||
'amount' => 'amount',
|
||||
'balance' => 'balance',
|
||||
'client' => 'client_id',
|
||||
'client_number' => 'client.number',
|
||||
'client_id_number' => 'client.id_number',
|
||||
'custom_surcharge1' => 'custom_surcharge1',
|
||||
'custom_surcharge2' => 'custom_surcharge2',
|
||||
'custom_surcharge3' => 'custom_surcharge3',
|
||||
@ -198,6 +200,8 @@ class InvoiceItemExport extends BaseExport
|
||||
|
||||
// if(in_array('client_id', $this->input['report_keys']))
|
||||
$entity['client'] = $invoice->client->present()->name();
|
||||
$entity['client_id_number'] = $invoice->client->id_number;
|
||||
$entity['client_number'] = $invoice->client->number;
|
||||
|
||||
// if(in_array('status_id', $this->input['report_keys']))
|
||||
$entity['status'] = $invoice->stringStatus($invoice->status_id);
|
||||
|
@ -11,15 +11,15 @@
|
||||
|
||||
namespace App\Export\CSV;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use League\Csv\Writer;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Product;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use League\Csv\Writer;
|
||||
|
||||
class ProductSalesExport extends BaseExport
|
||||
{
|
||||
@ -117,10 +117,9 @@ class ProductSalesExport extends BaseExport
|
||||
});
|
||||
|
||||
|
||||
$grouped = $this->sales->groupBy('product_key')->map(function ($key, $value){
|
||||
|
||||
$grouped = $this->sales->groupBy('product_key')->map(function ($key, $value) {
|
||||
$data = [
|
||||
'product' => $value,
|
||||
'product' => $value,
|
||||
'quantity' => $key->sum('quantity'),
|
||||
'markup' => $key->sum('markup'),
|
||||
'profit' => $key->sum('profit'),
|
||||
@ -183,11 +182,10 @@ class ProductSalesExport extends BaseExport
|
||||
$this->sales->push($entity);
|
||||
|
||||
return $entity;
|
||||
|
||||
}
|
||||
|
||||
private function decorateAdvancedFields(Invoice $invoice, $entity) :array
|
||||
{
|
||||
{
|
||||
$product = $this->getProduct($entity['product_key']);
|
||||
|
||||
$entity['cost'] = $product->cost ?? 0;
|
||||
@ -199,31 +197,31 @@ class ProductSalesExport extends BaseExport
|
||||
$entity['date'] = Carbon::parse($invoice->date)->format($this->company->date_format());
|
||||
|
||||
$entity['discount'] = $this->calculateDiscount($invoice, $entity);
|
||||
$entity['markup'] = round(((($entity['price'] - $entity['discount'] - $entity['cost']) / $unit_cost) * 100),2);
|
||||
$entity['markup'] = round(((($entity['price'] - $entity['discount'] - $entity['cost']) / $unit_cost) * 100), 2);
|
||||
|
||||
$entity['net_total'] = $entity['price'] - $entity['discount'];
|
||||
$entity['profit'] = $entity['price'] - $entity['discount'] - $entity['cost'];
|
||||
|
||||
if(strlen($entity['tax_name1']) > 1) {
|
||||
if (strlen($entity['tax_name1']) > 1) {
|
||||
$entity['tax_name1'] = $entity['tax_name1'] . ' [' . $entity['tax_rate1'] . '%]';
|
||||
$entity['tax_amount1'] = $this->calculateTax($invoice, $entity['line_total'], $entity['tax_rate1']);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
$entity['tax_amount1'] = 0;
|
||||
}
|
||||
|
||||
if(strlen($entity['tax_name2']) > 1) {
|
||||
if (strlen($entity['tax_name2']) > 1) {
|
||||
$entity['tax_name2'] = $entity['tax_name2'] . ' [' . $entity['tax_rate2'] . '%]';
|
||||
$entity['tax_amount2'] = $this->calculateTax($invoice, $entity['line_total'], $entity['tax_rate2']);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
$entity['tax_amount2'] = 0;
|
||||
}
|
||||
|
||||
if(strlen($entity['tax_name3']) > 1) {
|
||||
if (strlen($entity['tax_name3']) > 1) {
|
||||
$entity['tax_name3'] = $entity['tax_name3'] . ' [' . $entity['tax_rate3'] . '%]';
|
||||
$entity['tax_amount3'] = $this->calculateTax($invoice, $entity['line_total'], $entity['tax_rate3']);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
$entity['tax_amount3'] = 0;
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
@ -240,13 +238,11 @@ class ProductSalesExport extends BaseExport
|
||||
{
|
||||
$amount = $amount - ($amount * ($invoice->discount / 100));
|
||||
|
||||
if($invoice->uses_inclusive_taxes) {
|
||||
if ($invoice->uses_inclusive_taxes) {
|
||||
return round($amount - ($amount / (1 + ($tax_rate / 100))), 2);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return round(($amount * $tax_rate / 100), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -258,15 +254,15 @@ class ProductSalesExport extends BaseExport
|
||||
* @param mixed $entity
|
||||
* @return float
|
||||
*/
|
||||
private function calculateDiscount(Invoice $invoice , $entity) :float
|
||||
private function calculateDiscount(Invoice $invoice, $entity) :float
|
||||
{
|
||||
if($entity['discount'] == 0)
|
||||
if ($entity['discount'] == 0) {
|
||||
return 0;
|
||||
|
||||
if($invoice->is_amount_discount && $entity['discount'] != 0) {
|
||||
return $entity['discount'];
|
||||
}
|
||||
elseif(!$invoice->is_amount_discount && $entity['discount'] != 0) {
|
||||
|
||||
if ($invoice->is_amount_discount && $entity['discount'] != 0) {
|
||||
return $entity['discount'];
|
||||
} elseif (!$invoice->is_amount_discount && $entity['discount'] != 0) {
|
||||
return round($entity['line_total'] * ($entity['discount'] / 100), 2);
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Factory;
|
||||
|
||||
use App\DataProviders\DesignBlocks;
|
||||
use App\Models\Design;
|
||||
|
||||
class DesignFactory
|
||||
@ -24,7 +25,7 @@ class DesignFactory
|
||||
$design->is_active = true;
|
||||
$design->is_custom = true;
|
||||
$design->name = '';
|
||||
$design->design = '';
|
||||
$design->design = new DesignBlocks();
|
||||
|
||||
return $design;
|
||||
}
|
||||
|
@ -35,7 +35,8 @@ class ProductFactory
|
||||
$product->custom_value3 = '';
|
||||
$product->custom_value4 = '';
|
||||
$product->is_deleted = 0;
|
||||
|
||||
$product->tax_id = 1;
|
||||
|
||||
return $product;
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ class RecurringInvoiceFactory
|
||||
$invoice->remaining_cycles = -1;
|
||||
$invoice->paid_to_date = 0;
|
||||
$invoice->auto_bill_enabled = false;
|
||||
$invoice->is_proforma = false;
|
||||
$invoice->auto_bill = 'off';
|
||||
|
||||
return $invoice;
|
||||
|
@ -46,6 +46,7 @@ class RecurringInvoiceToInvoiceFactory
|
||||
$invoice->custom_value4 = $recurring_invoice->custom_value4;
|
||||
$invoice->amount = $recurring_invoice->amount;
|
||||
$invoice->uses_inclusive_taxes = $recurring_invoice->uses_inclusive_taxes;
|
||||
$invoice->is_proforma = $recurring_invoice->is_proforma;
|
||||
|
||||
$invoice->custom_surcharge1 = $recurring_invoice->custom_surcharge1;
|
||||
$invoice->custom_surcharge2 = $recurring_invoice->custom_surcharge2;
|
||||
|
@ -85,7 +85,10 @@ class CreditFilters extends QueryFilters
|
||||
->orWhere('credits.custom_value1', 'like', '%'.$filter.'%')
|
||||
->orWhere('credits.custom_value2', 'like', '%'.$filter.'%')
|
||||
->orWhere('credits.custom_value3', 'like', '%'.$filter.'%')
|
||||
->orWhere('credits.custom_value4', 'like', '%'.$filter.'%');
|
||||
->orWhere('credits.custom_value4', 'like', '%'.$filter.'%')
|
||||
->orWhereHas('client', function ($q) use ($filter) {
|
||||
$q->where('name', 'like', '%'.$filter.'%');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,10 @@ class InvoiceFilters extends QueryFilters
|
||||
->orWhere('custom_value1', 'like', '%'.$filter.'%')
|
||||
->orWhere('custom_value2', 'like', '%'.$filter.'%')
|
||||
->orWhere('custom_value3', 'like', '%'.$filter.'%')
|
||||
->orWhere('custom_value4', 'like', '%'.$filter.'%');
|
||||
->orWhere('custom_value4', 'like', '%'.$filter.'%')
|
||||
->orWhereHas('client', function ($q) use ($filter) {
|
||||
$q->where('name', 'like', '%'.$filter.'%');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -128,7 +131,7 @@ class InvoiceFilters extends QueryFilters
|
||||
*/
|
||||
public function upcoming(): Builder
|
||||
{
|
||||
return $this->builder
|
||||
return $this->builder->whereIn('status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
|
||||
->where(function ($query) {
|
||||
$query->whereNull('due_date')
|
||||
->orWhere('due_date', '>', now());
|
||||
|
@ -136,7 +136,7 @@ class PaymentFilters extends QueryFilters
|
||||
* Sorts the list based on $sort.
|
||||
*
|
||||
* formatted as column|asc
|
||||
*
|
||||
*
|
||||
* @param string $sort
|
||||
* @return Builder
|
||||
*/
|
||||
|
@ -63,8 +63,8 @@ class PurchaseOrderFilters extends QueryFilters
|
||||
$po_status[] = PurchaseOrder::STATUS_CANCELLED;
|
||||
}
|
||||
|
||||
if (count($status_parameters) >=1) {
|
||||
$query->whereIn('status_id', $status_parameters);
|
||||
if (count($po_status) >=1) {
|
||||
$query->whereIn('status_id', $po_status);
|
||||
}
|
||||
});
|
||||
|
||||
@ -93,7 +93,10 @@ class PurchaseOrderFilters extends QueryFilters
|
||||
->orWhere('custom_value1', 'like', '%'.$filter.'%')
|
||||
->orWhere('custom_value2', 'like', '%'.$filter.'%')
|
||||
->orWhere('custom_value3', 'like', '%'.$filter.'%')
|
||||
->orWhere('custom_value4', 'like', '%'.$filter.'%');
|
||||
->orWhere('custom_value4', 'like', '%'.$filter.'%')
|
||||
->orWhereHas('vendor', function ($q) use ($filter) {
|
||||
$q->where('name', 'like', '%'.$filter.'%');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,10 @@ class QuoteFilters extends QueryFilters
|
||||
->orwhere('custom_value1', 'like', '%'.$filter.'%')
|
||||
->orWhere('custom_value2', 'like', '%'.$filter.'%')
|
||||
->orWhere('custom_value3', 'like', '%'.$filter.'%')
|
||||
->orWhere('custom_value4', 'like', '%'.$filter.'%');
|
||||
->orWhere('custom_value4', 'like', '%'.$filter.'%')
|
||||
->orWhereHas('client', function ($q) use ($filter) {
|
||||
$q->where('name', 'like', '%'.$filter.'%');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,13 @@
|
||||
|
||||
namespace App\Helpers\Invoice;
|
||||
|
||||
use App\DataMapper\BaseSettings;
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\DataMapper\BaseSettings;
|
||||
use App\DataMapper\Tax\RuleInterface;
|
||||
use App\Utils\Traits\NumberFormatter;
|
||||
use App\DataMapper\Tax\ZipTax\Response;
|
||||
|
||||
class InvoiceItemSum
|
||||
{
|
||||
@ -22,6 +25,38 @@ class InvoiceItemSum
|
||||
use Discounter;
|
||||
use Taxer;
|
||||
|
||||
private array $tax_jurisdictions = [
|
||||
'AT', // Austria
|
||||
'BE', // Belgium
|
||||
'BG', // Bulgaria
|
||||
'CY', // Cyprus
|
||||
'CZ', // Czech Republic
|
||||
'DE', // Germany
|
||||
'DK', // Denmark
|
||||
'EE', // Estonia
|
||||
'ES', // Spain
|
||||
'FI', // Finland
|
||||
'FR', // France
|
||||
'GR', // Greece
|
||||
'HR', // Croatia
|
||||
'HU', // Hungary
|
||||
'IE', // Ireland
|
||||
'IT', // Italy
|
||||
'LT', // Lithuania
|
||||
'LU', // Luxembourg
|
||||
'LV', // Latvia
|
||||
'MT', // Malta
|
||||
'NL', // Netherlands
|
||||
'PL', // Poland
|
||||
'PT', // Portugal
|
||||
'RO', // Romania
|
||||
'SE', // Sweden
|
||||
'SI', // Slovenia
|
||||
'SK', // Slovakia
|
||||
|
||||
'US', //USA
|
||||
];
|
||||
|
||||
protected $invoice;
|
||||
|
||||
private $items;
|
||||
@ -48,6 +83,12 @@ class InvoiceItemSum
|
||||
|
||||
private $tax_collection;
|
||||
|
||||
private ?Client $client;
|
||||
|
||||
private bool $calc_tax = false;
|
||||
|
||||
private RuleInterface $rule;
|
||||
|
||||
public function __construct($invoice)
|
||||
{
|
||||
$this->tax_collection = collect([]);
|
||||
@ -56,6 +97,8 @@ class InvoiceItemSum
|
||||
|
||||
if ($this->invoice->client) {
|
||||
$this->currency = $this->invoice->client->currency();
|
||||
$this->client = $this->invoice->client;
|
||||
$this->shouldCalculateTax();
|
||||
} else {
|
||||
$this->currency = $this->invoice->vendor->currency();
|
||||
}
|
||||
@ -89,6 +132,33 @@ class InvoiceItemSum
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function shouldCalculateTax(): self
|
||||
{
|
||||
if (!$this->invoice->company->calculate_taxes) {
|
||||
$this->calc_tax = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
//should we be filtering by client country here? do we need to reflect at the company <=> client level?
|
||||
if (in_array($this->client->country->iso_3166_2, $this->tax_jurisdictions)) { //only calculate for supported tax jurisdictions
|
||||
$class = "App\DataMapper\Tax\\".$this->client->company->country()->iso_3166_2."\\Rule";
|
||||
|
||||
$tax_data = new Response($this->invoice->tax_data);
|
||||
|
||||
$this->rule = new $class();
|
||||
$this->rule
|
||||
->setTaxData($tax_data)
|
||||
->setClient($this->client)
|
||||
->init();
|
||||
|
||||
$this->calc_tax = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function push()
|
||||
{
|
||||
$this->sub_total += $this->getLineTotal();
|
||||
@ -122,8 +192,33 @@ class InvoiceItemSum
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to calculate taxes based on the clients location
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
private function calcTaxesAutomatically(): self
|
||||
{
|
||||
$this->rule->tax($this->item->tax_id ?? null);
|
||||
|
||||
$this->item->tax_name1 = $this->rule->tax_name1;
|
||||
$this->item->tax_rate1 = $this->rule->tax_rate1;
|
||||
|
||||
$this->item->tax_name2 = $this->rule->tax_name2;
|
||||
$this->item->tax_rate2 = $this->rule->tax_rate2;
|
||||
|
||||
$this->item->tax_name3 = $this->rule->tax_name3;
|
||||
$this->item->tax_rate3 = $this->rule->tax_rate3;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function calcTaxes()
|
||||
{
|
||||
if ($this->calc_tax) {
|
||||
$this->calcTaxesAutomatically();
|
||||
}
|
||||
|
||||
$item_tax = 0;
|
||||
|
||||
$amount = $this->item->line_total - ($this->item->line_total * ($this->invoice->discount / 100));
|
||||
@ -131,7 +226,6 @@ class InvoiceItemSum
|
||||
|
||||
$item_tax += $item_tax_rate1_total;
|
||||
|
||||
// if($item_tax_rate1_total != 0)
|
||||
if (strlen($this->item->tax_name1) > 1) {
|
||||
$this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total);
|
||||
}
|
||||
@ -155,7 +249,7 @@ class InvoiceItemSum
|
||||
$this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision));
|
||||
|
||||
$this->item->gross_line_total = $this->getLineTotal() + $item_tax;
|
||||
|
||||
|
||||
$this->item->tax_amount = $item_tax;
|
||||
|
||||
return $this;
|
||||
|
@ -45,6 +45,8 @@ class InvoiceSum
|
||||
|
||||
private $precision;
|
||||
|
||||
public InvoiceItemSum $invoice_items;
|
||||
|
||||
/**
|
||||
* Constructs the object with Invoice and Settings object.
|
||||
*
|
||||
|
@ -11,16 +11,17 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Account\CreateAccountRequest;
|
||||
use App\Http\Requests\Account\UpdateAccountRequest;
|
||||
use App\Jobs\Account\CreateAccount;
|
||||
use App\Models\Account;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Utils\TruthSource;
|
||||
use App\Models\CompanyUser;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Jobs\Account\CreateAccount;
|
||||
use App\Transformers\AccountTransformer;
|
||||
use App\Transformers\CompanyUserTransformer;
|
||||
use App\Utils\TruthSource;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Http\Requests\Account\CreateAccountRequest;
|
||||
use App\Http\Requests\Account\UpdateAccountRequest;
|
||||
|
||||
class AccountController extends BaseController
|
||||
{
|
||||
@ -146,15 +147,20 @@ class AccountController extends BaseController
|
||||
if (! ($account instanceof Account)) {
|
||||
return $account;
|
||||
}
|
||||
|
||||
MultiDB::findAndSetDbByAccountKey($account->key);
|
||||
|
||||
$ct = CompanyUser::whereUserId(auth()->user()->id);
|
||||
$cu = CompanyUser::where('user_id', $account->users()->first()->id);
|
||||
|
||||
$company_user = $cu->first();
|
||||
|
||||
$truth = app()->make(TruthSource::class);
|
||||
$truth->setCompanyUser($ct->first());
|
||||
$truth->setUser(auth()->user());
|
||||
$truth->setCompany($ct->first()->company);
|
||||
$truth->setCompanyUser($company_user);
|
||||
$truth->setUser($company_user->user);
|
||||
$truth->setCompany($company_user->company);
|
||||
$truth->setCompanyToken($company_user->tokens()->where('user_id', $company_user->user_id)->where('company_id', $company_user->company_id)->first());
|
||||
|
||||
return $this->listResponse($ct);
|
||||
return $this->listResponse($cu);
|
||||
}
|
||||
|
||||
public function update(UpdateAccountRequest $request, Account $account)
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
@ -15,7 +16,6 @@ use App\DataMapper\Analytics\LoginFailure;
|
||||
use App\DataMapper\Analytics\LoginSuccess;
|
||||
use App\Events\User\UserLoggedIn;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Login\LoginRequest;
|
||||
use App\Jobs\Account\CreateAccount;
|
||||
use App\Jobs\Company\CreateCompanyToken;
|
||||
@ -23,8 +23,6 @@ use App\Libraries\MultiDB;
|
||||
use App\Libraries\OAuth\OAuth;
|
||||
use App\Libraries\OAuth\Providers\Google;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\User;
|
||||
@ -38,7 +36,6 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use Microsoft\Graph\Model;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
@ -46,18 +43,7 @@ use Turbo124\Beacon\Facades\LightLogs;
|
||||
|
||||
class LoginController extends BaseController
|
||||
{
|
||||
/**
|
||||
* @OA\Tag(
|
||||
* name="login",
|
||||
* description="Authentication",
|
||||
* @OA\ExternalDocumentation(
|
||||
* description="Find out more",
|
||||
* url="https://invoiceninja.github.io"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
use AuthenticatesUsers;
|
||||
|
||||
use UserSessionAttributes;
|
||||
use LoginCache;
|
||||
|
||||
@ -89,7 +75,7 @@ class LoginController extends BaseController
|
||||
* @param Request $request
|
||||
* @param User $user
|
||||
* @return void
|
||||
* deprecated .1 API ONLY we don't need to set any session variables
|
||||
* @deprecated .1 API ONLY we don't need to set any session variables
|
||||
*/
|
||||
public function authenticated(Request $request, User $user): void
|
||||
{
|
||||
@ -99,63 +85,8 @@ class LoginController extends BaseController
|
||||
/**
|
||||
* Login via API.
|
||||
*
|
||||
* @param Request $request The request
|
||||
*
|
||||
* @return Response|User Process user login.
|
||||
*
|
||||
* @param LoginRequest $request The request
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
* @OA\Post(
|
||||
* path="/api/v1/login",
|
||||
* operationId="postLogin",
|
||||
* tags={"login"},
|
||||
* summary="Attempts authentication",
|
||||
* description="Returns a CompanyUser object on success",
|
||||
* @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/include_static"),
|
||||
* @OA\Parameter(ref="#/components/parameters/clear_cache"),
|
||||
* @OA\RequestBody(
|
||||
* description="User credentials",
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="application/json",
|
||||
* @OA\Schema(
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="email",
|
||||
* description="The user email address",
|
||||
* type="string",
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="password",
|
||||
* example="1234567",
|
||||
* description="The user password must meet minimum criteria ~ >6 characters",
|
||||
* type="string"
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="The Company User 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\JsonContent(ref="#/components/schemas/CompanyUser"),
|
||||
* ),
|
||||
* @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 apiLogin(LoginRequest $request)
|
||||
{
|
||||
@ -175,7 +106,7 @@ class LoginController extends BaseController
|
||||
if ($this->attemptLogin($request)) {
|
||||
LightLogs::create(new LoginSuccess())
|
||||
->increment()
|
||||
->queue();
|
||||
->batch();
|
||||
|
||||
$user = $this->guard()->user();
|
||||
|
||||
@ -221,7 +152,7 @@ class LoginController extends BaseController
|
||||
} else {
|
||||
LightLogs::create(new LoginFailure())
|
||||
->increment()
|
||||
->queue();
|
||||
->batch();
|
||||
|
||||
$this->incrementLoginAttempts($request);
|
||||
|
||||
@ -236,39 +167,7 @@ class LoginController extends BaseController
|
||||
* Refreshes the data feed with the current Company User.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return CompanyUser Refresh Feed.
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/refresh",
|
||||
* operationId="refresh",
|
||||
* tags={"refresh"},
|
||||
* summary="Refreshes the dataset",
|
||||
* description="Refreshes the dataset",
|
||||
* @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/include_static"),
|
||||
* @OA\Parameter(ref="#/components/parameters/clear_cache"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="The Company User 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\JsonContent(ref="#/components/schemas/CompanyUser"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
* @return CompanyUser Refresh Feed.
|
||||
*/
|
||||
public function refresh(Request $request)
|
||||
{
|
||||
@ -346,7 +245,7 @@ class LoginController extends BaseController
|
||||
private function handleSocialiteLogin($provider, $token)
|
||||
{
|
||||
$user = $this->getSocialiteUser($provider, $token);
|
||||
|
||||
|
||||
if ($user) {
|
||||
return $this->loginOrCreateFromSocialite($user, $provider);
|
||||
}
|
||||
@ -363,7 +262,7 @@ class LoginController extends BaseController
|
||||
'oauth_user_id' => $user->id,
|
||||
'oauth_provider_id' => $provider,
|
||||
];
|
||||
|
||||
|
||||
if ($existing_user = MultiDB::hasUser($query)) {
|
||||
if (!$existing_user->account) {
|
||||
return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400);
|
||||
@ -408,7 +307,7 @@ class LoginController extends BaseController
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
}
|
||||
|
||||
|
||||
nlog("socialite");
|
||||
nlog($user);
|
||||
|
||||
@ -478,7 +377,7 @@ class LoginController extends BaseController
|
||||
|
||||
if (auth()->user()->company_users()->count() != auth()->user()->tokens()->distinct('company_id')->count()) {
|
||||
auth()->user()->companies->each(function ($company) {
|
||||
if (!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->exists()) {
|
||||
if (!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->where('is_system', true)->exists()) {
|
||||
(new CreateCompanyToken($company, auth()->user(), 'Google_O_Auth'))->handle();
|
||||
}
|
||||
});
|
||||
@ -499,7 +398,6 @@ class LoginController extends BaseController
|
||||
return response()->json(['message' => 'Invalid response from oauth server, no access token in response.'], 400);
|
||||
}
|
||||
|
||||
|
||||
$graph = new \Microsoft\Graph\Graph();
|
||||
$graph->setAccessToken($accessToken);
|
||||
|
||||
@ -536,17 +434,22 @@ class LoginController extends BaseController
|
||||
return $this->existingLoginUser($user->getId(), 'microsoft');
|
||||
}
|
||||
|
||||
// Signup!
|
||||
$new_account = [
|
||||
'first_name' => $user->getGivenName() ?: '',
|
||||
'last_name' => $user->getSurname() ?: '',
|
||||
'password' => '',
|
||||
'email' => $email,
|
||||
'oauth_user_id' => $user->getId(),
|
||||
'oauth_provider_id' => 'microsoft',
|
||||
];
|
||||
|
||||
return $this->createNewAccount($new_account);
|
||||
// Signup!
|
||||
if (request()->has('create') && request()->input('create') == 'true') {
|
||||
$new_account = [
|
||||
'first_name' => $user->getGivenName() ?: '',
|
||||
'last_name' => $user->getSurname() ?: '',
|
||||
'password' => '',
|
||||
'email' => $email,
|
||||
'oauth_user_id' => $user->getId(),
|
||||
'oauth_provider_id' => 'microsoft',
|
||||
];
|
||||
|
||||
return $this->createNewAccount($new_account);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'User not found. If you believe this is an error, please send an email to contact@invoiceninja.com'], 400);
|
||||
}
|
||||
|
||||
|
||||
@ -640,19 +543,23 @@ class LoginController extends BaseController
|
||||
return $this->existingLoginUser($google->harvestSubField($user), 'google');
|
||||
}
|
||||
|
||||
//user not found anywhere - lets sign them up.
|
||||
$name = OAuth::splitName($google->harvestName($user));
|
||||
if (request()->has('create') && request()->input('create') == 'true') {
|
||||
//user not found anywhere - lets sign them up.
|
||||
$name = OAuth::splitName($google->harvestName($user));
|
||||
|
||||
$new_account = [
|
||||
'first_name' => $name[0],
|
||||
'last_name' => $name[1],
|
||||
'password' => '',
|
||||
'email' => $google->harvestEmail($user),
|
||||
'oauth_user_id' => $google->harvestSubField($user),
|
||||
'oauth_provider_id' => 'google',
|
||||
];
|
||||
$new_account = [
|
||||
'first_name' => $name[0],
|
||||
'last_name' => $name[1],
|
||||
'password' => '',
|
||||
'email' => $google->harvestEmail($user),
|
||||
'oauth_user_id' => $google->harvestSubField($user),
|
||||
'oauth_provider_id' => 'google',
|
||||
];
|
||||
|
||||
return $this->createNewAccount($new_account);
|
||||
return $this->createNewAccount($new_account);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'User not found. If you believe this is an error, please send an email to contact@invoiceninja.com'], 400);
|
||||
}
|
||||
|
||||
return response()
|
||||
@ -700,7 +607,7 @@ class LoginController extends BaseController
|
||||
|
||||
if ($provider == 'microsoft') {
|
||||
$scopes = ['email', 'Mail.Send', 'offline_access', 'profile', 'User.Read openid'];
|
||||
$parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url')."/auth/microsoft"];
|
||||
$parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url') . "/auth/microsoft"];
|
||||
}
|
||||
|
||||
if (request()->has('code')) {
|
||||
|
@ -1,81 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller handles the registration of new users as well as their
|
||||
| validation and creation. By default this controller uses a trait to
|
||||
| provide this functionality without requiring any additional code.
|
||||
|
|
||||
*/
|
||||
|
||||
use RegistersUsers;
|
||||
|
||||
/**
|
||||
* Where to redirect users after registration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/dashboard';
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a validator for an incoming registration request.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
protected function validator(array $data)
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'first_name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users',
|
||||
'password' => 'required|string|min:6|confirmed',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user instance after a valid registration.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \App\User
|
||||
*/
|
||||
protected function create(array $data)
|
||||
{
|
||||
return User::create([
|
||||
'first_name' => $data['first_name'],
|
||||
'email' => $data['email'],
|
||||
'password' => Hash::make($data['password']),
|
||||
]);
|
||||
}
|
||||
}
|
@ -28,16 +28,13 @@ class VendorContactLoginController extends Controller
|
||||
|
||||
public function catch()
|
||||
{
|
||||
$data = [
|
||||
|
||||
];
|
||||
|
||||
return $this->render('purchase_orders.catch');
|
||||
}
|
||||
|
||||
public function logout()
|
||||
{
|
||||
Auth::guard('vendor')->logout();
|
||||
|
||||
request()->session()->invalidate();
|
||||
|
||||
return redirect('/vendors');
|
||||
|
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Foundation\Auth\VerifiesEmails;
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class VerificationController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Email Verification Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling email verification for any
|
||||
| user that recently registered with the application. Emails may also
|
||||
| be resent if the user did not receive the original email message.
|
||||
|
|
||||
*/
|
||||
|
||||
use VerifiesEmails;
|
||||
|
||||
/**
|
||||
* Where to redirect users after verification.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/dashboard';
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->middleware('signed')->only('verify');
|
||||
$this->middleware('throttle:6,1')->only('verify', 'resend');
|
||||
}
|
||||
}
|
@ -276,8 +276,6 @@ class YodleeController extends BaseController
|
||||
{
|
||||
//this is the main hook we use for notifications
|
||||
|
||||
nlog("data refresh");
|
||||
nlog($request->all());
|
||||
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
|
||||
|
@ -531,7 +531,6 @@ class BankTransactionController extends BaseController
|
||||
*/
|
||||
public function match(MatchBankTransactionRequest $request)
|
||||
{
|
||||
|
||||
$bts = (new MatchBankTransactions(auth()->user()->company()->id, auth()->user()->company()->db, $request->all()))->handle();
|
||||
|
||||
return $this->listResponse($bts);
|
||||
|
@ -33,6 +33,7 @@ use App\Utils\Statics;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
@ -565,7 +566,7 @@ class BaseController extends Controller
|
||||
/**
|
||||
* Mini Load Query
|
||||
*
|
||||
* @param mixed $query
|
||||
* @param Builder $query
|
||||
* @return void
|
||||
*/
|
||||
protected function miniLoadResponse($query)
|
||||
@ -667,7 +668,7 @@ class BaseController extends Controller
|
||||
/**
|
||||
* Passes back the miniloaded data response
|
||||
*
|
||||
* @param mixed $query
|
||||
* @param Builder $query
|
||||
* @return void
|
||||
*/
|
||||
protected function timeConstrainedResponse($query)
|
||||
@ -911,9 +912,8 @@ class BaseController extends Controller
|
||||
|
||||
/**
|
||||
* List response
|
||||
*
|
||||
* @param mixed $query
|
||||
* @return void
|
||||
*
|
||||
* @param Builder $query
|
||||
*/
|
||||
protected function listResponse($query)
|
||||
{
|
||||
@ -927,24 +927,26 @@ class BaseController extends Controller
|
||||
|
||||
$query->with($includes);
|
||||
|
||||
if (auth()->user() && ! auth()->user()->hasPermission('view_'.Str::snake(class_basename($this->entity_type)))) {
|
||||
$user = Auth::user();
|
||||
|
||||
if ($user && ! $user->hasPermission('view_'.Str::snake(class_basename($this->entity_type)))) {
|
||||
if (in_array($this->entity_type, [User::class])) {
|
||||
$query->where('id', auth()->user()->id);
|
||||
$query->where('id', $user->id);
|
||||
} elseif (in_array($this->entity_type, [BankTransactionRule::class,CompanyGateway::class, TaxRate::class, BankIntegration::class, Scheduler::class, BankTransaction::class, Webhook::class, ExpenseCategory::class])) { //table without assigned_user_id
|
||||
if ($this->entity_type == BankIntegration::class && !auth()->user()->isSuperUser() && auth()->user()->hasIntersectPermissions(['create_bank_transaction','edit_bank_transaction','view_bank_transaction'])) {
|
||||
if ($this->entity_type == BankIntegration::class && !$user->isSuperUser() && $user->hasIntersectPermissions(['create_bank_transaction','edit_bank_transaction','view_bank_transaction'])) {
|
||||
$query->exclude(["balance"]);
|
||||
} //allows us to selective display bank integrations back to the user if they can view / create bank transactions but without the bank balance being present in the response
|
||||
else {
|
||||
$query->where('user_id', '=', auth()->user()->id);
|
||||
$query->where('user_id', '=', $user->id);
|
||||
}
|
||||
} elseif (in_array($this->entity_type, [Design::class, GroupSetting::class, PaymentTerm::class, TaskStatus::class])) {
|
||||
// nlog($this->entity_type);
|
||||
} else {
|
||||
$query->where('user_id', '=', auth()->user()->id)->orWhere('assigned_user_id', auth()->user()->id);
|
||||
$query->where('user_id', '=', $user->id)->orWhere('assigned_user_id', $user->id);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->entity_type == Client::class && auth()->user()->hasExcludedPermissions($this->client_excludable_permissions, $this->client_excludable_overrides)) {
|
||||
if ($this->entity_type == Client::class && $user->hasExcludedPermissions($this->client_excludable_permissions, $this->client_excludable_overrides)) {
|
||||
$query->exclude($this->client_exclusion_fields);
|
||||
}
|
||||
|
||||
@ -1010,7 +1012,6 @@ class BaseController extends Controller
|
||||
* Item Response
|
||||
*
|
||||
* @param mixed $item
|
||||
* @return void
|
||||
*/
|
||||
protected function itemResponse($item)
|
||||
{
|
||||
@ -1087,7 +1088,7 @@ class BaseController extends Controller
|
||||
{
|
||||
if ((bool) $this->checkAppSetup() !== false && $account = Account::first()) {
|
||||
//always redirect invoicing.co to invoicing.co
|
||||
if (Ninja::isHosted() && !in_array(request()->getSchemeAndHttpHost(), ['https://staging.invoicing.co', 'https://invoicing.co', 'https://demo.invoicing.co'])) {
|
||||
if (Ninja::isHosted() && !in_array(request()->getSchemeAndHttpHost(), ['https://staging.invoicing.co', 'https://invoicing.co', 'https://demo.invoicing.co', 'https://invoiceninja.net'])) {
|
||||
return redirect()->secure('https://invoicing.co');
|
||||
}
|
||||
|
||||
|
@ -80,8 +80,6 @@ class InvoiceController extends Controller
|
||||
/**
|
||||
* Pay one or more invoices.
|
||||
*
|
||||
* @param ProcessInvoicesInBulkRequest $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function catch_bulk()
|
||||
{
|
||||
|
@ -12,24 +12,24 @@
|
||||
|
||||
namespace App\Http\Controllers\ClientPortal;
|
||||
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use Illuminate\View\View;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use App\PaymentDrivers\Stripe\BankTransfer;
|
||||
use App\Services\ClientPortal\InstantPayment;
|
||||
use App\Services\Subscription\SubscriptionService;
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
* Class PaymentController.
|
||||
@ -64,15 +64,14 @@ class PaymentController extends Controller
|
||||
$data = false;
|
||||
$gateway = false;
|
||||
|
||||
if($payment->gateway_type_id == GatewayType::DIRECT_DEBIT && $payment->type_id == PaymentType::DIRECT_DEBIT){
|
||||
|
||||
if ($payment->gateway_type_id == GatewayType::DIRECT_DEBIT && $payment->type_id == PaymentType::DIRECT_DEBIT) {
|
||||
if (method_exists($payment->company_gateway->driver($payment->client), 'getPaymentIntent')) {
|
||||
$stripe = $payment->company_gateway->driver($payment->client);
|
||||
$payment_intent = $stripe->getPaymentIntent($payment->transaction_reference);
|
||||
|
||||
$bt = new BankTransfer($stripe);
|
||||
|
||||
match($payment->currency->code){
|
||||
match ($payment->currency->code) {
|
||||
'MXN' => $data = $bt->formatDataforMx($payment_intent),
|
||||
'EUR' => $data = $bt->formatDataforEur($payment_intent),
|
||||
'JPY' => $data = $bt->formatDataforJp($payment_intent),
|
||||
@ -87,7 +86,7 @@ class PaymentController extends Controller
|
||||
return $this->render('payments.show', [
|
||||
'payment' => $payment,
|
||||
'bank_details' => $payment_intent ? $data : false,
|
||||
'currency' => strtolower($payment->currency->code),
|
||||
'currency' => $payment->currency ? strtolower($payment->currency->code) : strtolower($payment->client->currency()->code),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@ namespace App\Http\Controllers\ClientPortal;
|
||||
use App\Events\Payment\Methods\MethodDeleted;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\PaymentMethod\CreatePaymentMethodRequest;
|
||||
use App\Http\Requests\ClientPortal\PaymentMethod\VerifyPaymentMethodRequest;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
|
120
app/Http/Controllers/ClientPortal/PrePaymentController.php
Normal file
120
app/Http/Controllers/ClientPortal/PrePaymentController.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\ClientPortal;
|
||||
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\PrePayments\StorePrePaymentRequest;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Number;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
* Class PrePaymentController.
|
||||
*/
|
||||
class PrePaymentController extends Controller
|
||||
{
|
||||
use MakesHash;
|
||||
use MakesDates;
|
||||
|
||||
/**
|
||||
* Show the list of payments.
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$client = auth()->guard('contact')->user()->client;
|
||||
$minimum = $client->getSetting('client_initiated_payments_minimum');
|
||||
$minimum_amount = $minimum == 0 ? "" : Number::formatMoney($minimum, $client);
|
||||
|
||||
$data = [
|
||||
'title' => ctrans('texts.amount'). " " .$client->currency()->code." (".auth()->guard('contact')->user()->client->currency()->symbol . ")",
|
||||
'allows_recurring' => true,
|
||||
'minimum' => $minimum,
|
||||
'minimum_amount' => $minimum_amount,
|
||||
];
|
||||
|
||||
return $this->render('pre_payments.index', $data);
|
||||
}
|
||||
|
||||
public function process(StorePrePaymentRequest $request)
|
||||
{
|
||||
$invoice = InvoiceFactory::create(auth()->guard('contact')->user()->company_id, auth()->guard('contact')->user()->user_id);
|
||||
$invoice->due_date = now()->format('Y-m-d');
|
||||
$invoice->is_proforma = true;
|
||||
$invoice->client_id = auth()->guard('contact')->user()->client_id;
|
||||
|
||||
$line_item = new InvoiceItem();
|
||||
$line_item->cost = (float)$request->amount;
|
||||
$line_item->quantity = 1;
|
||||
$line_item->product_key = ctrans('texts.pre_payment');
|
||||
$line_item->notes = $request->notes;
|
||||
$line_item->type_id = '1';
|
||||
|
||||
$items = [];
|
||||
$items[] = $line_item;
|
||||
$invoice->line_items = $items;
|
||||
$invoice->number = ctrans('texts.pre_payment') . " " . now()->format('Y-m-d : H:i:s');
|
||||
|
||||
$invoice_repo = new InvoiceRepository();
|
||||
|
||||
$data = [
|
||||
'client_id' => $invoice->client_id,
|
||||
'quantity' => 1,
|
||||
'date' => now()->format('Y-m-d'),
|
||||
];
|
||||
|
||||
$invoice = $invoice_repo->save($data, $invoice)
|
||||
->service()
|
||||
->markSent()
|
||||
->applyNumber()
|
||||
->fillDefaults()
|
||||
->save();
|
||||
|
||||
$total = $invoice->balance;
|
||||
|
||||
//format totals
|
||||
$formatted_total = Number::formatMoney($invoice->amount, auth()->guard('contact')->user()->client);
|
||||
|
||||
$payment_methods = auth()->guard('contact')->user()->client->service()->getPaymentMethods($request->amount);
|
||||
|
||||
//if there is only one payment method -> lets return straight to the payment page
|
||||
$invoices = collect();
|
||||
$invoices->push($invoice);
|
||||
|
||||
$invoices->map(function ($invoice) {
|
||||
$invoice->balance = Number::formatValue($invoice->balance, $invoice->client->currency());
|
||||
return $invoice;
|
||||
});
|
||||
|
||||
$data = [
|
||||
'settings' => auth()->guard('contact')->user()->client->getMergedSettings(),
|
||||
'invoices' => $invoices,
|
||||
'formatted_total' => $formatted_total,
|
||||
'payment_methods' => $payment_methods,
|
||||
'hashed_ids' => $invoices->pluck('hashed_id'),
|
||||
'total' => $total,
|
||||
'pre_payment' => true,
|
||||
'frequency_id' => $request->frequency_id,
|
||||
'remaining_cycles' => $request->remaining_cycles,
|
||||
'is_recurring' => $request->is_recurring == 'on' ? true : false,
|
||||
];
|
||||
|
||||
return $this->render('invoices.payment', $data);
|
||||
}
|
||||
}
|
@ -178,7 +178,7 @@ class QuoteController extends Controller
|
||||
if ($process) {
|
||||
foreach ($quotes as $quote) {
|
||||
if (request()->has('user_input') && strlen(request()->input('user_input')) > 2) {
|
||||
$quote->public_notes .= $quote->public_notes . "\n" . request()->input('user_input');
|
||||
$quote->po_number = substr(request()->input('user_input'), 0, 180);
|
||||
$quote->saveQuietly();
|
||||
}
|
||||
|
||||
|
@ -19,22 +19,28 @@ use App\Utils\Ninja;
|
||||
|
||||
class SubscriptionController extends Controller
|
||||
{
|
||||
/**
|
||||
* This function is used to display the subscription page.
|
||||
*
|
||||
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
|
||||
public function index()
|
||||
{
|
||||
if (Ninja::isHosted()) {
|
||||
$count = RecurringInvoice::query()
|
||||
->where('client_id', auth()->guard('contact')->user()->client->id)
|
||||
->where('company_id', auth()->guard('contact')->user()->client->company_id)
|
||||
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('is_deleted', 0)
|
||||
->whereNotNull('subscription_id')
|
||||
->withTrashed()
|
||||
->count();
|
||||
// if (Ninja::isHosted()) {
|
||||
// $count = RecurringInvoice::query()
|
||||
// ->where('client_id', auth()->guard('contact')->user()->client->id)
|
||||
// ->where('company_id', auth()->guard('contact')->user()->client->company_id)
|
||||
// ->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
// ->where('is_deleted', 0)
|
||||
// ->whereNotNull('subscription_id')
|
||||
// ->withTrashed()
|
||||
// ->count();
|
||||
|
||||
if ($count == 0) {
|
||||
return redirect()->route('client.ninja_contact_login', ['contact_key' => auth()->guard('contact')->user()->contact_key, 'company_key' => auth()->guard('contact')->user()->company->company_key]);
|
||||
}
|
||||
}
|
||||
// if ($count == 0) {
|
||||
// return redirect()->route('client.ninja_contact_login', ['contact_key' => auth()->guard('contact')->user()->contact_key, 'company_key' => auth()->guard('contact')->user()->company->company_key]);
|
||||
// }
|
||||
// }
|
||||
|
||||
return render('subscriptions.index');
|
||||
}
|
||||
@ -44,7 +50,6 @@ class SubscriptionController extends Controller
|
||||
*
|
||||
* @param ShowRecurringInvoiceRequest $request
|
||||
* @param RecurringInvoice $recurring_invoice
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function show(ShowRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice)
|
||||
{
|
||||
|
@ -44,7 +44,6 @@ class SubscriptionPurchaseController extends Controller
|
||||
|
||||
public function upgrade(Subscription $subscription, Request $request)
|
||||
{
|
||||
|
||||
App::setLocale($subscription->company->locale());
|
||||
|
||||
/* Make sure the contact is logged into the correct company for this subscription */
|
||||
|
@ -40,6 +40,7 @@ use App\Utils\Traits\SavesDocuments;
|
||||
use App\Utils\Traits\Uploadable;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
|
||||
/**
|
||||
@ -486,6 +487,11 @@ class CompanyController extends BaseController
|
||||
$company_user->forceDelete();
|
||||
});
|
||||
|
||||
try {
|
||||
Storage::disk(config('filesystems.default'))->deleteDirectory($company->company_key);
|
||||
} catch(\Exception $e) {
|
||||
}
|
||||
|
||||
$account->delete();
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
@ -494,7 +500,7 @@ class CompanyController extends BaseController
|
||||
|
||||
LightLogs::create(new AccountDeleted())
|
||||
->increment()
|
||||
->queue();
|
||||
->batch();
|
||||
} else {
|
||||
$company_id = $company->id;
|
||||
|
||||
@ -511,6 +517,12 @@ class CompanyController extends BaseController
|
||||
$nmo->to_user = auth()->user();
|
||||
(new NinjaMailerJob($nmo, true))->handle();
|
||||
|
||||
try {
|
||||
Storage::disk(config('filesystems.default'))->deleteDirectory($company->company_key);
|
||||
} catch(\Exception $e) {
|
||||
}
|
||||
|
||||
|
||||
$company->delete();
|
||||
|
||||
//If we are deleting the default companies, we'll need to make a new company the default.
|
||||
|
@ -11,26 +11,27 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Quote;
|
||||
use App\Events\Credit\CreditWasEmailed;
|
||||
use App\Events\Quote\QuoteWasEmailed;
|
||||
use App\Http\Requests\Email\SendEmailRequest;
|
||||
use App\Jobs\Entity\EmailEntity;
|
||||
use App\Jobs\PurchaseOrder\PurchaseOrderEmail;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Services\Email\Email;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Jobs\Entity\EmailEntity;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Services\Email\Email;
|
||||
use App\Services\Email\EmailObject;
|
||||
use App\Events\Quote\QuoteWasEmailed;
|
||||
use App\Transformers\QuoteTransformer;
|
||||
use App\Events\Credit\CreditWasEmailed;
|
||||
use App\Transformers\CreditTransformer;
|
||||
use App\Transformers\InvoiceTransformer;
|
||||
use App\Http\Requests\Email\SendEmailRequest;
|
||||
use App\Jobs\PurchaseOrder\PurchaseOrderEmail;
|
||||
use App\Transformers\PurchaseOrderTransformer;
|
||||
use App\Transformers\QuoteTransformer;
|
||||
use App\Transformers\RecurringInvoiceTransformer;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Mail\Mailables\Address;
|
||||
|
||||
class EmailController extends BaseController
|
||||
{
|
||||
@ -135,6 +136,9 @@ class EmailController extends BaseController
|
||||
$mo->email_template_body = $request->input('template');
|
||||
$mo->email_template_subject = str_replace("template", "subject", $request->input('template'));
|
||||
|
||||
if ($request->has('cc_email') && $request->cc_email) {
|
||||
$mo->cc[] = new Address($request->cc_email);
|
||||
}
|
||||
|
||||
// if ($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order' || $entity == 'App\Models\PurchaseOrder') {
|
||||
// return $this->sendPurchaseOrder($entity_obj, $data, $template);
|
||||
@ -149,7 +153,6 @@ class EmailController extends BaseController
|
||||
$mo->invitation_id = $invitation->id;
|
||||
|
||||
Email::dispatch($mo, $invitation->company);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@ -190,7 +193,7 @@ class EmailController extends BaseController
|
||||
$this->entity_transformer = RecurringInvoiceTransformer::class;
|
||||
}
|
||||
|
||||
if($entity_obj instanceof PurchaseOrder){
|
||||
if ($entity_obj instanceof PurchaseOrder) {
|
||||
$this->entity_type = PurchaseOrder::class;
|
||||
$this->entity_transformer = PurchaseOrderTransformer::class;
|
||||
}
|
||||
@ -214,8 +217,7 @@ class EmailController extends BaseController
|
||||
|
||||
private function resolveClass(string $entity): string
|
||||
{
|
||||
|
||||
match($entity){
|
||||
match ($entity) {
|
||||
'invoice' => $class = Invoice::class,
|
||||
'App\Models\Invoice' => $class = Invoice::class,
|
||||
'credit' => $class = Credit::class,
|
||||
@ -229,6 +231,5 @@ class EmailController extends BaseController
|
||||
};
|
||||
|
||||
return $class;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ class HostedMigrationController extends Controller
|
||||
$account->hosted_company_count = 10;
|
||||
$account->save();
|
||||
|
||||
MultiDB::findAndSetDbByAccountKey($account->key);
|
||||
|
||||
$company = $account->companies->first();
|
||||
|
||||
$company_token = CompanyToken::where('user_id', auth()->user()->id)
|
||||
|
@ -163,19 +163,15 @@ class ImportController extends Controller
|
||||
$bestDelimiter = ' ';
|
||||
$count = 0;
|
||||
foreach ($delimiters as $delimiter) {
|
||||
|
||||
// if (substr_count($csvfile, $delimiter) > $count) {
|
||||
// $count = substr_count($csvfile, $delimiter);
|
||||
// $bestDelimiter = $delimiter;
|
||||
// }
|
||||
|
||||
if (substr_count(strstr($csvfile,"\n",true), $delimiter) > $count) {
|
||||
if (substr_count(strstr($csvfile, "\n", true), $delimiter) > $count) {
|
||||
$count = substr_count($csvfile, $delimiter);
|
||||
$bestDelimiter = $delimiter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
return $bestDelimiter;
|
||||
}
|
||||
|
@ -11,13 +11,13 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use stdClass;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Account;
|
||||
use App\Utils\CurlUtils;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Http\Request;
|
||||
use stdClass;
|
||||
|
||||
class LicenseController extends BaseController
|
||||
{
|
||||
@ -80,7 +80,7 @@ class LicenseController extends BaseController
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function indexx()
|
||||
public function index()
|
||||
{
|
||||
$this->checkLicense();
|
||||
|
||||
@ -89,6 +89,10 @@ class LicenseController extends BaseController
|
||||
$license_key = request()->input('license_key');
|
||||
$product_id = 3;
|
||||
|
||||
if(substr($license_key, 0, 3) == 'v5_') {
|
||||
return $this->v5ClaimLicense($license_key, $product_id);
|
||||
}
|
||||
|
||||
$url = config('ninja.license_url')."/claim_license?license_key={$license_key}&product_id={$product_id}&get_date=true";
|
||||
$data = trim(CurlUtils::get($url));
|
||||
|
||||
@ -149,21 +153,19 @@ class LicenseController extends BaseController
|
||||
return response()->json($error, 400);
|
||||
}
|
||||
|
||||
public function v5ClaimLicense(Request $request)
|
||||
public function v5ClaimLicense(string $license_key)
|
||||
{
|
||||
$this->checkLicense();
|
||||
|
||||
/* Catch claim license requests */
|
||||
if (config('ninja.environment') == 'selfhost' && request()->has('license_key')) {
|
||||
|
||||
if (config('ninja.environment') == 'selfhost') {
|
||||
// $response = Http::get( "http://ninja.test:8000/claim_license", [
|
||||
$response = Http::get( "https://invoicing.co/claim_license", [
|
||||
'license_key' => $request->input('license_key'),
|
||||
$response = Http::get("https://invoicing.co/claim_license", [
|
||||
'license_key' => $license_key,
|
||||
'product_id' => 3,
|
||||
]);
|
||||
|
||||
if($response->successful()) {
|
||||
|
||||
if ($response->successful()) {
|
||||
$payload = $response->json();
|
||||
|
||||
$account = auth()->user()->account;
|
||||
@ -179,8 +181,7 @@ class LicenseController extends BaseController
|
||||
];
|
||||
|
||||
return response()->json($error, 200);
|
||||
}else {
|
||||
|
||||
} else {
|
||||
$error = [
|
||||
'message' => trans('texts.white_label_license_error'),
|
||||
'errors' => new stdClass,
|
||||
@ -188,7 +189,6 @@ class LicenseController extends BaseController
|
||||
|
||||
return response()->json($error, 400);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$error = [
|
||||
@ -197,7 +197,6 @@ class LicenseController extends BaseController
|
||||
];
|
||||
|
||||
return response()->json($error, 400);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -210,6 +209,5 @@ class LicenseController extends BaseController
|
||||
$account->plan_expires = null;
|
||||
$account->save();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Credit;
|
||||
use App\Models\GroupSetting;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\Quote;
|
||||
@ -37,7 +36,6 @@ use App\Services\PdfMaker\Design;
|
||||
use App\Services\PdfMaker\Design as PdfDesignModel;
|
||||
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
||||
use App\Services\PdfMaker\PdfMaker;
|
||||
use App\Services\Preview\StubBuilder;
|
||||
use App\Utils\HostedPDF\NinjaPdf;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Ninja;
|
||||
@ -146,7 +144,7 @@ class PreviewController extends BaseController
|
||||
$maker
|
||||
->design($design)
|
||||
->build();
|
||||
|
||||
|
||||
if (request()->query('html') == 'true') {
|
||||
return $maker->getCompiledHTML();
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Filters\RecurringInvoiceFilters;
|
||||
use App\Http\Requests\RecurringInvoice\ActionRecurringInvoiceRequest;
|
||||
use App\Http\Requests\RecurringInvoice\BulkRecurringInvoiceRequest;
|
||||
use App\Http\Requests\RecurringInvoice\CreateRecurringInvoiceRequest;
|
||||
use App\Http\Requests\RecurringInvoice\DestroyRecurringInvoiceRequest;
|
||||
use App\Http\Requests\RecurringInvoice\EditRecurringInvoiceRequest;
|
||||
@ -23,6 +24,7 @@ use App\Http\Requests\RecurringInvoice\ShowRecurringInvoiceRequest;
|
||||
use App\Http\Requests\RecurringInvoice\StoreRecurringInvoiceRequest;
|
||||
use App\Http\Requests\RecurringInvoice\UpdateRecurringInvoiceRequest;
|
||||
use App\Http\Requests\RecurringInvoice\UploadRecurringInvoiceRequest;
|
||||
use App\Jobs\RecurringInvoice\UpdateRecurring;
|
||||
use App\Models\Account;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Repositories\RecurringInvoiceRepository;
|
||||
@ -392,50 +394,6 @@ class RecurringInvoiceController extends BaseController
|
||||
*
|
||||
* @param DestroyRecurringInvoiceRequest $request
|
||||
* @param RecurringInvoice $recurring_invoice
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
* @throws \Exception
|
||||
* @OA\Delete(
|
||||
* path="/api/v1/recurring_invoices/{id}",
|
||||
* operationId="deleteRecurringInvoice",
|
||||
* tags={"recurring_invoices"},
|
||||
* summary="Deletes a RecurringInvoice",
|
||||
* description="Handles the deletion of an RecurringInvoice by id",
|
||||
* @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 RecurringInvoice 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(DestroyRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice)
|
||||
{
|
||||
@ -445,195 +403,30 @@ class RecurringInvoiceController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/recurring_invoice/{invitation_key}/download",
|
||||
* operationId="downloadRecurringInvoice",
|
||||
* tags={"invoices"},
|
||||
* summary="Download a specific invoice by invitation key",
|
||||
* description="Downloads a specific invoice",
|
||||
* @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="invitation_key",
|
||||
* in="path",
|
||||
* description="The Recurring Invoice Invitation Key",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the recurring invoice pdf",
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
* @param $invitation_key
|
||||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
*/
|
||||
public function downloadPdf($invitation_key)
|
||||
public function bulk(BulkRecurringInvoiceRequest $request)
|
||||
{
|
||||
$invitation = $this->recurring_invoice_repo->getInvitationByKey($invitation_key);
|
||||
$contact = $invitation->contact;
|
||||
$recurring_invoice = $invitation->recurring_invoice;
|
||||
$percentage_increase = request()->has('percentage_increase') ? request()->input('percentage_increase') : 0;
|
||||
|
||||
$file = $recurring_invoice->service()->getInvoicePdf($contact);
|
||||
if (in_array($request->action, ['increase_prices', 'update_prices'])) {
|
||||
UpdateRecurring::dispatch($request->ids, auth()->user()->company(), auth()->user(), $request->action, $percentage_increase);
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file), ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
return response()->json(['message' => 'Update in progress.'], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform bulk actions on the list view.
|
||||
*
|
||||
* @return Collection
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/recurring_invoices/bulk",
|
||||
* operationId="bulkRecurringInvoices",
|
||||
* tags={"recurring_invoices"},
|
||||
* summary="Performs bulk actions on an array of recurring_invoices",
|
||||
* description="",
|
||||
* @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="Hashed IDs",
|
||||
* 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 RecurringInvoice 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\JsonContent(ref="#/components/schemas/RecurringInvoice"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
$recurring_invoices = RecurringInvoice::withTrashed()->find($request->ids);
|
||||
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function bulk()
|
||||
{
|
||||
$action = request()->input('action');
|
||||
|
||||
$ids = request()->input('ids');
|
||||
|
||||
$recurring_invoices = RecurringInvoice::withTrashed()->find($this->transformKeys($ids));
|
||||
|
||||
$recurring_invoices->each(function ($recurring_invoice, $key) use ($action) {
|
||||
$recurring_invoices->each(function ($recurring_invoice, $key) use ($request) {
|
||||
if (auth()->user()->can('edit', $recurring_invoice)) {
|
||||
$this->performAction($recurring_invoice, $action, true);
|
||||
$this->performAction($recurring_invoice, $request->action, true);
|
||||
}
|
||||
});
|
||||
|
||||
return $this->listResponse(RecurringInvoice::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
return $this->listResponse(RecurringInvoice::withTrashed()->whereIn('id', $request->ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurring Invoice Actions.
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/recurring_invoices/{id}/{action}",
|
||||
* operationId="actionRecurringInvoice",
|
||||
* tags={"recurring_invoices"},
|
||||
* summary="Performs a custom action on an RecurringInvoice",
|
||||
* description="Performs a custom action on an RecurringInvoice.
|
||||
|
||||
The current range of actions are as follows
|
||||
- clone_to_RecurringInvoice
|
||||
- clone_to_quote
|
||||
- history
|
||||
- delivery_note
|
||||
- mark_paid
|
||||
- download
|
||||
- archive
|
||||
- delete
|
||||
- email",
|
||||
* @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 RecurringInvoice Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Parameter(
|
||||
* name="action",
|
||||
* in="path",
|
||||
* description="The action string to be performed",
|
||||
* example="clone_to_quote",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the RecurringInvoice 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/RecurringInvoice"),
|
||||
* ),
|
||||
* @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 ActionRecurringInvoiceRequest $request
|
||||
* @param RecurringInvoice $recurring_invoice
|
||||
* @param $action
|
||||
@ -763,4 +556,29 @@ class RecurringInvoiceController extends BaseController
|
||||
|
||||
return $this->itemResponse($recurring_invoice->fresh());
|
||||
}
|
||||
|
||||
public function downloadPdf(string $invitation_key)
|
||||
{
|
||||
$invitation = $this->recurring_invoice_repo->getInvitationByKey($invitation_key);
|
||||
|
||||
if (! $invitation) {
|
||||
return response()->json(['message' => 'no record found'], 400);
|
||||
}
|
||||
|
||||
$contact = $invitation->contact;
|
||||
$invoice = $invitation->recurring_invoice;
|
||||
|
||||
$file = $invoice->service()->getInvoicePdf($contact);
|
||||
|
||||
$headers = ['Content-Type' => 'application/pdf'];
|
||||
|
||||
if (request()->input('inline') == 'true') {
|
||||
$headers = array_merge($headers, ['Content-Disposition' => 'inline']);
|
||||
}
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file), $headers);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ class SubdomainController extends BaseController
|
||||
'beta',
|
||||
'prometh',
|
||||
'license',
|
||||
'socket',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
|
@ -13,8 +13,8 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Factory\SchedulerFactory;
|
||||
use App\Filters\SchedulerFilters;
|
||||
use App\Http\Requests\TaskScheduler\DestroySchedulerRequest;
|
||||
use App\Http\Requests\TaskScheduler\CreateSchedulerRequest;
|
||||
use App\Http\Requests\TaskScheduler\DestroySchedulerRequest;
|
||||
use App\Http\Requests\TaskScheduler\ShowSchedulerRequest;
|
||||
use App\Http\Requests\TaskScheduler\StoreSchedulerRequest;
|
||||
use App\Http\Requests\TaskScheduler\UpdateSchedulerRequest;
|
||||
|
@ -592,9 +592,9 @@ class UserController extends BaseController
|
||||
*/
|
||||
public function detach(DetachCompanyUserRequest $request, User $user)
|
||||
{
|
||||
if ($request->entityIsDeleted($user)) {
|
||||
return $request->disallowUpdate();
|
||||
}
|
||||
// if ($request->entityIsDeleted($user)) {
|
||||
// return $request->disallowUpdate();
|
||||
// }
|
||||
|
||||
$company_user = CompanyUser::whereUserId($user->id)
|
||||
->whereCompanyId(auth()->user()->companyId())
|
||||
|
@ -17,14 +17,17 @@ use App\Filters\WebhookFilters;
|
||||
use App\Http\Requests\Webhook\CreateWebhookRequest;
|
||||
use App\Http\Requests\Webhook\DestroyWebhookRequest;
|
||||
use App\Http\Requests\Webhook\EditWebhookRequest;
|
||||
use App\Http\Requests\Webhook\RetryWebhookRequest;
|
||||
use App\Http\Requests\Webhook\ShowWebhookRequest;
|
||||
use App\Http\Requests\Webhook\StoreWebhookRequest;
|
||||
use App\Http\Requests\Webhook\UpdateWebhookRequest;
|
||||
use App\Jobs\Util\WebhookSingle;
|
||||
use App\Models\Webhook;
|
||||
use App\Repositories\BaseRepository;
|
||||
use App\Transformers\WebhookTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class WebhookController extends BaseController
|
||||
{
|
||||
@ -487,4 +490,28 @@ class WebhookController extends BaseController
|
||||
|
||||
return $this->listResponse(Webhook::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
}
|
||||
|
||||
public function retry(RetryWebhookRequest $request, Webhook $webhook)
|
||||
{
|
||||
match ($request->entity) {
|
||||
'invoice' => $includes ='client',
|
||||
'payment' => $includes ='invoices,client',
|
||||
'project' => $includes ='client',
|
||||
'purchase_order' => $includes ='vendor',
|
||||
'quote' => $includes ='client',
|
||||
default => $includes = ''
|
||||
};
|
||||
|
||||
$class = 'App\Models\\'.ucfirst(Str::camel($request->entity));
|
||||
|
||||
$entity = $class::withTrashed()->where('id', $this->decodePrimaryKey($request->entity_id))->company()->first();
|
||||
|
||||
if (!$entity) {
|
||||
return response()->json(['message' => ctrans('texts.record_not_found')], 400);
|
||||
}
|
||||
|
||||
WebhookSingle::dispatchSync($webhook->id, $entity, auth()->user()->company()->db, $includes);
|
||||
|
||||
return $this->itemResponse($webhook);
|
||||
}
|
||||
}
|
||||
|
@ -11,54 +11,55 @@
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use App\Http\Middleware\ApiSecretCheck;
|
||||
use App\Utils\Ninja;
|
||||
use App\Http\Middleware\Cors;
|
||||
use App\Http\Middleware\SetDb;
|
||||
use App\Http\Middleware\Locale;
|
||||
use App\Http\Middleware\SetWebDb;
|
||||
use App\Http\Middleware\UrlSetDb;
|
||||
use App\Http\Middleware\TokenAuth;
|
||||
use App\Http\Middleware\SetEmailDb;
|
||||
use App\Http\Middleware\VerifyHash;
|
||||
use App\Http\Middleware\SetInviteDb;
|
||||
use App\Http\Middleware\TrimStrings;
|
||||
use App\Http\Middleware\Authenticate;
|
||||
use App\Http\Middleware\CheckClientExistence;
|
||||
use App\Http\Middleware\CheckForMaintenanceMode;
|
||||
use App\Http\Middleware\ClientPortalEnabled;
|
||||
use App\Http\Middleware\ContactSetDb;
|
||||
use App\Http\Middleware\QueryLogging;
|
||||
use App\Http\Middleware\TrustProxies;
|
||||
use App\Http\Middleware\UserVerified;
|
||||
use App\Http\Middleware\VendorLocale;
|
||||
use App\Http\Middleware\PhantomSecret;
|
||||
use App\Http\Middleware\SetDocumentDb;
|
||||
use App\Http\Middleware\ApiSecretCheck;
|
||||
use App\Http\Middleware\ContactAccount;
|
||||
use App\Http\Middleware\EncryptCookies;
|
||||
use App\Http\Middleware\SessionDomains;
|
||||
use App\Http\Middleware\ContactKeyLogin;
|
||||
use App\Http\Middleware\ContactRegister;
|
||||
use App\Http\Middleware\ContactSetDb;
|
||||
use App\Http\Middleware\ContactTokenAuth;
|
||||
use App\Http\Middleware\Cors;
|
||||
use App\Http\Middleware\EncryptCookies;
|
||||
use App\Http\Middleware\Locale;
|
||||
use App\Http\Middleware\PasswordProtection;
|
||||
use App\Http\Middleware\PhantomSecret;
|
||||
use App\Http\Middleware\QueryLogging;
|
||||
use App\Http\Middleware\RedirectIfAuthenticated;
|
||||
use App\Http\Middleware\SessionDomains;
|
||||
use App\Http\Middleware\SetDb;
|
||||
use App\Http\Middleware\SetDbByCompanyKey;
|
||||
use App\Http\Middleware\SetDocumentDb;
|
||||
use App\Http\Middleware\SetDomainNameDb;
|
||||
use App\Http\Middleware\SetEmailDb;
|
||||
use App\Http\Middleware\SetInviteDb;
|
||||
use App\Http\Middleware\SetWebDb;
|
||||
use App\Http\Middleware\Shop\ShopTokenAuth;
|
||||
use App\Http\Middleware\TokenAuth;
|
||||
use App\Http\Middleware\TrimStrings;
|
||||
use App\Http\Middleware\TrustProxies;
|
||||
use App\Http\Middleware\UrlSetDb;
|
||||
use App\Http\Middleware\UserVerified;
|
||||
use App\Http\Middleware\VendorContactKeyLogin;
|
||||
use App\Http\Middleware\VendorLocale;
|
||||
use App\Http\Middleware\VerifyCsrfToken;
|
||||
use App\Http\Middleware\VerifyHash;
|
||||
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
||||
use App\Http\Middleware\ContactTokenAuth;
|
||||
use Illuminate\Auth\Middleware\Authorize;
|
||||
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
||||
use App\Http\Middleware\SetDbByCompanyKey;
|
||||
use App\Http\Middleware\PasswordProtection;
|
||||
use App\Http\Middleware\ClientPortalEnabled;
|
||||
use App\Http\Middleware\CheckClientExistence;
|
||||
use App\Http\Middleware\VendorContactKeyLogin;
|
||||
use Illuminate\Http\Middleware\SetCacheHeaders;
|
||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Routing\Middleware\ValidateSignature;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use App\Http\Middleware\CheckForMaintenanceMode;
|
||||
use App\Http\Middleware\RedirectIfAuthenticated;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
use Illuminate\Routing\Middleware\ValidateSignature;
|
||||
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
|
||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequestsWithRedis;
|
||||
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
@ -75,9 +76,7 @@ class Kernel extends HttpKernel
|
||||
TrimStrings::class,
|
||||
ConvertEmptyStringsToNull::class,
|
||||
TrustProxies::class,
|
||||
// \Illuminate\Http\Middleware\HandleCors::class,
|
||||
Cors::class,
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
@ -140,7 +139,6 @@ class Kernel extends HttpKernel
|
||||
'cors' => Cors::class,
|
||||
'guest' => RedirectIfAuthenticated::class,
|
||||
'signed' => ValidateSignature::class,
|
||||
'throttle' => ThrottleRequests::class,
|
||||
'verified' => EnsureEmailIsVerified::class,
|
||||
'query_logging' => QueryLogging::class,
|
||||
'token_auth' => TokenAuth::class,
|
||||
@ -152,7 +150,6 @@ class Kernel extends HttpKernel
|
||||
'email_db' => SetEmailDb::class,
|
||||
'invite_db' => SetInviteDb::class,
|
||||
'password_protected' => PasswordProtection::class,
|
||||
'signed' => ValidateSignature::class,
|
||||
'portal_enabled' => ClientPortalEnabled::class,
|
||||
'url_db' => UrlSetDb::class,
|
||||
'web_db' => SetWebDb::class,
|
||||
@ -162,7 +159,6 @@ class Kernel extends HttpKernel
|
||||
'vendor_locale' => VendorLocale::class,
|
||||
'contact_register' => ContactRegister::class,
|
||||
'verify_hash' => VerifyHash::class,
|
||||
'shop_token_auth' => ShopTokenAuth::class,
|
||||
'phantom_secret' => PhantomSecret::class,
|
||||
'contact_key_login' => ContactKeyLogin::class,
|
||||
'vendor_contact_key_login' => VendorContactKeyLogin::class,
|
||||
@ -170,6 +166,7 @@ class Kernel extends HttpKernel
|
||||
'user_verified' => UserVerified::class,
|
||||
'document_db' => SetDocumentDb::class,
|
||||
'session_domain' => SessionDomains::class,
|
||||
//we dyanamically add the throttle middleware in RouteServiceProvider
|
||||
];
|
||||
|
||||
protected $middlewarePriority = [
|
||||
@ -189,7 +186,6 @@ class Kernel extends HttpKernel
|
||||
ContactTokenAuth::class,
|
||||
ContactKeyLogin::class,
|
||||
Authenticate::class,
|
||||
ShopTokenAuth::class,
|
||||
ContactRegister::class,
|
||||
PhantomSecret::class,
|
||||
CheckClientExistence::class,
|
||||
@ -199,4 +195,5 @@ class Kernel extends HttpKernel
|
||||
SubstituteBindings::class,
|
||||
ContactAccount::class,
|
||||
];
|
||||
|
||||
}
|
||||
|
@ -11,24 +11,24 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use Livewire\Component;
|
||||
use App\Libraries\MultiDB;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\ClientContact;
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\ContactPasswordlessLogin;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Subscription;
|
||||
use App\Repositories\ClientContactRepository;
|
||||
use App\Repositories\ClientRepository;
|
||||
use App\Services\Subscription\SubscriptionService;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Mail\ContactPasswordlessLogin;
|
||||
use App\Repositories\ClientRepository;
|
||||
use App\Repositories\ClientContactRepository;
|
||||
use App\Services\Subscription\SubscriptionService;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
|
||||
class BillingPortalPurchase extends Component
|
||||
{
|
||||
@ -401,7 +401,7 @@ class BillingPortalPurchase extends Component
|
||||
$context = 'purchase';
|
||||
|
||||
// if(Ninja::isHosted() && $this->subscription->service()->recurring_products()->first()?->id == SubscriptionService::WHITE_LABEL) {
|
||||
if(Ninja::isHosted() && $this->subscription->service()->recurring_products()->first()?->product_key == 'whitelabel') {
|
||||
if (Ninja::isHosted() && $this->subscription->service()->recurring_products()->first()?->product_key == 'whitelabel') {
|
||||
$context = 'whitelabel';
|
||||
}
|
||||
|
||||
|
@ -172,8 +172,7 @@ class BillingPortalPurchasev2 extends Component
|
||||
$this->contact = auth()->guard('contact')->user();
|
||||
$this->authenticated = true;
|
||||
$this->payment_started = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$this->bundle = collect();
|
||||
}
|
||||
|
||||
@ -276,7 +275,6 @@ class BillingPortalPurchasev2 extends Component
|
||||
*/
|
||||
public function handleCoupon()
|
||||
{
|
||||
|
||||
$this->resetErrorBag('coupon');
|
||||
$this->resetValidation('coupon');
|
||||
|
||||
@ -311,7 +309,7 @@ class BillingPortalPurchasev2 extends Component
|
||||
'description' => $p->notes,
|
||||
'product_key' => $p->product_key,
|
||||
'unit_cost' => $p->price,
|
||||
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||
'product' => substr(strip_tags($p->markdownNotes()), 0, 50),
|
||||
'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id),
|
||||
'total' => $total,
|
||||
'qty' => $qty,
|
||||
@ -329,7 +327,7 @@ class BillingPortalPurchasev2 extends Component
|
||||
'description' => $p->notes,
|
||||
'product_key' => $p->product_key,
|
||||
'unit_cost' => $p->price,
|
||||
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||
'product' => substr(strip_tags($p->markdownNotes()), 0, 50),
|
||||
'price' => Number::formatMoney($total, $this->subscription->company),
|
||||
'total' => $total,
|
||||
'qty' => $qty,
|
||||
@ -352,7 +350,7 @@ class BillingPortalPurchasev2 extends Component
|
||||
'description' => $p->notes,
|
||||
'product_key' => $p->product_key,
|
||||
'unit_cost' => $p->price,
|
||||
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||
'product' => substr(strip_tags($p->markdownNotes()), 0, 50),
|
||||
'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id),
|
||||
'total' => $total,
|
||||
'qty' => $qty,
|
||||
@ -375,7 +373,7 @@ class BillingPortalPurchasev2 extends Component
|
||||
'description' => $p->notes,
|
||||
'product_key' => $p->product_key,
|
||||
'unit_cost' => $p->price,
|
||||
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||
'product' => substr(strip_tags($p->markdownNotes()), 0, 50),
|
||||
'price' => Number::formatMoney($total, $this->subscription->company),
|
||||
'total' => $total,
|
||||
'qty' => $qty,
|
||||
|
@ -232,8 +232,9 @@ class RequiredClientInfo extends Component
|
||||
if ($cg && $cg->update_details) {
|
||||
$payment_gateway = $cg->driver($this->client)->init();
|
||||
|
||||
// if(method_exists($payment_gateway, "updateCustomer"))
|
||||
// $payment_gateway->updateCustomer();
|
||||
if (method_exists($payment_gateway, "updateCustomer")) {
|
||||
$payment_gateway->updateCustomer();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
124
app/Http/Middleware/ThrottleRequestsWithPredis.php
Normal file
124
app/Http/Middleware/ThrottleRequestsWithPredis.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Cache\RateLimiter;
|
||||
use Illuminate\Redis\Limiters\DurationLimiter;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
|
||||
class ThrottleRequestsWithPredis extends ThrottleRequests
|
||||
{
|
||||
/**
|
||||
* The Redis factory implementation.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Redis\Factory
|
||||
*/
|
||||
protected $redis;
|
||||
|
||||
/**
|
||||
* The timestamp of the end of the current duration by key.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $decaysAt = [];
|
||||
|
||||
/**
|
||||
* The number of remaining slots by key.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $remaining = [];
|
||||
|
||||
/**
|
||||
* Create a new request throttler.
|
||||
*
|
||||
* @param \Illuminate\Cache\RateLimiter $limiter
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(RateLimiter $limiter)
|
||||
{
|
||||
parent::__construct($limiter);
|
||||
|
||||
$this->redis = \Illuminate\Support\Facades\Redis::connection('sentinel-cache');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param array $limits
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
|
||||
*/
|
||||
protected function handleRequest($request, Closure $next, array $limits)
|
||||
{
|
||||
foreach ($limits as $limit) {
|
||||
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decayMinutes)) {
|
||||
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
|
||||
}
|
||||
}
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
foreach ($limits as $limit) {
|
||||
$response = $this->addHeaders(
|
||||
$response,
|
||||
$limit->maxAttempts,
|
||||
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given key has been "accessed" too many times.
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $maxAttempts
|
||||
* @param int $decayMinutes
|
||||
* @return mixed
|
||||
*/
|
||||
protected function tooManyAttempts($key, $maxAttempts, $decayMinutes)
|
||||
{
|
||||
$limiter = new DurationLimiter(
|
||||
$this->redis,
|
||||
$key,
|
||||
$maxAttempts,
|
||||
$decayMinutes * 60
|
||||
);
|
||||
|
||||
return tap(! $limiter->acquire(), function () use ($key, $limiter) {
|
||||
[$this->decaysAt[$key], $this->remaining[$key]] = [
|
||||
$limiter->decaysAt, $limiter->remaining,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the number of remaining attempts.
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $maxAttempts
|
||||
* @param int|null $retryAfter
|
||||
* @return int
|
||||
*/
|
||||
protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
|
||||
{
|
||||
return is_null($retryAfter) ? $this->remaining[$key] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of seconds until the lock is released.
|
||||
*
|
||||
* @param string $key
|
||||
* @return int
|
||||
*/
|
||||
protected function getTimeUntilNextRetry($key)
|
||||
{
|
||||
return $this->decaysAt[$key] - $this->currentTime();
|
||||
}
|
||||
}
|
@ -29,10 +29,11 @@ class UploadBankIntegrationRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -29,10 +29,11 @@ class UploadBankTransactionRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -38,10 +38,11 @@ class StoreClientRequest extends Request
|
||||
|
||||
public function rules()
|
||||
{
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -38,10 +38,11 @@ class UpdateClientRequest extends Request
|
||||
{
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -29,10 +29,11 @@ class UploadClientRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\ClientPortal\PrePayments;
|
||||
|
||||
use App\Http\ViewComposers\PortalComposer;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StorePrePaymentRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_INVOICES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'notes' => 'required|bail|',
|
||||
'amount' => 'required|bail|gte:minimum_amount|numeric',
|
||||
'minimum_amount' => '',
|
||||
];
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
|
||||
$this->replace($input);
|
||||
|
||||
}
|
||||
}
|
@ -29,10 +29,11 @@ class UploadCompanyRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -43,10 +43,11 @@ class StoreCreditRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -42,10 +42,11 @@ class UpdateCreditRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -29,10 +29,11 @@ class UploadCreditRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -32,13 +32,18 @@ class StoreDesignRequest extends Request
|
||||
return [
|
||||
//'name' => 'required',
|
||||
'name' => 'required|unique:designs,name,null,null,company_id,'.auth()->user()->companyId(),
|
||||
'design' => 'required',
|
||||
'design' => 'required|array',
|
||||
'design.header' => 'required|min:1',
|
||||
'design.body' => 'required|min:1',
|
||||
'design.footer' => 'required|min:1',
|
||||
'design.includes' => 'required|min:1',
|
||||
];
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
$input['design'] = (isset($input['design']) && is_array($input['design'])) ? $input['design'] : [];
|
||||
|
||||
if (! array_key_exists('product', $input['design']) || is_null($input['design']['product'])) {
|
||||
$input['design']['product'] = '';
|
||||
|
@ -43,6 +43,7 @@ class SendEmailRequest extends Request
|
||||
'template' => 'bail|required',
|
||||
'entity' => 'bail|required',
|
||||
'entity_id' => 'bail|required',
|
||||
'cc_email' => 'bail|sometimes|email|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -29,10 +29,11 @@ class UploadExpenseRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -29,10 +29,11 @@ class UploadGroupSettingRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -37,10 +37,11 @@ class StoreInvoiceRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -39,10 +39,11 @@ class UpdateInvoiceRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Http\Requests\Invoice;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
|
||||
class UploadInvoiceRequest extends Request
|
||||
{
|
||||
@ -29,10 +30,11 @@ class UploadInvoiceRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
@ -46,5 +48,23 @@ class UploadInvoiceRequest extends Request
|
||||
public function prepareForValidation()
|
||||
{
|
||||
|
||||
//tests to see if upload via binary data works.
|
||||
|
||||
// if(request()->getContent())
|
||||
// {
|
||||
// // $file = new UploadedFile(request()->getContent(), request()->header('filename'));
|
||||
// $file = new UploadedFile(request()->getContent(), 'something.png');
|
||||
// // request()->files->set('documents', $file);
|
||||
|
||||
// $this->files->add(['file' => $file]);
|
||||
|
||||
// // Merge it in request also (As I found this is not needed in every case)
|
||||
// $this->merge(['file' => $file]);
|
||||
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -112,10 +112,11 @@ class StorePaymentRequest extends Request
|
||||
|
||||
];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -44,10 +44,11 @@ class UpdatePaymentRequest extends Request
|
||||
$rules['number'] = Rule::unique('payments')->where('company_id', auth()->user()->company()->id)->ignore($this->payment->id);
|
||||
}
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -29,10 +29,11 @@ class UploadPaymentRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -47,6 +47,7 @@ class DesignPreviewRequest extends Request
|
||||
'settings' => 'sometimes',
|
||||
'group_id' => 'sometimes',
|
||||
'client_id' => 'sometimes',
|
||||
'design' => 'bail|sometimes|array'
|
||||
];
|
||||
|
||||
return $rules;
|
||||
|
@ -12,10 +12,6 @@
|
||||
namespace App\Http\Requests\Preview;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
namespace App\Http\Requests\Preview;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
|
@ -28,10 +28,11 @@ class StoreProductRequest extends Request
|
||||
|
||||
public function rules()
|
||||
{
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -12,7 +12,6 @@
|
||||
namespace App\Http\Requests\Product;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Product;
|
||||
use App\Utils\Traits\ChecksEntityStatus;
|
||||
|
||||
class UpdateProductRequest extends Request
|
||||
@ -31,10 +30,11 @@ class UpdateProductRequest extends Request
|
||||
|
||||
public function rules()
|
||||
{
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -28,10 +28,11 @@ class UploadProductRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
$rules = [];
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -11,8 +11,8 @@
|
||||
|
||||
namespace App\Http\Requests\Project;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Project;
|
||||
|
||||
class CreateProjectRequest extends Request
|
||||
{
|
||||
@ -24,6 +24,5 @@ class CreateProjectRequest extends Request
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', Project::class);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ class ShowProjectRequest extends Request
|
||||
{
|
||||
// return auth()->user()->isAdmin();
|
||||
return auth()->user()->can('view', $this->project);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,10 +42,11 @@ class StoreProjectRequest extends Request
|
||||
$rules['number'] = Rule::unique('projects')->where('company_id', auth()->user()->company()->id);
|
||||
}
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -37,10 +37,11 @@ class UpdateProjectRequest extends Request
|
||||
$rules['number'] = Rule::unique('projects')->where('company_id', auth()->user()->company()->id)->ignore($this->project->id);
|
||||
}
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -29,10 +29,11 @@ class UploadProjectRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -47,10 +47,11 @@ class StorePurchaseOrderRequest extends Request
|
||||
$rules['is_amount_discount'] = ['boolean'];
|
||||
$rules['line_items'] = 'array';
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -50,10 +50,11 @@ class UpdatePurchaseOrderRequest extends Request
|
||||
$rules['discount'] = 'sometimes|numeric';
|
||||
$rules['is_amount_discount'] = ['boolean'];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -29,10 +29,11 @@ class UploadPurchaseOrderRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if($this->file('documents') && is_array($this->file('documents')))
|
||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
elseif($this->file('documents'))
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user