mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge remote-tracking branch 'origin/v5-develop' into HEAD
This commit is contained in:
commit
72009d2269
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -8,7 +8,7 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
|
|
||||||
<!-- Before posting please check our "Troubleshooting" category in the docs:
|
<!-- Before posting please check our "Troubleshooting" category in the docs:
|
||||||
https://invoiceninja.github.io/docs/self-host-troubleshooting/ -->
|
https://invoiceninja.github.io/en/self-host-troubleshooting/ -->
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
- Version: <!-- i.e. v4.5.25 / v5.0.30 -->
|
- Version: <!-- i.e. v4.5.25 / v5.0.30 -->
|
||||||
|
@ -51,6 +51,8 @@ All Pro and Enterprise features from the hosted app are included in the open-sou
|
|||||||
* [Docker File](https://hub.docker.com/r/invoiceninja/invoiceninja/)
|
* [Docker File](https://hub.docker.com/r/invoiceninja/invoiceninja/)
|
||||||
* [Cloudron](https://www.cloudron.io/store/com.invoiceninja.cloudronapp2.html)
|
* [Cloudron](https://www.cloudron.io/store/com.invoiceninja.cloudronapp2.html)
|
||||||
* [Softaculous](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja)
|
* [Softaculous](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja)
|
||||||
|
* [Elestio](https://elest.io/open-source/invoiceninja)
|
||||||
|
* [YunoHost](https://apps.yunohost.org/app/invoiceninja5)
|
||||||
|
|
||||||
### Recommended Providers
|
### Recommended Providers
|
||||||
* [Stripe](https://stripe.com/)
|
* [Stripe](https://stripe.com/)
|
||||||
|
@ -1 +1 @@
|
|||||||
5.10.17
|
5.10.27
|
50
app/Casts/QuickbooksSettingsCast.php
Normal file
50
app/Casts/QuickbooksSettingsCast.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Casts;
|
||||||
|
|
||||||
|
use App\DataMapper\QuickbooksSettings;
|
||||||
|
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||||
|
|
||||||
|
class QuickbooksSettingsCast implements CastsAttributes
|
||||||
|
{
|
||||||
|
public function get($model, string $key, $value, array $attributes)
|
||||||
|
{
|
||||||
|
$data = json_decode($value, true);
|
||||||
|
|
||||||
|
if(!is_array($data))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$qb = new QuickbooksSettings();
|
||||||
|
$qb->accessTokenKey = $data['accessTokenKey'];
|
||||||
|
$qb->refresh_token = $data['refresh_token'];
|
||||||
|
$qb->realmID = $data['realmID'];
|
||||||
|
$qb->accessTokenExpiresAt = $data['accessTokenExpiresAt'];
|
||||||
|
$qb->refreshTokenExpiresAt = $data['refreshTokenExpiresAt'];
|
||||||
|
$qb->settings = $data['settings'] ?? [];
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set($model, string $key, $value, array $attributes)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
$key => json_encode([
|
||||||
|
'accessTokenKey' => $value->accessTokenKey,
|
||||||
|
'refresh_token' => $value->refresh_token,
|
||||||
|
'realmID' => $value->realmID,
|
||||||
|
'accessTokenExpiresAt' => $value->accessTokenExpiresAt,
|
||||||
|
'refreshTokenExpiresAt' => $value->refreshTokenExpiresAt,
|
||||||
|
'settings' => $value->settings,
|
||||||
|
])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -1169,7 +1169,7 @@ class CheckData extends Command
|
|||||||
->whereNull('exchange_rate')
|
->whereNull('exchange_rate')
|
||||||
->orWhere('exchange_rate', 0)
|
->orWhere('exchange_rate', 0)
|
||||||
->cursor()
|
->cursor()
|
||||||
->each(function ($expense){
|
->each(function ($expense) {
|
||||||
$expense->exchange_rate = 1;
|
$expense->exchange_rate = 1;
|
||||||
$expense->saveQuietly();
|
$expense->saveQuietly();
|
||||||
|
|
||||||
|
83
app/DataMapper/Analytics/LegalEntityCreated.php
Normal file
83
app/DataMapper/Analytics/LegalEntityCreated.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\DataMapper\Analytics;
|
||||||
|
|
||||||
|
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
|
||||||
|
|
||||||
|
class LegalEntityCreated extends GenericMixedMetric
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The type of Sample.
|
||||||
|
*
|
||||||
|
* Monotonically incrementing counter
|
||||||
|
*
|
||||||
|
* - counter
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $type = 'mixed_metric';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the counter.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $name = 'einvoice.legal_entity.created';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The datetime of the counter measurement.
|
||||||
|
*
|
||||||
|
* date("Y-m-d H:i:s")
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public $datetime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Class failure name
|
||||||
|
* set to 0.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $string_metric5 = 'stub';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The exception string
|
||||||
|
* set to 0.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $string_metric6 = 'stub';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The counter
|
||||||
|
* set to 1.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public $int_metric1 = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Company Key
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $string_metric7 = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subject
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $string_metric8 = '';
|
||||||
|
|
||||||
|
public function __construct($string_metric7 = '', $string_metric8 = '')
|
||||||
|
{
|
||||||
|
$this->string_metric7 = $string_metric7;
|
||||||
|
$this->string_metric8 = $string_metric8;
|
||||||
|
}
|
||||||
|
}
|
@ -516,9 +516,15 @@ class CompanySettings extends BaseSettings
|
|||||||
public $quote_late_fee_amount1 = 0;
|
public $quote_late_fee_amount1 = 0;
|
||||||
public $quote_late_fee_percent1 = 0;
|
public $quote_late_fee_percent1 = 0;
|
||||||
|
|
||||||
|
public string $payment_flow = 'default'; //smooth
|
||||||
|
|
||||||
|
public string $email_subject_payment_failed = '';
|
||||||
|
public string $email_template_payment_failed = '';
|
||||||
|
|
||||||
public static $casts = [
|
public static $casts = [
|
||||||
|
'email_template_payment_failed' => 'string',
|
||||||
|
'email_subject_payment_failed' => 'string',
|
||||||
|
'payment_flow' => 'string',
|
||||||
'enable_quote_reminder1' => 'bool',
|
'enable_quote_reminder1' => 'bool',
|
||||||
'quote_num_days_reminder1' => 'int',
|
'quote_num_days_reminder1' => 'int',
|
||||||
'quote_schedule_reminder1' => 'string',
|
'quote_schedule_reminder1' => 'string',
|
||||||
|
@ -30,6 +30,7 @@ class EmailTemplateDefaults
|
|||||||
'email_template_custom2',
|
'email_template_custom2',
|
||||||
'email_template_custom3',
|
'email_template_custom3',
|
||||||
'email_template_purchase_order',
|
'email_template_purchase_order',
|
||||||
|
'email_template_payment_failed'
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function getDefaultTemplate($template, $locale)
|
public static function getDefaultTemplate($template, $locale)
|
||||||
@ -39,6 +40,8 @@ class EmailTemplateDefaults
|
|||||||
switch ($template) {
|
switch ($template) {
|
||||||
/* Template */
|
/* Template */
|
||||||
|
|
||||||
|
case 'email_template_payment_failed':
|
||||||
|
return self::emailPaymentFailedTemplate();
|
||||||
case 'email_template_invoice':
|
case 'email_template_invoice':
|
||||||
return self::emailInvoiceTemplate();
|
return self::emailInvoiceTemplate();
|
||||||
case 'email_template_quote':
|
case 'email_template_quote':
|
||||||
@ -73,6 +76,9 @@ class EmailTemplateDefaults
|
|||||||
case 'email_subject_invoice':
|
case 'email_subject_invoice':
|
||||||
return self::emailInvoiceSubject();
|
return self::emailInvoiceSubject();
|
||||||
|
|
||||||
|
case 'email_subject_payment_failed':
|
||||||
|
return self::emailPaymentFailedSubject();
|
||||||
|
|
||||||
case 'email_subject_quote':
|
case 'email_subject_quote':
|
||||||
return self::emailQuoteSubject();
|
return self::emailQuoteSubject();
|
||||||
|
|
||||||
@ -127,6 +133,16 @@ class EmailTemplateDefaults
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function emailPaymentFailedSubject()
|
||||||
|
{
|
||||||
|
return ctrans('texts.notification_invoice_payment_failed_subject', ['invoice' => '$number']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function emailPaymentFailedTemplate()
|
||||||
|
{
|
||||||
|
return '<p>$client<br><br>'.ctrans('texts.client_payment_failure_body', ['invoice' => '$number', 'amount' => '$amount']).'</p><div>$payment_error</div><br><div>$payment_button</div>';
|
||||||
|
}
|
||||||
|
|
||||||
public static function emailQuoteReminder1Subject()
|
public static function emailQuoteReminder1Subject()
|
||||||
{
|
{
|
||||||
return ctrans('texts.quote_reminder_subject', ['quote' => '$number', 'company' => '$company.name']);
|
return ctrans('texts.quote_reminder_subject', ['quote' => '$number', 'company' => '$company.name']);
|
||||||
@ -135,9 +151,7 @@ class EmailTemplateDefaults
|
|||||||
public static function emailQuoteReminder1Body()
|
public static function emailQuoteReminder1Body()
|
||||||
{
|
{
|
||||||
|
|
||||||
$invoice_message = '<p>$client<br><br>'.self::transformText('quote_reminder_message').'</p><div class="center">$view_button</div>';
|
return '<p>$client<br><br>'.self::transformText('quote_reminder_message').'</p><div>$view_button</div>';
|
||||||
|
|
||||||
return $invoice_message;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,14 +177,14 @@ class EmailTemplateDefaults
|
|||||||
|
|
||||||
public static function emailInvoiceTemplate()
|
public static function emailInvoiceTemplate()
|
||||||
{
|
{
|
||||||
$invoice_message = '<p>$client<br><br>'.self::transformText('invoice_message').'</p><div class="center">$view_button</div>';
|
$invoice_message = '<p>$client<br><br>'.self::transformText('invoice_message').'</p><div>$view_button</div>';
|
||||||
|
|
||||||
return $invoice_message;
|
return $invoice_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function emailInvoiceReminderTemplate()
|
public static function emailInvoiceReminderTemplate()
|
||||||
{
|
{
|
||||||
$invoice_message = '<p>$client<br><br>'.self::transformText('reminder_message').'</p><div class="center">$view_button</div>';
|
$invoice_message = '<p>$client<br><br>'.self::transformText('reminder_message').'</p><div>$view_button</div>';
|
||||||
|
|
||||||
return $invoice_message;
|
return $invoice_message;
|
||||||
}
|
}
|
||||||
@ -182,7 +196,7 @@ class EmailTemplateDefaults
|
|||||||
|
|
||||||
public static function emailQuoteTemplate()
|
public static function emailQuoteTemplate()
|
||||||
{
|
{
|
||||||
$quote_message = '<p>$client<br><br>'.self::transformText('quote_message').'</p><div class="center">$view_button</div>';
|
$quote_message = '<p>$client<br><br>'.self::transformText('quote_message').'</p><div>$view_button</div>';
|
||||||
|
|
||||||
return $quote_message;
|
return $quote_message;
|
||||||
}
|
}
|
||||||
@ -199,28 +213,28 @@ class EmailTemplateDefaults
|
|||||||
|
|
||||||
public static function emailPurchaseOrderTemplate()
|
public static function emailPurchaseOrderTemplate()
|
||||||
{
|
{
|
||||||
$purchase_order_message = '<p>$vendor<br><br>'.self::transformText('purchase_order_message').'</p><div class="center">$view_button</div>';
|
$purchase_order_message = '<p>$vendor<br><br>'.self::transformText('purchase_order_message').'</p><div>$view_button</div>';
|
||||||
|
|
||||||
return $purchase_order_message;
|
return $purchase_order_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function emailPaymentTemplate()
|
public static function emailPaymentTemplate()
|
||||||
{
|
{
|
||||||
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div class="center">$view_button</div>';
|
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div>$view_button</div>';
|
||||||
|
|
||||||
return $payment_message;
|
return $payment_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function emailCreditTemplate()
|
public static function emailCreditTemplate()
|
||||||
{
|
{
|
||||||
$credit_message = '<p>$client<br><br>'.self::transformText('credit_message').'</p><div class="center">$view_button</div>';
|
$credit_message = '<p>$client<br><br>'.self::transformText('credit_message').'</p><div>$view_button</div>';
|
||||||
|
|
||||||
return $credit_message;
|
return $credit_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function emailPaymentPartialTemplate()
|
public static function emailPaymentPartialTemplate()
|
||||||
{
|
{
|
||||||
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div class="center">$view_button</div>';
|
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div>$view_button</div>';
|
||||||
|
|
||||||
return $payment_message;
|
return $payment_message;
|
||||||
}
|
}
|
||||||
|
60
app/DataMapper/QuickbooksSettings.php
Normal file
60
app/DataMapper/QuickbooksSettings.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\DataMapper;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Database\Eloquent\Castable;
|
||||||
|
use App\Casts\QuickbooksSettingsCast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QuickbooksSettings.
|
||||||
|
*/
|
||||||
|
class QuickbooksSettings implements Castable
|
||||||
|
{
|
||||||
|
public string $accessTokenKey;
|
||||||
|
|
||||||
|
public string $refresh_token;
|
||||||
|
|
||||||
|
public string $realmID;
|
||||||
|
|
||||||
|
public int $accessTokenExpiresAt;
|
||||||
|
|
||||||
|
public int $refreshTokenExpiresAt;
|
||||||
|
|
||||||
|
public string $baseURL;
|
||||||
|
/**
|
||||||
|
* entity client,invoice,quote,purchase_order,vendor,payment
|
||||||
|
* sync true/false
|
||||||
|
* update_record true/false
|
||||||
|
* direction push/pull/birectional
|
||||||
|
* */
|
||||||
|
public array $settings = [
|
||||||
|
'client' => ['sync' => true, 'update_record' => true, 'direction' => 'bidirectional'],
|
||||||
|
'vendor' => ['sync' => true, 'update_record' => true, 'direction' => 'bidirectional'],
|
||||||
|
'invoice' => ['sync' => true, 'update_record' => true, 'direction' => 'bidirectional'],
|
||||||
|
'quote' => ['sync' => true, 'update_record' => true, 'direction' => 'bidirectional'],
|
||||||
|
'purchase_order' => ['sync' => true, 'update_record' => true, 'direction' => 'bidirectional'],
|
||||||
|
'product' => ['sync' => true, 'update_record' => true, 'direction' => 'bidirectional'],
|
||||||
|
'payment' => ['sync' => true, 'update_record' => true, 'direction' => 'bidirectional'],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the caster class to use when casting from / to this cast target.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $arguments
|
||||||
|
*/
|
||||||
|
public static function castUsing(array $arguments): string
|
||||||
|
{
|
||||||
|
return QuickbooksSettingsCast::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,6 +17,7 @@ use App\Models\Invoice;
|
|||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\DataProviders\USStates;
|
use App\DataProviders\USStates;
|
||||||
use App\DataMapper\Tax\ZipTax\Response;
|
use App\DataMapper\Tax\ZipTax\Response;
|
||||||
|
use App\Models\RecurringInvoice;
|
||||||
|
|
||||||
class BaseRule implements RuleInterface
|
class BaseRule implements RuleInterface
|
||||||
{
|
{
|
||||||
@ -132,7 +133,7 @@ class BaseRule implements RuleInterface
|
|||||||
|
|
||||||
public function shouldCalcTax(): bool
|
public function shouldCalcTax(): bool
|
||||||
{
|
{
|
||||||
return $this->should_calc_tax;
|
return $this->should_calc_tax && $this->checkIfInvoiceLocked();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Initializes the tax rule for the entity.
|
* Initializes the tax rule for the entity.
|
||||||
@ -215,7 +216,7 @@ class BaseRule implements RuleInterface
|
|||||||
|
|
||||||
$this->invoice->tax_data = $tax_data;
|
$this->invoice->tax_data = $tax_data;
|
||||||
|
|
||||||
if(\DB::transactionLevel() == 0) {
|
if(\DB::transactionLevel() == 0 && isset($this->invoice->id)) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->invoice->saveQuietly();
|
$this->invoice->saveQuietly();
|
||||||
@ -400,4 +401,40 @@ class BaseRule implements RuleInterface
|
|||||||
return ! in_array($iso_3166_2, array_merge($this->eu_country_codes, array_keys($this->region_codes)));
|
return ! in_array($iso_3166_2, array_merge($this->eu_country_codes, array_keys($this->region_codes)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function checkIfInvoiceLocked(): bool
|
||||||
|
{
|
||||||
|
$lock_invoices = $this->client->getSetting('lock_invoices');
|
||||||
|
|
||||||
|
if($this->invoice instanceof RecurringInvoice) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($lock_invoices) {
|
||||||
|
case 'off':
|
||||||
|
return true;
|
||||||
|
case 'when_sent':
|
||||||
|
if ($this->invoice->status_id == Invoice::STATUS_SENT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case 'when_paid':
|
||||||
|
if ($this->invoice->status_id == Invoice::STATUS_PAID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
//if now is greater than the end of month the invoice was dated - do not modify
|
||||||
|
case 'end_of_month':
|
||||||
|
if(\Carbon\Carbon::parse($this->invoice->date)->setTimezone($this->invoice->company->timezone()->name)->endOfMonth()->lte(now())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,8 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
public float $reduced_tax_rate = 0;
|
public float $reduced_tax_rate = 0;
|
||||||
|
|
||||||
public string $tax_name1 = 'MwSt.';
|
public string $tax_name1 = 'MwSt.';
|
||||||
|
|
||||||
|
private string $tax_name;
|
||||||
/**
|
/**
|
||||||
* Initializes the rules and builds any required data.
|
* Initializes the rules and builds any required data.
|
||||||
*
|
*
|
||||||
@ -50,6 +52,7 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
*/
|
*/
|
||||||
public function init(): self
|
public function init(): self
|
||||||
{
|
{
|
||||||
|
$this->tax_name = $this->tax_name1;
|
||||||
$this->calculateRates();
|
$this->calculateRates();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -91,6 +94,7 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
*/
|
*/
|
||||||
public function reverseTax($item): self
|
public function reverseTax($item): self
|
||||||
{
|
{
|
||||||
|
$this->tax_name1 = $this->tax_name;
|
||||||
$this->tax_rate1 = 0;
|
$this->tax_rate1 = 0;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -103,6 +107,8 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
*/
|
*/
|
||||||
public function taxReduced($item): self
|
public function taxReduced($item): self
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$this->tax_name1 = $this->tax_name;
|
||||||
$this->tax_rate1 = $this->reduced_tax_rate;
|
$this->tax_rate1 = $this->reduced_tax_rate;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -115,6 +121,8 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
*/
|
*/
|
||||||
public function zeroRated($item): self
|
public function zeroRated($item): self
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$this->tax_name1 = $this->tax_name;
|
||||||
$this->tax_rate1 = 0;
|
$this->tax_rate1 = 0;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -142,6 +150,7 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
public function taxDigital($item): self
|
public function taxDigital($item): self
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$this->tax_name1 = $this->tax_name;
|
||||||
$this->tax_rate1 = $this->tax_rate;
|
$this->tax_rate1 = $this->tax_rate;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -155,6 +164,7 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
public function taxService($item): self
|
public function taxService($item): self
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$this->tax_name1 = $this->tax_name;
|
||||||
$this->tax_rate1 = $this->tax_rate;
|
$this->tax_rate1 = $this->tax_rate;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -168,6 +178,7 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
public function taxShipping($item): self
|
public function taxShipping($item): self
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$this->tax_name1 = $this->tax_name;
|
||||||
$this->tax_rate1 = $this->tax_rate;
|
$this->tax_rate1 = $this->tax_rate;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -181,6 +192,7 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
public function taxPhysical($item): self
|
public function taxPhysical($item): self
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$this->tax_name1 = $this->tax_name;
|
||||||
$this->tax_rate1 = $this->tax_rate;
|
$this->tax_rate1 = $this->tax_rate;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -229,8 +241,7 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
// nlog("tax exempt");
|
// nlog("tax exempt");
|
||||||
$this->tax_rate = 0;
|
$this->tax_rate = 0;
|
||||||
$this->reduced_tax_rate = 0;
|
$this->reduced_tax_rate = 0;
|
||||||
} elseif($this->client_subregion != $this->client->company->tax_data->seller_subregion && in_array($this->client_subregion, $this->eu_country_codes) && $this->client->vat_number && $this->eu_business_tax_exempt) {
|
} elseif($this->client_subregion != $this->client->company->tax_data->seller_subregion && in_array($this->client_subregion, $this->eu_country_codes) && $this->client->vat_number && $this->client->has_valid_vat_number && $this->eu_business_tax_exempt) {
|
||||||
// elseif($this->client_subregion != $this->client->company->tax_data->seller_subregion && in_array($this->client_subregion, $this->eu_country_codes) && $this->client->has_valid_vat_number && $this->eu_business_tax_exempt)
|
|
||||||
// nlog("euro zone and tax exempt");
|
// nlog("euro zone and tax exempt");
|
||||||
$this->tax_rate = 0;
|
$this->tax_rate = 0;
|
||||||
$this->reduced_tax_rate = 0;
|
$this->reduced_tax_rate = 0;
|
||||||
@ -240,8 +251,8 @@ class Rule extends BaseRule implements RuleInterface
|
|||||||
$this->reduced_tax_rate = 0;
|
$this->reduced_tax_rate = 0;
|
||||||
} elseif(!in_array($this->client_subregion, $this->eu_country_codes)) {
|
} elseif(!in_array($this->client_subregion, $this->eu_country_codes)) {
|
||||||
$this->defaultForeign();
|
$this->defaultForeign();
|
||||||
} elseif(in_array($this->client_subregion, $this->eu_country_codes) && !$this->client->vat_number) { //eu country / no valid vat
|
} elseif(in_array($this->client_subregion, $this->eu_country_codes) && ((strlen($this->client->vat_number ?? '') == 1) || !$this->client->has_valid_vat_number)) { //eu country / no valid vat
|
||||||
if(($this->client->company->tax_data->seller_subregion != $this->client_subregion) && $this->client->company->tax_data->regions->EU->has_sales_above_threshold) {
|
if($this->client->company->tax_data->seller_subregion != $this->client_subregion) {
|
||||||
// nlog("eu zone with sales above threshold");
|
// nlog("eu zone with sales above threshold");
|
||||||
$this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->tax_rate ?? 0;
|
$this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->tax_rate ?? 0;
|
||||||
$this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_tax_rate ?? 0;
|
$this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_tax_rate ?? 0;
|
||||||
|
@ -48,8 +48,7 @@ class TaxModel
|
|||||||
public function migrate(): self
|
public function migrate(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
if($this->version == 'alpha')
|
if($this->version == 'alpha') {
|
||||||
{
|
|
||||||
$this->regions->EU->subregions->PL = new \stdClass();
|
$this->regions->EU->subregions->PL = new \stdClass();
|
||||||
$this->regions->EU->subregions->PL->tax_rate = 23;
|
$this->regions->EU->subregions->PL->tax_rate = 23;
|
||||||
$this->regions->EU->subregions->PL->tax_name = 'VAT';
|
$this->regions->EU->subregions->PL->tax_name = 'VAT';
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
namespace App\DataProviders;
|
namespace App\DataProviders;
|
||||||
|
|
||||||
final class CAProvinces {
|
final class CAProvinces
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* The provinces and territories of Canada
|
* The provinces and territories of Canada
|
||||||
*
|
*
|
||||||
@ -30,7 +40,8 @@ final class CAProvinces {
|
|||||||
* @param string $abbreviation
|
* @param string $abbreviation
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function getName($abbreviation) {
|
public static function getName($abbreviation)
|
||||||
|
{
|
||||||
return self::$provinces[$abbreviation];
|
return self::$provinces[$abbreviation];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +50,8 @@ final class CAProvinces {
|
|||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function get() {
|
public static function get()
|
||||||
|
{
|
||||||
return self::$provinces;
|
return self::$provinces;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +61,8 @@ final class CAProvinces {
|
|||||||
* @param string $name
|
* @param string $name
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function getAbbreviation($name) {
|
public static function getAbbreviation($name)
|
||||||
|
{
|
||||||
return array_search(ucwords($name), self::$provinces);
|
return array_search(ucwords($name), self::$provinces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\DataProviders;
|
|
||||||
|
|
||||||
use Omnipay\Rotessa\Object\Frequency;
|
|
||||||
|
|
||||||
final class Frequencies
|
|
||||||
{
|
|
||||||
public static function get() : array {
|
|
||||||
return Frequency::getTypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFromType() {
|
|
||||||
|
|
||||||
}
|
|
||||||
public static function getOnePayment() {
|
|
||||||
return Frequency::ONCE;
|
|
||||||
}
|
|
||||||
}
|
|
35
app/Events/Invoice/InvoiceAutoBillFailed.php
Normal file
35
app/Events/Invoice/InvoiceAutoBillFailed.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Events\Invoice;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class InvoiceAutoBillFailed.
|
||||||
|
*/
|
||||||
|
class InvoiceAutoBillFailed
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @param Invoice $invoice
|
||||||
|
* @param Company $company
|
||||||
|
* @param array $event_vars
|
||||||
|
*/
|
||||||
|
public function __construct(public Invoice $invoice, public Company $company, public array $event_vars, public ?string $notes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
35
app/Events/Invoice/InvoiceAutoBillSuccess.php
Normal file
35
app/Events/Invoice/InvoiceAutoBillSuccess.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Events\Invoice;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class InvoiceAutoBillSuccess.
|
||||||
|
*/
|
||||||
|
class InvoiceAutoBillSuccess
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @param Invoice $invoice
|
||||||
|
* @param Company $company
|
||||||
|
* @param array $event_vars
|
||||||
|
*/
|
||||||
|
public function __construct(public Invoice $invoice, public Company $company, public array $event_vars)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -451,6 +451,7 @@ class BaseExport
|
|||||||
'project' => 'task.project_id',
|
'project' => 'task.project_id',
|
||||||
'billable' => 'task.billable',
|
'billable' => 'task.billable',
|
||||||
'item_notes' => 'task.item_notes',
|
'item_notes' => 'task.item_notes',
|
||||||
|
'time_log' => 'task.time_log',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected array $forced_client_fields = [
|
protected array $forced_client_fields = [
|
||||||
@ -1040,7 +1041,7 @@ class BaseExport
|
|||||||
|
|
||||||
$recurring_filters = [];
|
$recurring_filters = [];
|
||||||
|
|
||||||
if($this->company->getSetting('report_include_drafts')){
|
if($this->company->getSetting('report_include_drafts')) {
|
||||||
$recurring_filters[] = RecurringInvoice::STATUS_DRAFT;
|
$recurring_filters[] = RecurringInvoice::STATUS_DRAFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ class ClientExport extends BaseExport
|
|||||||
$query->where('is_deleted', 0);
|
$query->where('is_deleted', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $this->addDateRange($query,' clients');
|
$query = $this->addDateRange($query, ' clients');
|
||||||
|
|
||||||
if($this->input['document_email_attachment'] ?? false) {
|
if($this->input['document_email_attachment'] ?? false) {
|
||||||
$this->queueDocuments($query);
|
$this->queueDocuments($query);
|
||||||
|
@ -269,8 +269,7 @@ class ExpenseExport extends BaseExport
|
|||||||
|
|
||||||
if($expense->uses_inclusive_taxes) {
|
if($expense->uses_inclusive_taxes) {
|
||||||
$entity['expense.net_amount'] = round($expense->amount, $precision) - $total_tax_amount;
|
$entity['expense.net_amount'] = round($expense->amount, $precision) - $total_tax_amount;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$entity['expense.net_amount'] = round($expense->amount, $precision);
|
$entity['expense.net_amount'] = round($expense->amount, $precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,10 +229,6 @@ class InvoiceItemExport extends BaseExport
|
|||||||
// $entity['currency'] = $invoice->client->currency() ? $invoice->client->currency()->code : $invoice->company->currency()->code;
|
// $entity['currency'] = $invoice->client->currency() ? $invoice->client->currency()->code : $invoice->company->currency()->code;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// if(array_key_exists('type', $entity)) {
|
|
||||||
// $entity['type'] = $invoice->typeIdString($entity['type']);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if(array_key_exists('tax_category', $entity)) {
|
// if(array_key_exists('tax_category', $entity)) {
|
||||||
// $entity['tax_category'] = $invoice->taxTypeString($entity['tax_category']);
|
// $entity['tax_category'] = $invoice->taxTypeString($entity['tax_category']);
|
||||||
// }
|
// }
|
||||||
|
@ -213,10 +213,6 @@ class PurchaseOrderItemExport extends BaseExport
|
|||||||
// $entity['currency'] = $purchase_order->vendor->currency() ? $purchase_order->vendor->currency()->code : $purchase_order->company->currency()->code;
|
// $entity['currency'] = $purchase_order->vendor->currency() ? $purchase_order->vendor->currency()->code : $purchase_order->company->currency()->code;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// if(array_key_exists('type', $entity)) {
|
|
||||||
// $entity['type'] = $purchase_order->typeIdString($entity['type']);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if(array_key_exists('tax_category', $entity)) {
|
// if(array_key_exists('tax_category', $entity)) {
|
||||||
// $entity['tax_category'] = $purchase_order->taxTypeString($entity['tax_category']);
|
// $entity['tax_category'] = $purchase_order->taxTypeString($entity['tax_category']);
|
||||||
// }
|
// }
|
||||||
|
@ -156,7 +156,7 @@ class TaskExport extends BaseExport
|
|||||||
$entity[$key] = $transformed_entity[$parts[1]];
|
$entity[$key] = $transformed_entity[$parts[1]];
|
||||||
} elseif (array_key_exists($key, $transformed_entity)) {
|
} elseif (array_key_exists($key, $transformed_entity)) {
|
||||||
$entity[$key] = $transformed_entity[$key];
|
$entity[$key] = $transformed_entity[$key];
|
||||||
} elseif (in_array($key, ['task.start_date', 'task.end_date', 'task.duration', 'task.billable', 'task.item_notes'])) {
|
} elseif (in_array($key, ['task.start_date', 'task.end_date', 'task.duration', 'task.billable', 'task.item_notes', 'task.time_log'])) {
|
||||||
$entity[$key] = '';
|
$entity[$key] = '';
|
||||||
} else {
|
} else {
|
||||||
$entity[$key] = $this->decorator->transform($key, $task);
|
$entity[$key] = $this->decorator->transform($key, $task);
|
||||||
@ -207,6 +207,9 @@ class TaskExport extends BaseExport
|
|||||||
$seconds = $task->calcDuration();
|
$seconds = $task->calcDuration();
|
||||||
$entity['task.duration'] = $seconds;
|
$entity['task.duration'] = $seconds;
|
||||||
$entity['task.duration_words'] = $seconds > 86400 ? CarbonInterval::seconds($seconds)->locale($this->company->locale())->cascade()->forHumans() : now()->startOfDay()->addSeconds($seconds)->format('H:i:s');
|
$entity['task.duration_words'] = $seconds > 86400 ? CarbonInterval::seconds($seconds)->locale($this->company->locale())->cascade()->forHumans() : now()->startOfDay()->addSeconds($seconds)->format('H:i:s');
|
||||||
|
|
||||||
|
$entity['task.time_log'] = (isset($item[1]) && $item[1] != 0) ? $item[1] - $item[0] : ctrans('texts.is_running');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array('task.billable', $this->input['report_keys']) || in_array('billable', $this->input['report_keys'])) {
|
if (in_array('task.billable', $this->input['report_keys']) || in_array('billable', $this->input['report_keys'])) {
|
||||||
|
@ -76,6 +76,26 @@ class InvoiceItemFactory
|
|||||||
$data[] = $item;
|
$data[] = $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$item = self::create();
|
||||||
|
$item->quantity = $faker->numberBetween(1, 10);
|
||||||
|
$item->cost = $faker->randomFloat(2, 1, 1000);
|
||||||
|
$item->line_total = $item->quantity * $item->cost;
|
||||||
|
$item->is_amount_discount = true;
|
||||||
|
$item->discount = $faker->numberBetween(1, 10);
|
||||||
|
$item->notes = str_replace(['"',"'"], ['',""], $faker->realText(20));
|
||||||
|
$item->product_key = $faker->word();
|
||||||
|
// $item->custom_value1 = $faker->realText(10);
|
||||||
|
// $item->custom_value2 = $faker->realText(10);
|
||||||
|
// $item->custom_value3 = $faker->realText(10);
|
||||||
|
// $item->custom_value4 = $faker->realText(10);
|
||||||
|
$item->tax_name1 = 'GST';
|
||||||
|
$item->tax_rate1 = 10.00;
|
||||||
|
$item->type_id = '2';
|
||||||
|
|
||||||
|
$data[] = $item;
|
||||||
|
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,14 @@ class CreditFilters extends QueryFilters
|
|||||||
->orWhere('last_name', 'like', '%'.$filter.'%')
|
->orWhere('last_name', 'like', '%'.$filter.'%')
|
||||||
->orWhere('email', 'like', '%'.$filter.'%');
|
->orWhere('email', 'like', '%'.$filter.'%');
|
||||||
})
|
})
|
||||||
->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')) LIKE ?", ['%'.$filter.'%']);
|
->orWhereRaw("
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(
|
||||||
|
JSON_ARRAY(
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')),
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].product_key'))
|
||||||
|
), '$[*]')
|
||||||
|
) LIKE ?", ['%'.$filter.'%']);
|
||||||
|
// ->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')) LIKE ?", ['%'.$filter.'%']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +99,12 @@ class ExpenseFilters extends QueryFilters
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (in_array('uninvoiced', $status_parameters)) {
|
||||||
|
$query->orWhere(function ($query) {
|
||||||
|
$query->whereNull('invoice_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (in_array('paid', $status_parameters)) {
|
if (in_array('paid', $status_parameters)) {
|
||||||
$query->orWhere(function ($query) {
|
$query->orWhere(function ($query) {
|
||||||
$query->whereNotNull('payment_date');
|
$query->whereNotNull('payment_date');
|
||||||
@ -158,6 +164,19 @@ class ExpenseFilters extends QueryFilters
|
|||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function categories(string $categories = ''): Builder
|
||||||
|
{
|
||||||
|
$categories_exploded = explode(",", $categories);
|
||||||
|
|
||||||
|
if(empty($categories) || count(array_filter($categories_exploded)) == 0) {
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
$categories_keys = $this->transformKeys($categories_exploded);
|
||||||
|
|
||||||
|
return $this->builder->whereIn('category_id', $categories_keys);
|
||||||
|
}
|
||||||
|
|
||||||
public function number(string $number = ''): Builder
|
public function number(string $number = ''): Builder
|
||||||
{
|
{
|
||||||
if (strlen($number) == 0) {
|
if (strlen($number) == 0) {
|
||||||
@ -205,6 +224,11 @@ class ExpenseFilters extends QueryFilters
|
|||||||
->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]);
|
->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($sort_col[0] == 'payment_date' && in_array($sort_col[1], ['asc', 'desc'])) {
|
||||||
|
return $this->builder
|
||||||
|
->orderByRaw('ISNULL(payment_date), payment_date '. $sort_col[1]);
|
||||||
|
}
|
||||||
|
|
||||||
if($sort_col[0] == 'number') {
|
if($sort_col[0] == 'number') {
|
||||||
return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir);
|
return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,14 @@ class InvoiceFilters extends QueryFilters
|
|||||||
->orWhere('last_name', 'like', '%'.$filter.'%')
|
->orWhere('last_name', 'like', '%'.$filter.'%')
|
||||||
->orWhere('email', 'like', '%'.$filter.'%');
|
->orWhere('email', 'like', '%'.$filter.'%');
|
||||||
})
|
})
|
||||||
->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')) LIKE ?", ['%'.$filter.'%']);
|
->orWhereRaw("
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(
|
||||||
|
JSON_ARRAY(
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')),
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].product_key'))
|
||||||
|
), '$[*]')
|
||||||
|
) LIKE ?", ['%'.$filter.'%']);
|
||||||
|
// ->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')) LIKE ?", ['%'.$filter.'%']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,58 +266,6 @@ class InvoiceFilters extends QueryFilters
|
|||||||
return $this->builder->where('due_date', '>=', $date);
|
return $this->builder->where('due_date', '>=', $date);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter by date range
|
|
||||||
*
|
|
||||||
* @param string $date_range
|
|
||||||
* @return Builder
|
|
||||||
*/
|
|
||||||
public function date_range(string $date_range = ''): Builder
|
|
||||||
{
|
|
||||||
$parts = explode(",", $date_range);
|
|
||||||
|
|
||||||
if (count($parts) != 2) {
|
|
||||||
return $this->builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
$start_date = Carbon::parse($parts[0]);
|
|
||||||
$end_date = Carbon::parse($parts[1]);
|
|
||||||
|
|
||||||
return $this->builder->whereBetween('date', [$start_date, $end_date]);
|
|
||||||
} catch(\Exception $e) {
|
|
||||||
return $this->builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter by due date range
|
|
||||||
*
|
|
||||||
* @param string $date_range
|
|
||||||
* @return Builder
|
|
||||||
*/
|
|
||||||
public function due_date_range(string $date_range = ''): Builder
|
|
||||||
{
|
|
||||||
$parts = explode(",", $date_range);
|
|
||||||
|
|
||||||
if (count($parts) != 2) {
|
|
||||||
return $this->builder;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
|
|
||||||
$start_date = Carbon::parse($parts[0]);
|
|
||||||
$end_date = Carbon::parse($parts[1]);
|
|
||||||
|
|
||||||
return $this->builder->whereBetween('due_date', [$start_date, $end_date]);
|
|
||||||
} catch(\Exception $e) {
|
|
||||||
return $this->builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts the list based on $sort.
|
* Sorts the list based on $sort.
|
||||||
*
|
*
|
||||||
|
@ -60,7 +60,7 @@ class ProjectFilters extends QueryFilters
|
|||||||
{
|
{
|
||||||
$sort_col = explode('|', $sort);
|
$sort_col = explode('|', $sort);
|
||||||
|
|
||||||
if (!is_array($sort_col) || count($sort_col) != 2) {
|
if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col[0], \Illuminate\Support\Facades\Schema::getColumnListing('projects'))) {
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,14 @@ class PurchaseOrderFilters extends QueryFilters
|
|||||||
->orWhere('custom_value4', 'like', '%'.$filter.'%')
|
->orWhere('custom_value4', 'like', '%'.$filter.'%')
|
||||||
->orWhereHas('vendor', function ($q) use ($filter) {
|
->orWhereHas('vendor', function ($q) use ($filter) {
|
||||||
$q->where('name', 'like', '%'.$filter.'%');
|
$q->where('name', 'like', '%'.$filter.'%');
|
||||||
});
|
})
|
||||||
|
->orWhereRaw("
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(
|
||||||
|
JSON_ARRAY(
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')),
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].product_key'))
|
||||||
|
), '$[*]')
|
||||||
|
) LIKE ?", ['%'.$filter.'%']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,4 +331,61 @@ abstract class QueryFilters
|
|||||||
->orderByRaw("{$this->with_property} = ? DESC", [$value])
|
->orderByRaw("{$this->with_property} = ? DESC", [$value])
|
||||||
->company();
|
->company();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter by date range
|
||||||
|
*
|
||||||
|
* @param string $date_range
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
public function date_range(string $date_range = ''): Builder
|
||||||
|
{
|
||||||
|
$parts = explode(",", $date_range);
|
||||||
|
|
||||||
|
if (count($parts) != 2 || !in_array('date', \Illuminate\Support\Facades\Schema::getColumnListing($this->builder->getModel()->getTable()))) {
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$start_date = Carbon::parse($parts[0]);
|
||||||
|
$end_date = Carbon::parse($parts[1]);
|
||||||
|
|
||||||
|
return $this->builder->whereBetween('date', [$start_date, $end_date]);
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter by due date range
|
||||||
|
*
|
||||||
|
* @param string $date_range
|
||||||
|
* @return Builder
|
||||||
|
*/
|
||||||
|
public function due_date_range(string $date_range = ''): Builder
|
||||||
|
{
|
||||||
|
|
||||||
|
$parts = explode(",", $date_range);
|
||||||
|
|
||||||
|
if (count($parts) != 2 || !in_array('due_date', \Illuminate\Support\Facades\Schema::getColumnListing($this->builder->getModel()->getTable()))) {
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$start_date = Carbon::parse($parts[0]);
|
||||||
|
$end_date = Carbon::parse($parts[1]);
|
||||||
|
|
||||||
|
return $this->builder->whereBetween('due_date', [$start_date, $end_date]);
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,14 @@ class QuoteFilters extends QueryFilters
|
|||||||
->orWhere('last_name', 'like', '%'.$filter.'%')
|
->orWhere('last_name', 'like', '%'.$filter.'%')
|
||||||
->orWhere('email', 'like', '%'.$filter.'%');
|
->orWhere('email', 'like', '%'.$filter.'%');
|
||||||
})
|
})
|
||||||
->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')) LIKE ?", ['%'.$filter.'%']);
|
->orWhereRaw("
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(
|
||||||
|
JSON_ARRAY(
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')),
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].product_key'))
|
||||||
|
), '$[*]')
|
||||||
|
) LIKE ?", ['%'.$filter.'%']);
|
||||||
|
// ->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')) LIKE ?", ['%'.$filter.'%']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,14 @@ class RecurringInvoiceFilters extends QueryFilters
|
|||||||
->orWhere('last_name', 'like', '%'.$filter.'%')
|
->orWhere('last_name', 'like', '%'.$filter.'%')
|
||||||
->orWhere('email', 'like', '%'.$filter.'%');
|
->orWhere('email', 'like', '%'.$filter.'%');
|
||||||
})
|
})
|
||||||
->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')) LIKE ?", ['%'.$filter.'%']);
|
->orWhereRaw("
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(
|
||||||
|
JSON_ARRAY(
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')),
|
||||||
|
JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].product_key'))
|
||||||
|
), '$[*]')
|
||||||
|
) LIKE ?", ['%'.$filter.'%']);
|
||||||
|
//->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(line_items, '$[*].notes')) LIKE ?", ['%'.$filter.'%']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +141,7 @@ class RecurringInvoiceFilters extends QueryFilters
|
|||||||
return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir);
|
return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($sort_col[0] == 'status_id'){
|
if($sort_col[0] == 'status_id') {
|
||||||
return $this->builder->orderBy('status_id', $dir)->orderBy('last_sent_date', $dir);
|
return $this->builder->orderBy('status_id', $dir)->orderBy('last_sent_date', $dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +85,10 @@ class TaskFilters extends QueryFilters
|
|||||||
$this->builder->whereNull('invoice_id');
|
$this->builder->whereNull('invoice_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (in_array('is_running', $status_parameters)) {
|
||||||
|
$this->builder->where('is_running', true);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,11 +99,12 @@ class TransactionTransformer implements BankRevenueInterface
|
|||||||
} elseif (array_key_exists('internalTransactionId', $transaction)) {
|
} elseif (array_key_exists('internalTransactionId', $transaction)) {
|
||||||
$transactionId = $transaction["internalTransactionId"];
|
$transactionId = $transaction["internalTransactionId"];
|
||||||
} else {
|
} else {
|
||||||
nlog(`Invalid Input for nordigen transaction transformer: ` . $transaction);
|
nlog('Invalid Input for nordigen transaction transformer: ' . $transaction);
|
||||||
throw new \Exception('invalid dataset: missing transactionId - Please report this error to the developer');
|
throw new \Exception('invalid dataset: missing transactionId - Please report this error to the developer');
|
||||||
}
|
}
|
||||||
|
|
||||||
$amount = (float) $transaction["transactionAmount"]["amount"];
|
$amount = (float) $transaction["transactionAmount"]["amount"];
|
||||||
|
$base_type = $amount < 0 ? 'DEBIT' : 'CREDIT';
|
||||||
|
|
||||||
// description could be in varios places
|
// description could be in varios places
|
||||||
$description = '';
|
$description = '';
|
||||||
@ -140,7 +141,7 @@ class TransactionTransformer implements BankRevenueInterface
|
|||||||
return [
|
return [
|
||||||
'transaction_id' => 0,
|
'transaction_id' => 0,
|
||||||
'nordigen_transaction_id' => $transactionId,
|
'nordigen_transaction_id' => $transactionId,
|
||||||
'amount' => $amount,
|
'amount' => abs($amount),
|
||||||
'currency_id' => $this->convertCurrency($transaction["transactionAmount"]["currency"]),
|
'currency_id' => $this->convertCurrency($transaction["transactionAmount"]["currency"]),
|
||||||
'category_id' => null,
|
'category_id' => null,
|
||||||
'category_type' => array_key_exists('additionalInformation', $transaction) ? $transaction["additionalInformation"] : '',
|
'category_type' => array_key_exists('additionalInformation', $transaction) ? $transaction["additionalInformation"] : '',
|
||||||
@ -148,7 +149,7 @@ class TransactionTransformer implements BankRevenueInterface
|
|||||||
'description' => $description,
|
'description' => $description,
|
||||||
'participant' => $participant,
|
'participant' => $participant,
|
||||||
'participant_name' => $participant_name,
|
'participant_name' => $participant_name,
|
||||||
'base_type' => $amount < 0 ? 'DEBIT' : 'CREDIT',
|
'base_type' => $base_type,
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,12 @@ class ProRata
|
|||||||
* the time interval and the frequency duration
|
* the time interval and the frequency duration
|
||||||
*
|
*
|
||||||
* @param float $amount
|
* @param float $amount
|
||||||
* @param Carbon $from_date
|
* @param \Illuminate\Support\Carbon | \Carbon\Carbon $from_date
|
||||||
* @param Carbon $to_date
|
* @param \Illuminate\Support\Carbon | \Carbon\Carbon $to_date
|
||||||
* @param int $frequency
|
* @param int $frequency
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
public function refund(float $amount, Carbon $from_date, Carbon $to_date, int $frequency): float
|
public function refund(float $amount, $from_date, $to_date, int $frequency): float
|
||||||
{
|
{
|
||||||
$days = intval(abs($from_date->copy()->diffInDays($to_date)));
|
$days = intval(abs($from_date->copy()->diffInDays($to_date)));
|
||||||
$days_in_frequency = $this->getDaysInFrequency($frequency);
|
$days_in_frequency = $this->getDaysInFrequency($frequency);
|
||||||
@ -41,12 +41,12 @@ class ProRata
|
|||||||
* the time interval and the frequency duration
|
* the time interval and the frequency duration
|
||||||
*
|
*
|
||||||
* @param float $amount
|
* @param float $amount
|
||||||
* @param Carbon $from_date
|
* @param \Illuminate\Support\Carbon | \Carbon\Carbon $from_date
|
||||||
* @param Carbon $to_date
|
* @param \Illuminate\Support\Carbon | \Carbon\Carbon $to_date
|
||||||
* @param int $frequency
|
* @param int $frequency
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
public function charge(float $amount, Carbon $from_date, Carbon $to_date, int $frequency): float
|
public function charge(float $amount, $from_date, $to_date, int $frequency): float
|
||||||
{
|
{
|
||||||
$days = intval(abs($from_date->copy()->diffInDays($to_date)));
|
$days = intval(abs($from_date->copy()->diffInDays($to_date)));
|
||||||
$days_in_frequency = $this->getDaysInFrequency($frequency);
|
$days_in_frequency = $this->getDaysInFrequency($frequency);
|
||||||
|
@ -22,5 +22,5 @@
|
|||||||
*/
|
*/
|
||||||
function ctrans(string $string, $replace = [], $locale = null): string
|
function ctrans(string $string, $replace = [], $locale = null): string
|
||||||
{
|
{
|
||||||
return trans($string, $replace, $locale);
|
return html_entity_decode(trans($string, $replace, $locale));
|
||||||
}
|
}
|
||||||
|
@ -254,17 +254,20 @@ class ActivityController extends BaseController
|
|||||||
$activity->client_id = $entity->client_id;
|
$activity->client_id = $entity->client_id;
|
||||||
$activity->project_id = $entity->project_id;
|
$activity->project_id = $entity->project_id;
|
||||||
$activity->vendor_id = $entity->vendor_id;
|
$activity->vendor_id = $entity->vendor_id;
|
||||||
|
// no break
|
||||||
case Task::class:
|
case Task::class:
|
||||||
$activity->task_id = $entity->id;
|
$activity->task_id = $entity->id;
|
||||||
$activity->expense_id = $entity->id;
|
$activity->expense_id = $entity->id;
|
||||||
$activity->client_id = $entity->client_id;
|
$activity->client_id = $entity->client_id;
|
||||||
$activity->project_id = $entity->project_id;
|
$activity->project_id = $entity->project_id;
|
||||||
$activity->vendor_id = $entity->vendor_id;
|
$activity->vendor_id = $entity->vendor_id;
|
||||||
|
// no break
|
||||||
case Payment::class:
|
case Payment::class:
|
||||||
$activity->payment_id = $entity->id;
|
$activity->payment_id = $entity->id;
|
||||||
$activity->expense_id = $entity->id;
|
$activity->expense_id = $entity->id;
|
||||||
$activity->client_id = $entity->client_id;
|
$activity->client_id = $entity->client_id;
|
||||||
$activity->project_id = $entity->project_id;
|
$activity->project_id = $entity->project_id;
|
||||||
|
// no break
|
||||||
default:
|
default:
|
||||||
# code...
|
# code...
|
||||||
break;
|
break;
|
||||||
|
@ -41,8 +41,9 @@ class ContactLoginController extends Controller
|
|||||||
$company = false;
|
$company = false;
|
||||||
$account = false;
|
$account = false;
|
||||||
|
|
||||||
if($request->query('intended'))
|
if($request->query('intended')) {
|
||||||
$request->session()->put('url.intended', $request->query('intended'));
|
$request->session()->put('url.intended', $request->query('intended'));
|
||||||
|
}
|
||||||
|
|
||||||
if ($request->session()->has('company_key')) {
|
if ($request->session()->has('company_key')) {
|
||||||
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
|
MultiDB::findAndSetDbByCompanyKey($request->session()->get('company_key'));
|
||||||
@ -142,8 +143,9 @@ class ContactLoginController extends Controller
|
|||||||
|
|
||||||
$this->setRedirectPath();
|
$this->setRedirectPath();
|
||||||
|
|
||||||
if($intended)
|
if($intended) {
|
||||||
$this->redirectTo = $intended;
|
$this->redirectTo = $intended;
|
||||||
|
}
|
||||||
|
|
||||||
return $request->wantsJson()
|
return $request->wantsJson()
|
||||||
? new JsonResponse([], 204)
|
? new JsonResponse([], 204)
|
||||||
|
@ -934,7 +934,7 @@ class BaseController extends Controller
|
|||||||
} elseif (in_array($this->entity_type, [Design::class, GroupSetting::class, PaymentTerm::class, TaskStatus::class])) {
|
} elseif (in_array($this->entity_type, [Design::class, GroupSetting::class, PaymentTerm::class, TaskStatus::class])) {
|
||||||
// nlog($this->entity_type);
|
// nlog($this->entity_type);
|
||||||
} else {
|
} else {
|
||||||
$query->where(function ($q) use ($user){ //grouping these together improves query performance significantly)
|
$query->where(function ($q) use ($user) { //grouping these together improves query performance significantly)
|
||||||
$q->where('user_id', '=', $user->id)->orWhere('assigned_user_id', $user->id);
|
$q->where('user_id', '=', $user->id)->orWhere('assigned_user_id', $user->id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -996,7 +996,7 @@ class BaseController extends Controller
|
|||||||
|
|
||||||
if(request()->has('einvoice')) {
|
if(request()->has('einvoice')) {
|
||||||
|
|
||||||
if(class_exists(Schema::class)){
|
if(class_exists(Schema::class)) {
|
||||||
$ro = new Schema();
|
$ro = new Schema();
|
||||||
$response_data['einvoice_schema'] = $ro('Peppol');
|
$response_data['einvoice_schema'] = $ro('Peppol');
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ use Illuminate\Http\Request;
|
|||||||
*/
|
*/
|
||||||
class BrevoController extends BaseController
|
class BrevoController extends BaseController
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -115,8 +115,9 @@ class InvitationController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!auth()->guard('contact')->check()){
|
if(!auth()->guard('contact')->check()) {
|
||||||
$this->middleware('auth:contact');
|
$this->middleware('auth:contact');
|
||||||
|
/** @var \App\Models\InvoiceInvitation | \App\Models\QuoteInvitation | \App\Models\CreditInvitation | \App\Models\RecurringInvoiceInvitation $invitation */
|
||||||
return redirect()->route('client.login', ['intended' => route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent])]);
|
return redirect()->route('client.login', ['intended' => route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent])]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ class InvoiceController extends Controller
|
|||||||
|
|
||||||
$invitation = $invoice->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
|
$invitation = $invoice->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
|
||||||
|
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
if ($invitation && auth()->guard('contact') && ! session()->get('is_silent') && ! $invitation->viewed_date) {
|
if ($invitation && auth()->guard('contact') && ! session()->get('is_silent') && ! $invitation->viewed_date) {
|
||||||
$invitation->markViewed();
|
$invitation->markViewed();
|
||||||
|
|
||||||
@ -77,13 +78,17 @@ class InvoiceController extends Controller
|
|||||||
'key' => $invitation ? $invitation->key : false,
|
'key' => $invitation ? $invitation->key : false,
|
||||||
'hash' => $hash,
|
'hash' => $hash,
|
||||||
'variables' => $variables,
|
'variables' => $variables,
|
||||||
|
'invoices' => [$invoice->hashed_id],
|
||||||
|
'db' => $invoice->company->db,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($request->query('mode') === 'fullscreen') {
|
if ($request->query('mode') === 'fullscreen') {
|
||||||
return render('invoices.show-fullscreen', $data);
|
return render('invoices.show-fullscreen', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('invoices.show', $data);
|
return auth()->guard('contact')->user()->client->getSetting('payment_flow') == 'default' ? $this->render('invoices.show', $data) : $this->render('invoices.show_smooth', $data);
|
||||||
|
|
||||||
|
// return $this->render('invoices.show_smooth', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showBlob($hash)
|
public function showBlob($hash)
|
||||||
@ -235,9 +240,12 @@ class InvoiceController extends Controller
|
|||||||
'hashed_ids' => $invoices->pluck('hashed_id'),
|
'hashed_ids' => $invoices->pluck('hashed_id'),
|
||||||
'total' => $total,
|
'total' => $total,
|
||||||
'variables' => $variables,
|
'variables' => $variables,
|
||||||
|
'invitation' => $invitation,
|
||||||
|
'db' => $invitation->company->db,
|
||||||
];
|
];
|
||||||
|
|
||||||
return $this->render('invoices.payment', $data);
|
// return $this->render('invoices.payment', $data);
|
||||||
|
return auth()->guard('contact')->user()->client->getSetting('payment_flow') === 'default' ? $this->render('invoices.payment', $data) : $this->render('invoices.show_smooth_multi', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,6 +88,8 @@ class NinjaPlanController extends Controller
|
|||||||
{
|
{
|
||||||
$trial_started = "Trial Started @ ".now()->format('Y-m-d H:i:s');
|
$trial_started = "Trial Started @ ".now()->format('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
auth()->guard('contact')->user()->fill($request->only(['first_name','last_name']))->save();
|
||||||
|
|
||||||
$client = auth()->guard('contact')->user()->client;
|
$client = auth()->guard('contact')->user()->client;
|
||||||
$client->private_notes = $trial_started;
|
$client->private_notes = $trial_started;
|
||||||
$client->fill($request->all());
|
$client->fill($request->all());
|
||||||
|
@ -35,11 +35,16 @@ class PrePaymentController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Show the list of payments.
|
* Show the list of payments.
|
||||||
*
|
*
|
||||||
* @return Factory|View
|
* @return Factory|View|\Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
|
||||||
$client = auth()->guard('contact')->user()->client;
|
$client = auth()->guard('contact')->user()->client;
|
||||||
|
|
||||||
|
if(!$client->getSetting('client_initiated_payments'))
|
||||||
|
return redirect()->route('client.dashboard');
|
||||||
|
|
||||||
$minimum = $client->getSetting('client_initiated_payments_minimum');
|
$minimum = $client->getSetting('client_initiated_payments_minimum');
|
||||||
$minimum_amount = $minimum == 0 ? "" : Number::formatMoney($minimum, $client);
|
$minimum_amount = $minimum == 0 ? "" : Number::formatMoney($minimum, $client);
|
||||||
|
|
||||||
|
26
app/Http/Controllers/EInvoice/SelfhostController.php
Normal file
26
app/Http/Controllers/EInvoice/SelfhostController.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\EInvoice;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\EInvoice\SignupRequest;
|
||||||
|
|
||||||
|
class SelfhostController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
public function index(SignupRequest $request)
|
||||||
|
{
|
||||||
|
return view('einvoice.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -51,9 +51,9 @@ class EmailHistoryController extends BaseController
|
|||||||
/** @var \App\Models\User $user */
|
/** @var \App\Models\User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
|
|
||||||
$data = SystemLog::where('company_id', $user->company()->id)
|
$data = SystemLog::where('company_id', $user->company()->id)
|
||||||
->where('category_id', SystemLog::CATEGORY_MAIL)
|
->where('category_id', SystemLog::CATEGORY_MAIL)
|
||||||
|
->whereJsonContains('log->history->entity', $request->entity)
|
||||||
->whereJsonContains('log->history->entity_id', $this->encodePrimaryKey($request->entity_id))
|
->whereJsonContains('log->history->entity_id', $this->encodePrimaryKey($request->entity_id))
|
||||||
->orderBy('id', 'DESC')
|
->orderBy('id', 'DESC')
|
||||||
->cursor()
|
->cursor()
|
||||||
|
@ -584,14 +584,15 @@ class ExpenseController extends BaseController
|
|||||||
return $this->itemResponse($expense->fresh());
|
return $this->itemResponse($expense->fresh());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edocument(EDocumentRequest $request): string
|
public function edocument(EDocumentRequest $request)
|
||||||
{
|
{
|
||||||
if ($request->hasFile("documents")) {
|
$user = auth()->user();
|
||||||
return (new ImportEDocument($request->file("documents")[0]->get(), $request->file("documents")[0]->getClientOriginalName()))->handle();
|
|
||||||
}
|
foreach($request->file("documents") as $file) {
|
||||||
else {
|
ImportEDocument::dispatch($file->get(), $file->getClientOriginalName(), $user->company());
|
||||||
return "No file found";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Processing....'], 200);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,9 +59,9 @@ class ExportController extends BaseController
|
|||||||
/** @var \App\Models\User $user */
|
/** @var \App\Models\User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
$hash = Str::uuid();
|
$hash = Str::uuid()->toString();
|
||||||
$url = \Illuminate\Support\Facades\URL::temporarySignedRoute('protected_download', now()->addHour(), ['hash' => $hash]);
|
$url = \Illuminate\Support\Facades\URL::temporarySignedRoute('protected_download', now()->addHour(), ['hash' => $hash]);
|
||||||
Cache::put($hash, $url, now()->addHour());
|
Cache::put($hash, $url, 3600);
|
||||||
|
|
||||||
CompanyExport::dispatch($user->getCompany(), $user, $hash);
|
CompanyExport::dispatch($user->getCompany(), $user, $hash);
|
||||||
|
|
||||||
|
@ -118,9 +118,36 @@ class ImportController extends Controller
|
|||||||
|
|
||||||
})->toArray();
|
})->toArray();
|
||||||
|
|
||||||
|
//Exact string match
|
||||||
foreach($headers as $key => $value) {
|
foreach($headers as $key => $value) {
|
||||||
|
|
||||||
|
foreach($translated_keys as $tkey => $tvalue) {
|
||||||
|
|
||||||
|
$concat_needle = str_ireplace(" ", "", $tvalue['index'].$tvalue['label']);
|
||||||
|
$concat_value = str_ireplace(" ", "", $value);
|
||||||
|
|
||||||
|
if($this->testMatch($concat_value, $concat_needle)) {
|
||||||
|
|
||||||
|
$hit = $tvalue['key'];
|
||||||
|
$hints[$key] = $hit;
|
||||||
|
unset($translated_keys[$tkey]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$hints[$key] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Label Match
|
||||||
|
foreach($headers as $key => $value) {
|
||||||
|
|
||||||
|
if(isset($hints[$key])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach($translated_keys as $tkey => $tvalue) {
|
foreach($translated_keys as $tkey => $tvalue) {
|
||||||
|
|
||||||
if($this->testMatch($value, $tvalue['label'])) {
|
if($this->testMatch($value, $tvalue['label'])) {
|
||||||
@ -134,10 +161,9 @@ class ImportController extends Controller
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//second pass using the index of the translation here
|
//Index matching pass using the index of the translation here
|
||||||
foreach($headers as $key => $value) {
|
foreach($headers as $key => $value) {
|
||||||
if(isset($hints[$key])) {
|
if(isset($hints[$key])) {
|
||||||
continue;
|
continue;
|
||||||
|
63
app/Http/Controllers/ImportQuickbooksController.php
Normal file
63
app/Http/Controllers/ImportQuickbooksController.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\Quickbooks\AuthorizedQuickbooksRequest;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use App\Http\Requests\Quickbooks\AuthQuickbooksRequest;
|
||||||
|
use App\Services\Quickbooks\QuickbooksService;
|
||||||
|
|
||||||
|
class ImportQuickbooksController extends BaseController
|
||||||
|
{
|
||||||
|
// private array $import_entities = [
|
||||||
|
// 'client' => 'Customer',
|
||||||
|
// 'invoice' => 'Invoice',
|
||||||
|
// 'product' => 'Item',
|
||||||
|
// 'payment' => 'Payment'
|
||||||
|
// ];
|
||||||
|
|
||||||
|
public function onAuthorized(AuthorizedQuickbooksRequest $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']);
|
||||||
|
$company = $request->getCompany();
|
||||||
|
$qb = new QuickbooksService($company);
|
||||||
|
|
||||||
|
$realm = $request->query('realmId');
|
||||||
|
$access_token_object = $qb->sdk()->accessTokenFromCode($request->query('code'), $realm);
|
||||||
|
$qb->sdk()->saveOAuthToken($access_token_object);
|
||||||
|
|
||||||
|
return redirect(config('ninja.react_url'));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function authorizeQuickbooks(AuthQuickbooksRequest $request, string $token)
|
||||||
|
{
|
||||||
|
|
||||||
|
MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']);
|
||||||
|
$company = $request->getCompany();
|
||||||
|
$qb = new QuickbooksService($company);
|
||||||
|
|
||||||
|
$authorizationUrl = $qb->sdk()->getAuthorizationUrl();
|
||||||
|
$state = $qb->sdk()->getState();
|
||||||
|
|
||||||
|
Cache::put($state, $token, 190);
|
||||||
|
|
||||||
|
return redirect()->to($authorizationUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -503,7 +503,7 @@ class InvoiceController extends BaseController
|
|||||||
|
|
||||||
$invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
|
$invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
|
||||||
|
|
||||||
if ($invoices->count() == 0 ) {
|
if ($invoices->count() == 0) {
|
||||||
return response()->json(['message' => 'No Invoices Found']);
|
return response()->json(['message' => 'No Invoices Found']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ use Illuminate\Http\Request;
|
|||||||
*/
|
*/
|
||||||
class MailgunWebhookController extends BaseController
|
class MailgunWebhookController extends BaseController
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -35,7 +34,7 @@ class MailgunWebhookController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(\hash_equals(\hash_hmac('sha256', $input['signature']['timestamp'] . $input['signature']['token'], config('services.mailgun.webhook_signing_key')), $input['signature']['signature'])) {
|
if(\hash_equals(\hash_hmac('sha256', $input['signature']['timestamp'] . $input['signature']['token'], config('services.mailgun.webhook_signing_key')), $input['signature']['signature'])) {
|
||||||
ProcessMailgunWebhook::dispatch($request->all())->delay(rand(2,10));
|
ProcessMailgunWebhook::dispatch($request->all())->delay(rand(2, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['message' => 'Success.'], 200);
|
return response()->json(['message' => 'Success.'], 200);
|
||||||
|
@ -22,7 +22,6 @@ use Illuminate\Support\Str;
|
|||||||
|
|
||||||
class OneTimeTokenController extends BaseController
|
class OneTimeTokenController extends BaseController
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
@ -19,7 +19,6 @@ use Illuminate\Http\Request;
|
|||||||
*/
|
*/
|
||||||
class PostMarkController extends BaseController
|
class PostMarkController extends BaseController
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -297,6 +297,8 @@ class PreviewController extends BaseController
|
|||||||
->setTemplate($design_object)
|
->setTemplate($design_object)
|
||||||
->mock();
|
->mock();
|
||||||
} catch(SyntaxError $e) {
|
} catch(SyntaxError $e) {
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
return response()->json(['message' => 'invalid data access', 'errors' => ['design.design.body' => $e->getMessage()]], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request()->query('html') == 'true') {
|
if (request()->query('html') == 'true') {
|
||||||
|
@ -181,8 +181,9 @@ class SelfUpdateController extends BaseController
|
|||||||
|
|
||||||
public function checkVersion()
|
public function checkVersion()
|
||||||
{
|
{
|
||||||
if(Ninja::isHosted())
|
if(Ninja::isHosted()) {
|
||||||
return '5.10.SaaS';
|
return '5.10.SaaS';
|
||||||
|
}
|
||||||
|
|
||||||
return trim(file_get_contents(config('ninja.version_url')));
|
return trim(file_get_contents(config('ninja.version_url')));
|
||||||
}
|
}
|
||||||
|
@ -11,23 +11,25 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\VendorPortal;
|
namespace App\Http\Controllers\VendorPortal;
|
||||||
|
|
||||||
use App\Events\Misc\InvitationWasViewed;
|
use App\Utils\Ninja;
|
||||||
use App\Events\PurchaseOrder\PurchaseOrderWasAccepted;
|
use App\Models\Webhook;
|
||||||
use App\Events\PurchaseOrder\PurchaseOrderWasViewed;
|
use Illuminate\View\View;
|
||||||
|
use App\Models\PurchaseOrder;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Utils\Traits\MakesDates;
|
||||||
|
use App\Jobs\Entity\CreateRawPdf;
|
||||||
|
use App\Jobs\Util\WebhookHandler;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\VendorPortal\PurchaseOrders\ProcessPurchaseOrdersInBulkRequest;
|
use App\Jobs\Invoice\InjectSignature;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Contracts\View\Factory;
|
||||||
|
use App\Models\PurchaseOrderInvitation;
|
||||||
|
use App\Events\Misc\InvitationWasViewed;
|
||||||
|
use App\Events\PurchaseOrder\PurchaseOrderWasViewed;
|
||||||
|
use App\Events\PurchaseOrder\PurchaseOrderWasAccepted;
|
||||||
use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrderRequest;
|
use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrderRequest;
|
||||||
use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrdersRequest;
|
use App\Http\Requests\VendorPortal\PurchaseOrders\ShowPurchaseOrdersRequest;
|
||||||
use App\Jobs\Entity\CreateRawPdf;
|
use App\Http\Requests\VendorPortal\PurchaseOrders\ProcessPurchaseOrdersInBulkRequest;
|
||||||
use App\Jobs\Invoice\InjectSignature;
|
|
||||||
use App\Models\PurchaseOrder;
|
|
||||||
use App\Models\PurchaseOrderInvitation;
|
|
||||||
use App\Utils\Ninja;
|
|
||||||
use App\Utils\Traits\MakesDates;
|
|
||||||
use App\Utils\Traits\MakesHash;
|
|
||||||
use Illuminate\Contracts\View\Factory;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Illuminate\View\View;
|
|
||||||
|
|
||||||
class PurchaseOrderController extends Controller
|
class PurchaseOrderController extends Controller
|
||||||
{
|
{
|
||||||
@ -187,6 +189,9 @@ class PurchaseOrderController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
event(new PurchaseOrderWasAccepted($purchase_order, auth()->guard('vendor')->user(), $purchase_order->company, Ninja::eventVars()));
|
event(new PurchaseOrderWasAccepted($purchase_order, auth()->guard('vendor')->user(), $purchase_order->company, Ninja::eventVars()));
|
||||||
|
|
||||||
|
WebhookHandler::dispatch(Webhook::EVENT_ACCEPTED_PURCHASE_ORDER, $purchase_order, $purchase_order->company, 'vendor')->delay(0);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($purchase_count_query->count() == 1) {
|
if ($purchase_count_query->count() == 1) {
|
||||||
|
@ -14,7 +14,7 @@ namespace App\Http\Controllers\VendorPortal;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\VendorContact;
|
use App\Models\VendorContact;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use App\Utils\TranslationHelper;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class VendorContactController extends Controller
|
class VendorContactController extends Controller
|
||||||
{
|
{
|
||||||
@ -58,14 +58,14 @@ class VendorContactController extends Controller
|
|||||||
'settings' => $vendor_contact->vendor->company->settings,
|
'settings' => $vendor_contact->vendor->company->settings,
|
||||||
'company' => $vendor_contact->vendor->company,
|
'company' => $vendor_contact->vendor->company,
|
||||||
'sidebar' => $this->sidebarMenu(),
|
'sidebar' => $this->sidebarMenu(),
|
||||||
'countries' => TranslationHelper::getCountries(),
|
'countries' => app('countries'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(VendorContact $vendor_contact)
|
public function update(Request $request, VendorContact $vendor_contact)
|
||||||
{
|
{
|
||||||
$vendor_contact->fill(request()->all());
|
$vendor_contact->fill($request->all());
|
||||||
$vendor_contact->vendor->fill(request()->all());
|
$vendor_contact->vendor->fill($request->all());
|
||||||
$vendor_contact->push();
|
$vendor_contact->push();
|
||||||
|
|
||||||
return back()->withSuccess(ctrans('texts.profile_updated_successfully'));
|
return back()->withSuccess(ctrans('texts.profile_updated_successfully'));
|
||||||
@ -76,16 +76,10 @@ class VendorContactController extends Controller
|
|||||||
$enabled_modules = auth()->guard('vendor')->user()->company->enabled_modules;
|
$enabled_modules = auth()->guard('vendor')->user()->company->enabled_modules;
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
// TODO: Enable dashboard once it's completed.
|
|
||||||
// $this->settings->enable_client_portal_dashboard
|
|
||||||
// $data[] = [ 'title' => ctrans('texts.dashboard'), 'url' => 'client.dashboard', 'icon' => 'activity'];
|
|
||||||
|
|
||||||
if (self::MODULE_PURCHASE_ORDERS & $enabled_modules) {
|
if (self::MODULE_PURCHASE_ORDERS & $enabled_modules) {
|
||||||
$data[] = ['title' => ctrans('texts.purchase_orders'), 'url' => 'vendor.purchase_orders.index', 'icon' => 'file-text'];
|
$data[] = ['title' => ctrans('texts.purchase_orders'), 'url' => 'vendor.purchase_orders.index', 'icon' => 'file-text'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// $data[] = ['title' => ctrans('texts.documents'), 'url' => 'client.documents.index', 'icon' => 'download'];
|
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ class ContactRegister
|
|||||||
// As a fallback for self-hosted, it will use default company in the system
|
// As a fallback for self-hosted, it will use default company in the system
|
||||||
// if key isn't provided in the url.
|
// if key isn't provided in the url.
|
||||||
if (! $request->route()->parameter('company_key') && Ninja::isSelfHost()) {
|
if (! $request->route()->parameter('company_key') && Ninja::isSelfHost()) {
|
||||||
$company = Account::query()->first()->default_company;
|
$company = Account::query()->first()->default_company ?? Account::query()->first()->companies->first();
|
||||||
|
|
||||||
if (! $company->client_can_register) {
|
if (! $company->client_can_register) {
|
||||||
abort(400, 'Registration disabled');
|
abort(400, 'Registration disabled');
|
||||||
|
@ -4,15 +4,15 @@ namespace App\Http\Middleware;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Cache\RateLimiter;
|
use Illuminate\Cache\RateLimiter;
|
||||||
|
use Illuminate\Contracts\Redis\Factory as Redis;
|
||||||
use Illuminate\Redis\Limiters\DurationLimiter;
|
use Illuminate\Redis\Limiters\DurationLimiter;
|
||||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
|
||||||
|
|
||||||
class ThrottleRequestsWithPredis extends ThrottleRequests
|
class ThrottleRequestsWithPredis extends \Illuminate\Routing\Middleware\ThrottleRequests
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The Redis factory implementation.
|
* The Redis factory implementation.
|
||||||
*
|
*
|
||||||
* @var \Illuminate\Redis\Connections\Connection
|
* @var \Illuminate\Contracts\Redis\Factory
|
||||||
*/
|
*/
|
||||||
protected $redis;
|
protected $redis;
|
||||||
|
|
||||||
@ -32,14 +32,14 @@ class ThrottleRequestsWithPredis extends ThrottleRequests
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new request throttler.
|
* Create a new request throttler.
|
||||||
*
|
|
||||||
* @param \Illuminate\Cache\RateLimiter $limiter
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(RateLimiter $limiter)
|
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
|
public function __construct(RateLimiter $limiter, Redis $redis)
|
||||||
{
|
{
|
||||||
parent::__construct($limiter);
|
parent::__construct($limiter);
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
$this->redis = \Illuminate\Support\Facades\Redis::connection('sentinel-cache');
|
$this->redis = \Illuminate\Support\Facades\Redis::connection('sentinel-cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ class ThrottleRequestsWithPredis extends ThrottleRequests
|
|||||||
protected function handleRequest($request, Closure $next, array $limits)
|
protected function handleRequest($request, Closure $next, array $limits)
|
||||||
{
|
{
|
||||||
foreach ($limits as $limit) {
|
foreach ($limits as $limit) {
|
||||||
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decayMinutes)) {
|
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decaySeconds)) {
|
||||||
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
|
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,16 +79,16 @@ class ThrottleRequestsWithPredis extends ThrottleRequests
|
|||||||
*
|
*
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @param int $maxAttempts
|
* @param int $maxAttempts
|
||||||
* @param int $decayMinutes
|
* @param int $decaySeconds
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
protected function tooManyAttempts($key, $maxAttempts, $decayMinutes)
|
protected function tooManyAttempts($key, $maxAttempts, $decaySeconds)
|
||||||
{
|
{
|
||||||
$limiter = new DurationLimiter(
|
$limiter = new DurationLimiter(
|
||||||
$this->redis,
|
$this->getRedisConnection(),
|
||||||
$key,
|
$key,
|
||||||
$maxAttempts,
|
$maxAttempts,
|
||||||
$decayMinutes * 60
|
$decaySeconds
|
||||||
);
|
);
|
||||||
|
|
||||||
return tap(! $limiter->acquire(), function () use ($key, $limiter) {
|
return tap(! $limiter->acquire(), function () use ($key, $limiter) {
|
||||||
@ -121,4 +121,13 @@ class ThrottleRequestsWithPredis extends ThrottleRequests
|
|||||||
{
|
{
|
||||||
return $this->decaysAt[$key] - $this->currentTime();
|
return $this->decaysAt[$key] - $this->currentTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Redis connection that should be used for throttling.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function getRedisConnection()
|
||||||
|
{
|
||||||
|
return $this->redis;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,8 +68,9 @@ class StoreNoteRequest extends Request
|
|||||||
|
|
||||||
public function getEntity()
|
public function getEntity()
|
||||||
{
|
{
|
||||||
if(!$this->entity)
|
if(!$this->entity) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$class = "\\App\\Models\\".ucfirst(Str::camel(rtrim($this->entity, 's')));
|
$class = "\\App\\Models\\".ucfirst(Str::camel(rtrim($this->entity, 's')));
|
||||||
return $class::withTrashed()->find(is_string($this->entity_id) ? $this->decodePrimaryKey($this->entity_id) : $this->entity_id);
|
return $class::withTrashed()->find(is_string($this->entity_id) ? $this->decodePrimaryKey($this->entity_id) : $this->entity_id);
|
||||||
|
@ -48,7 +48,8 @@ class StoreBankTransactionRuleRequest extends Request
|
|||||||
'rules.*.value' => 'bail|required|nullable',
|
'rules.*.value' => 'bail|required|nullable',
|
||||||
'auto_convert' => 'bail|sometimes|bool',
|
'auto_convert' => 'bail|sometimes|bool',
|
||||||
'matches_on_all' => 'bail|sometimes|bool',
|
'matches_on_all' => 'bail|sometimes|bool',
|
||||||
'applies_to' => 'bail|sometimes|string',
|
'applies_to' => 'bail|sometimes|string|in:CREDIT,DEBIT',
|
||||||
|
'on_credit_match' => 'bail|sometimes|in:create_payment,link_payment'
|
||||||
];
|
];
|
||||||
|
|
||||||
$rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.$user->company()->id.',is_deleted,0';
|
$rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.$user->company()->id.',is_deleted,0';
|
||||||
|
@ -66,8 +66,7 @@ class ShowCalculatedFieldRequest extends Request
|
|||||||
$input['end_date'] = now()->format('Y-m-d');
|
$input['end_date'] = now()->format('Y-m-d');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($input['period']) && $input['period'] == 'previous')
|
if(isset($input['period']) && $input['period'] == 'previous') {
|
||||||
{
|
|
||||||
$dates = $this->calculatePreviousPeriodStartAndEndDates($input, $user->company());
|
$dates = $this->calculatePreviousPeriodStartAndEndDates($input, $user->company());
|
||||||
$input['start_date'] = $dates[0];
|
$input['start_date'] = $dates[0];
|
||||||
$input['end_date'] = $dates[1];
|
$input['end_date'] = $dates[1];
|
||||||
|
@ -13,13 +13,11 @@ namespace App\Http\Requests\Client;
|
|||||||
|
|
||||||
use App\DataMapper\ClientSettings;
|
use App\DataMapper\ClientSettings;
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
use App\Http\ValidationRules\Client\CountryCodeExistsRule;
|
|
||||||
use App\Http\ValidationRules\Ninja\CanStoreClientsRule;
|
use App\Http\ValidationRules\Ninja\CanStoreClientsRule;
|
||||||
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
|
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\GroupSetting;
|
use App\Models\GroupSetting;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
class StoreClientRequest extends Request
|
class StoreClientRequest extends Request
|
||||||
|
@ -50,7 +50,7 @@ class StoreCompanyRequest extends Request
|
|||||||
$rules['portal_domain'] = 'sometimes|url';
|
$rules['portal_domain'] = 'sometimes|url';
|
||||||
} else {
|
} else {
|
||||||
if (Ninja::isHosted()) {
|
if (Ninja::isHosted()) {
|
||||||
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain()];
|
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9-]{1,63}$/', new ValidSubdomain()];
|
||||||
} else {
|
} else {
|
||||||
$rules['subdomain'] = 'nullable|alpha_num';
|
$rules['subdomain'] = 'nullable|alpha_num';
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ class UpdateCompanyRequest extends Request
|
|||||||
$rules['smtp_local_domain'] = 'sometimes|string|nullable';
|
$rules['smtp_local_domain'] = 'sometimes|string|nullable';
|
||||||
// $rules['smtp_verify_peer'] = 'sometimes|string';
|
// $rules['smtp_verify_peer'] = 'sometimes|string';
|
||||||
|
|
||||||
$rules['e_invoice'] = ['sometimes','nullable', new ValidCompanyScheme()];
|
$rules['e_invoice'] = ['sometimes', 'nullable', new ValidCompanyScheme()];
|
||||||
|
|
||||||
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
|
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
|
||||||
$rules['portal_domain'] = 'bail|nullable|sometimes|url';
|
$rules['portal_domain'] = 'bail|nullable|sometimes|url';
|
||||||
|
28
app/Http/Requests/EInvoice/SignupRequest.php
Normal file
28
app/Http/Requests/EInvoice/SignupRequest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Requests\EInvoice;
|
||||||
|
|
||||||
|
use App\Utils\Ninja;
|
||||||
|
use App\Http\Requests\Request;
|
||||||
|
|
||||||
|
class SignupRequest extends Request
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return Ninja::isSelfHost();
|
||||||
|
}
|
||||||
|
}
|
@ -25,9 +25,9 @@ class EDocumentRequest extends Request
|
|||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||||
$rules['documents.*'] = $this->fileValidation();
|
$rules['documents.*'] = 'required|file|max:1000000|mimes:xml';
|
||||||
} elseif ($this->file('documents')) {
|
} elseif ($this->file('documents')) {
|
||||||
$rules['documents'] = $this->fileValidation();
|
$rules['documents'] = 'required|file|max:1000000|mimes:xml';
|
||||||
}
|
}
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
@ -40,10 +40,11 @@ class BulkInvoiceRequest extends Request
|
|||||||
/** @var \App\Models\User $user */
|
/** @var \App\Models\User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
if(\Illuminate\Support\Facades\Cache::has($this->ip()."|".$this->input('action', 0)."|".json_encode($this->input('ids', ''))."|".$user->company()->company_key))
|
if(\Illuminate\Support\Facades\Cache::has($this->ip()."|".$this->input('action', 0)."|".$user->company()->company_key)) {
|
||||||
throw new DuplicatePaymentException('Duplicate request.', 429);
|
throw new DuplicatePaymentException('Duplicate request.', 429);
|
||||||
|
}
|
||||||
|
|
||||||
\Illuminate\Support\Facades\Cache::put(($this->ip()."|".$this->input('action', 0)."|".json_encode($this->input('ids', ''))."|".$user->company()->company_key), true, 1);
|
\Illuminate\Support\Facades\Cache::put(($this->ip()."|".$this->input('action', 0)."|".$user->company()->company_key), true, 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +93,12 @@ class StoreInvoiceRequest extends Request
|
|||||||
/** @var \App\Models\User $user */
|
/** @var \App\Models\User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
|
if(\Illuminate\Support\Facades\Cache::has($this->ip()."|INVOICE|".$this->input('client_id', '')."|".$user->company()->company_key)) {
|
||||||
|
usleep(200000);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Illuminate\Support\Facades\Cache::put($this->ip()."|INVOICE|".$this->input('client_id', '')."|".$user->company()->company_key,1);
|
||||||
|
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
$input = $this->decodePrimaryKeys($input);
|
$input = $this->decodePrimaryKeys($input);
|
||||||
|
@ -80,8 +80,9 @@ class StorePaymentRequest extends Request
|
|||||||
/** @var \App\Models\User $user */
|
/** @var \App\Models\User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
if(\Illuminate\Support\Facades\Cache::has($this->ip()."|".$this->input('amount', 0)."|".$this->input('client_id', '')."|".$user->company()->company_key))
|
if(\Illuminate\Support\Facades\Cache::has($this->ip()."|".$this->input('amount', 0)."|".$this->input('client_id', '')."|".$user->company()->company_key)) {
|
||||||
throw new DuplicatePaymentException('Duplicate request.', 429);
|
throw new DuplicatePaymentException('Duplicate request.', 429);
|
||||||
|
}
|
||||||
|
|
||||||
\Illuminate\Support\Facades\Cache::put(($this->ip()."|".$this->input('amount', 0)."|".$this->input('client_id', '')."|".$user->company()->company_key), true, 1);
|
\Illuminate\Support\Facades\Cache::put(($this->ip()."|".$this->input('amount', 0)."|".$this->input('client_id', '')."|".$user->company()->company_key), true, 1);
|
||||||
|
|
||||||
|
69
app/Http/Requests/Quickbooks/AuthQuickbooksRequest.php
Normal file
69
app/Http/Requests/Quickbooks/AuthQuickbooksRequest.php
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Quickbooks;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class AuthQuickbooksRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return is_array($this->getTokenContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve one-time token instance.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getTokenContent()
|
||||||
|
{
|
||||||
|
if ($this->state) {
|
||||||
|
$this->token = $this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = Cache::get($this->token);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContact(): ?User
|
||||||
|
{
|
||||||
|
return User::findOrFail($this->getTokenContent()['user_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCompany(): ?Company
|
||||||
|
{
|
||||||
|
return Company::query()->where('company_key', $this->getTokenContent()['company_key'])->firstOrFail();
|
||||||
|
}
|
||||||
|
}
|
69
app/Http/Requests/Quickbooks/AuthorizedQuickbooksRequest.php
Normal file
69
app/Http/Requests/Quickbooks/AuthorizedQuickbooksRequest.php
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Quickbooks;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class AuthorizedQuickbooksRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return is_array($this->getTokenContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'code' => 'required|string',
|
||||||
|
'state' => 'required|string',
|
||||||
|
'realmId' => 'required|string',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve one-time token instance.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getTokenContent()
|
||||||
|
{
|
||||||
|
$token = Cache::get($this->state);
|
||||||
|
|
||||||
|
$data = Cache::get($token);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContact()
|
||||||
|
{
|
||||||
|
return User::findOrFail($this->getTokenContent()['user_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCompany()
|
||||||
|
{
|
||||||
|
return Company::where('company_key', $this->getTokenContent()['company_key'])->firstOrFail();
|
||||||
|
}
|
||||||
|
}
|
@ -44,7 +44,7 @@ class StoreQuoteRequest extends Request
|
|||||||
|
|
||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
$rules['client_id'] = ['required', 'bail', Rule::exists('clients', 'id')->where('company_id', $user->company()->id)->where('is_deleted',0)];
|
$rules['client_id'] = ['required', 'bail', Rule::exists('clients', 'id')->where('company_id', $user->company()->id)->where('is_deleted', 0)];
|
||||||
|
|
||||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||||
$rules['documents.*'] = $this->fileValidation();
|
$rules['documents.*'] = $this->fileValidation();
|
||||||
|
@ -17,7 +17,6 @@ use Illuminate\Auth\Access\AuthorizationException;
|
|||||||
|
|
||||||
class GenericReportRequest extends Request
|
class GenericReportRequest extends Request
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the user is authorized to make this request.
|
* Determine if the user is authorized to make this request.
|
||||||
*
|
*
|
||||||
|
@ -133,7 +133,7 @@ class UpdateTaskRequest extends Request
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isset($input['time_log']) || empty($input['time_log']) || $input['time_log'] == '{}') {
|
if(!isset($input['time_log']) || empty($input['time_log']) || $input['time_log'] == '{}' || $input['time_log'] == '[""]') {
|
||||||
$input['time_log'] = json_encode([]);
|
$input['time_log'] = json_encode([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ use App\Http\Requests\Request;
|
|||||||
|
|
||||||
class DisconnectUserMailerRequest extends Request
|
class DisconnectUserMailerRequest extends Request
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the user is authorized to make this request.
|
* Determine if the user is authorized to make this request.
|
||||||
*
|
*
|
||||||
|
@ -77,8 +77,7 @@ class UpdateUserRequest extends Request
|
|||||||
unset($input['oauth_user_token']);
|
unset($input['oauth_user_token']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($input['password']) && is_string($input['password']))
|
if(isset($input['password']) && is_string($input['password'])) {
|
||||||
{
|
|
||||||
$input['password'] = trim($input['password']);
|
$input['password'] = trim($input['password']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Credit Ninja (https://creditninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/creditninja/creditninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Credit Ninja LLC (https://creditninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Http\ValidationRules\Client;
|
|
||||||
|
|
||||||
use App\Models\Country;
|
|
||||||
use Illuminate\Contracts\Validation\Rule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class UniqueCreditNumberRule.
|
|
||||||
*/
|
|
||||||
class CountryCodeExistsRule implements Rule
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $attribute
|
|
||||||
* @param mixed $value
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function passes($attribute, $value)
|
|
||||||
{
|
|
||||||
return $this->checkIfCodeExists($value); //if it exists, return false!
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function message()
|
|
||||||
{
|
|
||||||
return 'Country code does not exist';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function checkIfCodeExists($value): bool
|
|
||||||
{
|
|
||||||
$country = Country::where('iso_3166_2', $value)
|
|
||||||
->orWhere('iso_3166_3', $value)
|
|
||||||
->exists();
|
|
||||||
|
|
||||||
if ($country) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,36 +12,26 @@
|
|||||||
namespace App\Http\ValidationRules\Company;
|
namespace App\Http\ValidationRules\Company;
|
||||||
|
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use Illuminate\Contracts\Validation\Rule;
|
use Closure;
|
||||||
|
use Illuminate\Contracts\Validation\ValidationRule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ValidCompanyQuantity.
|
* Class ValidCompanyQuantity.
|
||||||
*/
|
*/
|
||||||
class ValidCompanyQuantity implements Rule
|
class ValidCompanyQuantity implements ValidationRule
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @param string $attribute
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||||
* @param mixed $value
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function passes($attribute, $value)
|
|
||||||
{
|
{
|
||||||
if (config('ninja.testvars.travis')) {
|
$message = ctrans('texts.company_limit_reached', ['limit' => Ninja::isSelfHost() ? 10 : auth()->user()->company()->account->hosted_company_count]);
|
||||||
return true;
|
|
||||||
|
$test = Ninja::isSelfHost() ?
|
||||||
|
auth()->user()->company()->account->companies->count() < 10 :
|
||||||
|
(auth()->user()->account->isPaid() || auth()->user()->account->isTrial()) && auth()->user()->company()->account->companies->count() < 10 ;
|
||||||
|
|
||||||
|
if (!$test) {
|
||||||
|
$fail($message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Ninja::isSelfHost()) {
|
|
||||||
return auth()->user()->company()->account->companies->count() < 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (auth()->user()->account->isPaid() || auth()->user()->account->isTrial()) && auth()->user()->company()->account->companies->count() < 10 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function message()
|
|
||||||
{
|
|
||||||
return ctrans('texts.company_limit_reached', ['limit' => Ninja::isSelfHost() ? 10 : auth()->user()->company()->account->hosted_company_count]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,31 +12,22 @@
|
|||||||
namespace App\Http\ValidationRules\Company;
|
namespace App\Http\ValidationRules\Company;
|
||||||
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use Illuminate\Contracts\Validation\Rule;
|
use Closure;
|
||||||
|
use Illuminate\Contracts\Validation\ValidationRule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ValidCompanyQuantity.
|
* Class ValidSubdomain.
|
||||||
*/
|
*/
|
||||||
class ValidSubdomain implements Rule
|
class ValidSubdomain implements ValidationRule
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
public function passes($attribute, $value)
|
if(empty($value))
|
||||||
{
|
return;
|
||||||
if (empty($value)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MultiDB::checkDomainAvailable($value);
|
if (!MultiDB::checkDomainAvailable($value)) {
|
||||||
|
$fail(ctrans('texts.subdomain_taken'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function message()
|
|
||||||
{
|
|
||||||
return ctrans('texts.subdomain_taken');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,8 @@ class TwigLint implements ValidationRule
|
|||||||
try {
|
try {
|
||||||
$twig->parse($twig->tokenize(new \Twig\Source(preg_replace('/<!--.*?-->/s', '', $value), '')));
|
$twig->parse($twig->tokenize(new \Twig\Source(preg_replace('/<!--.*?-->/s', '', $value), '')));
|
||||||
} catch (\Twig\Error\SyntaxError $e) {
|
} catch (\Twig\Error\SyntaxError $e) {
|
||||||
// echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
|
|
||||||
nlog($e->getMessage());
|
|
||||||
$fail($e->getMessage());
|
$fail($e->getMessage());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,7 @@ class ValidClientScheme implements ValidationRule, ValidatorAwareRule
|
|||||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||||
{
|
{
|
||||||
|
|
||||||
if(isset($value['Invoice']))
|
if(isset($value['Invoice'])) {
|
||||||
{
|
|
||||||
$r = new EInvoice();
|
$r = new EInvoice();
|
||||||
$errors = $r->validateRequest($value['Invoice'], ClientLevel::class);
|
$errors = $r->validateRequest($value['Invoice'], ClientLevel::class);
|
||||||
|
|
||||||
|
@ -24,7 +24,6 @@ use Illuminate\Contracts\Validation\ValidatorAwareRule;
|
|||||||
*/
|
*/
|
||||||
class ValidCompanyScheme implements ValidationRule, ValidatorAwareRule
|
class ValidCompanyScheme implements ValidationRule, ValidatorAwareRule
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The validator instance.
|
* The validator instance.
|
||||||
*
|
*
|
||||||
@ -35,8 +34,7 @@ class ValidCompanyScheme implements ValidationRule, ValidatorAwareRule
|
|||||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||||
{
|
{
|
||||||
|
|
||||||
if(isset($value['Invoice']))
|
if(isset($value['Invoice'])) {
|
||||||
{
|
|
||||||
$r = new EInvoice();
|
$r = new EInvoice();
|
||||||
$errors = $r->validateRequest($value['Invoice'], CompanyLevel::class);
|
$errors = $r->validateRequest($value['Invoice'], CompanyLevel::class);
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ class ValidInvoicesRules implements Rule
|
|||||||
} elseif (floatval($invoice['amount']) > floatval($inv->balance)) {
|
} elseif (floatval($invoice['amount']) > floatval($inv->balance)) {
|
||||||
$this->error_msg = ctrans('texts.amount_greater_than_balance_v5');
|
$this->error_msg = ctrans('texts.amount_greater_than_balance_v5');
|
||||||
return false;
|
return false;
|
||||||
} elseif($inv->is_deleted){
|
} elseif($inv->is_deleted) {
|
||||||
$this->error_msg = 'One or more invoices in this request have since been deleted';
|
$this->error_msg = 'One or more invoices in this request have since been deleted';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
namespace App\Http\ViewComposers\Components\Rotessa;
|
namespace App\Http\ViewComposers\Components\Rotessa;
|
||||||
|
|
||||||
@ -33,8 +42,9 @@ class AccountComponent extends Component
|
|||||||
"authorization_type" => 'Online'
|
"authorization_type" => 'Online'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(public array $account) {
|
public function __construct(public array $account)
|
||||||
$this->attributes = $this->newAttributeBag(Arr::only($this->account, $this->fields) );
|
{
|
||||||
|
$this->attributes = $this->newAttributeBag(Arr::only($this->account, $this->fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
namespace App\Http\ViewComposers\Components\Rotessa;
|
namespace App\Http\ViewComposers\Components\Rotessa;
|
||||||
|
|
||||||
@ -25,21 +34,25 @@ class AddressComponent extends Component
|
|||||||
'country' => 'US'
|
'country' => 'US'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(public array $address) {
|
public function __construct(public array $address)
|
||||||
if(strlen($this->address['state']) > 2 ) {
|
{
|
||||||
|
if(strlen($this->address['state']) > 2) {
|
||||||
$this->address['state'] = $this->address['country'] == 'US' ? array_search($this->address['state'], USStates::$states) : CAProvinces::getAbbreviation($this->address['state']);
|
$this->address['state'] = $this->address['country'] == 'US' ? array_search($this->address['state'], USStates::$states) : CAProvinces::getAbbreviation($this->address['state']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->attributes = $this->newAttributeBag(
|
$this->attributes = $this->newAttributeBag(
|
||||||
Arr::only(Arr::mapWithKeys($this->address, function ($item, $key) {
|
Arr::only(
|
||||||
return in_array($key, ['address1','address2','state'])?[ (['address1'=>'address_1','address2'=>'address_2','state'=>'province_code'])[$key] => $item ] :[ $key => $item ];
|
Arr::mapWithKeys($this->address, function ($item, $key) {
|
||||||
|
return in_array($key, ['address1','address2','state']) ? [ (['address1' => 'address_1','address2' => 'address_2','state' => 'province_code'])[$key] => $item ] : [ $key => $item ];
|
||||||
}),
|
}),
|
||||||
$this->fields) );
|
$this->fields
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return render('gateways.rotessa.components.address', $this->attributes->getAttributes() + $this->defaults );
|
return render('gateways.rotessa.components.address', $this->attributes->getAttributes() + $this->defaults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
namespace App\Http\ViewComposers\Components\Rotessa;
|
namespace App\Http\ViewComposers\Components\Rotessa;
|
||||||
|
|
||||||
@ -9,21 +18,20 @@ use App\Models\ClientContact;
|
|||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
|
||||||
// Contact Component
|
// Contact Component
|
||||||
class ContactComponent extends Component
|
class ContactComponent extends Component
|
||||||
{
|
{
|
||||||
|
public function __construct(ClientContact $contact)
|
||||||
public function __construct(ClientContact $contact) {
|
{
|
||||||
|
|
||||||
$contact = collect($contact->client->contacts->firstWhere('is_primary', 1)->toArray())->merge([
|
$contact = collect($contact->client->contacts->firstWhere('is_primary', 1)->toArray())->merge([
|
||||||
'home_phone' =>$contact->client->phone,
|
'home_phone' => $contact->client->phone,
|
||||||
'custom_identifier' => $contact->client->client_hash,
|
'custom_identifier' => $contact->client->client_hash,
|
||||||
'name' =>$contact->client->name,
|
'name' => $contact->client->name,
|
||||||
'id' => null,
|
'id' => null,
|
||||||
] )->all();
|
])->all();
|
||||||
|
|
||||||
$this->attributes = $this->newAttributeBag(Arr::only($contact, $this->fields) );
|
$this->attributes = $this->newAttributeBag(Arr::only($contact, $this->fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
private $fields = [
|
private $fields = [
|
||||||
@ -44,7 +52,6 @@ class ContactComponent extends Component
|
|||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
\Debugbar::debug($this->attributes->getAttributes() + $this->defaults);
|
return render('gateways.rotessa.components.contact', $this->attributes->getAttributes() + $this->defaults);
|
||||||
return render('gateways.rotessa.components.contact', $this->attributes->getAttributes() + $this->defaults );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,11 +88,11 @@ class PortalComposer
|
|||||||
$data['sidebar'] = $this->sidebarMenu();
|
$data['sidebar'] = $this->sidebarMenu();
|
||||||
$data['header'] = [];
|
$data['header'] = [];
|
||||||
$data['footer'] = [];
|
$data['footer'] = [];
|
||||||
$data['countries'] = TranslationHelper::getCountries();
|
$data['countries'] = app('countries');
|
||||||
$data['company'] = auth()->guard('contact')->user()->company;
|
$data['company'] = auth()->guard('contact')->user()->company;
|
||||||
$data['client'] = auth()->guard('contact')->user()->client;
|
$data['client'] = auth()->guard('contact')->user()->client;
|
||||||
$data['settings'] = $this->settings;
|
$data['settings'] = $this->settings;
|
||||||
$data['currencies'] = TranslationHelper::getCurrencies();
|
$data['currencies'] = app('currencies');
|
||||||
$data['contact'] = auth()->guard('contact')->user();
|
$data['contact'] = auth()->guard('contact')->user();
|
||||||
|
|
||||||
$data['multiple_contacts'] = session()->get('multiple_contacts') ?: collect();
|
$data['multiple_contacts'] = session()->get('multiple_contacts') ?: collect();
|
||||||
@ -136,11 +136,11 @@ class PortalComposer
|
|||||||
|
|
||||||
$data[] = ['title' => ctrans('texts.statement'), 'url' => 'client.statement', 'icon' => 'activity'];
|
$data[] = ['title' => ctrans('texts.statement'), 'url' => 'client.statement', 'icon' => 'activity'];
|
||||||
|
|
||||||
if (Ninja::isHosted() && auth()->guard('contact')->user()->company->id == config('ninja.ninja_default_company_id')) {
|
// if (Ninja::isHosted() && auth()->guard('contact')->user()->company->id == config('ninja.ninja_default_company_id')) {
|
||||||
$data[] = ['title' => ctrans('texts.plan'), 'url' => 'client.plan', 'icon' => 'credit-card'];
|
$data[] = ['title' => ctrans('texts.plan'), 'url' => 'client.plan', 'icon' => 'credit-card'];
|
||||||
} else {
|
// } else {
|
||||||
$data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar'];
|
$data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar'];
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (auth()->guard('contact')->user()->client->getSetting('client_initiated_payments')) {
|
if (auth()->guard('contact')->user()->client->getSetting('client_initiated_payments')) {
|
||||||
$data[] = ['title' => ctrans('texts.pre_payment'), 'url' => 'client.pre_payments.index', 'icon' => 'dollar-sign'];
|
$data[] = ['title' => ctrans('texts.pre_payment'), 'url' => 'client.pre_payments.index', 'icon' => 'dollar-sign'];
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
use App\DataProviders\CAProvinces;
|
use App\DataProviders\CAProvinces;
|
||||||
@ -9,7 +18,6 @@ View::composer(['*.rotessa.components.address','*.rotessa.components.banks.US.ba
|
|||||||
$view->with('states', $states);
|
$view->with('states', $states);
|
||||||
});
|
});
|
||||||
|
|
||||||
// CAProvinces View Composer
|
|
||||||
View::composer(['*.rotessa.components.address','*.rotessa.components.banks.CA.bank','*.rotessa.components.dropdowns.country.CA'], function ($view) {
|
View::composer(['*.rotessa.components.address','*.rotessa.components.banks.CA.bank','*.rotessa.components.dropdowns.country.CA'], function ($view) {
|
||||||
$provinces = CAProvinces::get();
|
$provinces = CAProvinces::get();
|
||||||
$view->with('provinces', $provinces);
|
$view->with('provinces', $provinces);
|
||||||
|
@ -244,8 +244,7 @@ class Wave extends BaseImport implements ImportInterface
|
|||||||
if (empty($expense_data['vendor_id'])) {
|
if (empty($expense_data['vendor_id'])) {
|
||||||
$vendor_data['user_id'] = $this->getUserIDForRecord($expense_data);
|
$vendor_data['user_id'] = $this->getUserIDForRecord($expense_data);
|
||||||
|
|
||||||
if(isset($raw_expense['Vendor Name']) || isset($raw_expense['Vendor']))
|
if(isset($raw_expense['Vendor Name']) || isset($raw_expense['Vendor'])) {
|
||||||
{
|
|
||||||
$vendor_repository->save(
|
$vendor_repository->save(
|
||||||
['name' => isset($raw_expense['Vendor Name']) ? $raw_expense['Vendor Name'] : isset($raw_expense['Vendor'])],
|
['name' => isset($raw_expense['Vendor Name']) ? $raw_expense['Vendor Name'] : isset($raw_expense['Vendor'])],
|
||||||
$vendor = VendorFactory::create(
|
$vendor = VendorFactory::create(
|
||||||
|
@ -119,8 +119,9 @@ class BaseTransformer
|
|||||||
{
|
{
|
||||||
$code = array_key_exists($key, $data) ? $data[$key] : false;
|
$code = array_key_exists($key, $data) ? $data[$key] : false;
|
||||||
|
|
||||||
if(!$code)
|
if(!$code) {
|
||||||
return $this->company->settings->currency_id;
|
return $this->company->settings->currency_id;
|
||||||
|
}
|
||||||
|
|
||||||
/** @var \Illuminate\Support\Collection<\App\Models\Currency> */
|
/** @var \Illuminate\Support\Collection<\App\Models\Currency> */
|
||||||
$currencies = app('currencies');
|
$currencies = app('currencies');
|
||||||
|
@ -38,12 +38,13 @@ class ExpenseTransformer extends BaseTransformer
|
|||||||
|
|
||||||
$tax_rate = $total_tax > 0 ? round(($total_tax / $amount) * 100, 3) : 0;
|
$tax_rate = $total_tax > 0 ? round(($total_tax / $amount) * 100, 3) : 0;
|
||||||
|
|
||||||
if(isset($data['Notes / Memo']) && strlen($data['Notes / Memo']) > 1)
|
if(isset($data['Notes / Memo']) && strlen($data['Notes / Memo']) > 1) {
|
||||||
$public_notes = $data['Notes / Memo'];
|
$public_notes = $data['Notes / Memo'];
|
||||||
elseif (isset($data['Transaction Description']) && strlen($data['Transaction Description']) > 1)
|
} elseif (isset($data['Transaction Description']) && strlen($data['Transaction Description']) > 1) {
|
||||||
$public_notes = $data['Transaction Description'];
|
$public_notes = $data['Transaction Description'];
|
||||||
else
|
} else {
|
||||||
$public_notes = '';
|
$public_notes = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$transformed = [
|
$transformed = [
|
||||||
|
@ -237,7 +237,8 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
|
|
||||||
$amount = $this->bt->amount;
|
$amount = $this->bt->amount;
|
||||||
|
|
||||||
if ($_invoices->count() >0 && $this->checkPayable($_invoices)) {
|
if ($_invoices->count() > 0 && $this->checkPayable($_invoices)) {
|
||||||
|
|
||||||
$this->createPayment($_invoices, $amount);
|
$this->createPayment($_invoices, $amount);
|
||||||
|
|
||||||
$this->bts->push($this->bt->id);
|
$this->bts->push($this->bt->id);
|
||||||
@ -293,6 +294,8 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
$this->attachable_invoices = [];
|
$this->attachable_invoices = [];
|
||||||
$this->available_balance = $amount;
|
$this->available_balance = $amount;
|
||||||
|
|
||||||
|
nlog($invoices->count());
|
||||||
|
|
||||||
\DB::connection(config('database.default'))->transaction(function () use ($invoices) {
|
\DB::connection(config('database.default'))->transaction(function () use ($invoices) {
|
||||||
$invoices->each(function ($invoice) {
|
$invoices->each(function ($invoice) {
|
||||||
$this->invoice = Invoice::withTrashed()->where('id', $invoice->id)->lockForUpdate()->first();
|
$this->invoice = Invoice::withTrashed()->where('id', $invoice->id)->lockForUpdate()->first();
|
||||||
@ -326,11 +329,16 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
});
|
});
|
||||||
}, 2);
|
}, 2);
|
||||||
|
|
||||||
|
nlog("pre");
|
||||||
|
|
||||||
// @phpstan-ignore-next-line
|
// @phpstan-ignore-next-line
|
||||||
if (!$this->invoice) {
|
if (!$this->invoice) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nlog("post");
|
||||||
|
|
||||||
/* Create Payment */
|
/* Create Payment */
|
||||||
$payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
$payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||||
|
|
||||||
@ -387,7 +395,7 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
|
|
||||||
$hashed_keys = [];
|
$hashed_keys = [];
|
||||||
|
|
||||||
foreach($this->attachable_invoices as $attachable_invoice){ //@phpstan-ignore-line
|
foreach($this->attachable_invoices as $attachable_invoice) { //@phpstan-ignore-line
|
||||||
$hashed_keys[] = $this->encodePrimaryKey($attachable_invoice['id']);
|
$hashed_keys[] = $this->encodePrimaryKey($attachable_invoice['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,6 +403,9 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
$this->bt->status_id = BankTransaction::STATUS_CONVERTED;
|
$this->bt->status_id = BankTransaction::STATUS_CONVERTED;
|
||||||
$this->bt->payment_id = $payment->id;
|
$this->bt->payment_id = $payment->id;
|
||||||
$this->bt->save();
|
$this->bt->save();
|
||||||
|
|
||||||
|
nlog($this->bt->toArray());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function resolveCategory($input): ?int
|
private function resolveCategory($input): ?int
|
||||||
|
@ -695,7 +695,7 @@ class CompanyExport implements ShouldQueue
|
|||||||
|
|
||||||
$url = Cache::get($this->hash);
|
$url = Cache::get($this->hash);
|
||||||
|
|
||||||
Cache::put($this->hash, $storage_path, now()->addHour());
|
Cache::put($this->hash, $storage_path, 3600);
|
||||||
|
|
||||||
App::forgetInstance('translator');
|
App::forgetInstance('translator');
|
||||||
$t = app('translator');
|
$t = app('translator');
|
||||||
|
@ -114,8 +114,7 @@ class RecurringExpensesCron
|
|||||||
$exchange_rate = new CurrencyApi();
|
$exchange_rate = new CurrencyApi();
|
||||||
|
|
||||||
$expense->exchange_rate = $exchange_rate->exchangeRate($expense->currency_id, (int)$expense->company->settings->currency_id, Carbon::parse($expense->date));
|
$expense->exchange_rate = $exchange_rate->exchangeRate($expense->currency_id, (int)$expense->company->settings->currency_id, Carbon::parse($expense->date));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$expense->exchange_rate = 1;
|
$expense->exchange_rate = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,12 +48,12 @@ class RecurringInvoicesCron
|
|||||||
Auth::logout();
|
Auth::logout();
|
||||||
|
|
||||||
if (! config('ninja.db.multi_db_enabled')) {
|
if (! config('ninja.db.multi_db_enabled')) {
|
||||||
$recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
|
$recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||||
->where('recurring_invoices.is_deleted', false)
|
->where('is_deleted', false)
|
||||||
->where('recurring_invoices.remaining_cycles', '!=', '0')
|
->where('remaining_cycles', '!=', '0')
|
||||||
->whereNotNull('recurring_invoices.next_send_date')
|
->whereNotNull('next_send_date')
|
||||||
->whereNull('recurring_invoices.deleted_at')
|
->whereNull('deleted_at')
|
||||||
->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
|
->where('next_send_date', '<=', now()->toDateTimeString())
|
||||||
->whereHas('client', function ($query) {
|
->whereHas('client', function ($query) {
|
||||||
$query->where('is_deleted', 0)
|
$query->where('is_deleted', 0)
|
||||||
->where('deleted_at', null);
|
->where('deleted_at', null);
|
||||||
@ -87,27 +87,18 @@ class RecurringInvoicesCron
|
|||||||
foreach (MultiDB::$dbs as $db) {
|
foreach (MultiDB::$dbs as $db) {
|
||||||
MultiDB::setDB($db);
|
MultiDB::setDB($db);
|
||||||
|
|
||||||
$recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
|
$recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||||
->where('recurring_invoices.is_deleted', false)
|
->where('is_deleted', false)
|
||||||
->where('recurring_invoices.remaining_cycles', '!=', '0')
|
->where('remaining_cycles', '!=', '0')
|
||||||
->whereNull('recurring_invoices.deleted_at')
|
->whereNull('deleted_at')
|
||||||
->whereNotNull('recurring_invoices.next_send_date')
|
->whereNotNull('next_send_date')
|
||||||
->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
|
->where('next_send_date', '<=', now()->toDateTimeString())
|
||||||
// ->whereHas('client', function ($query) {
|
->whereHas('client', function ($query) {
|
||||||
// $query->where('is_deleted', 0)
|
$query->where('is_deleted', 0)
|
||||||
// ->where('deleted_at', null);
|
->where('deleted_at', null);
|
||||||
// })
|
|
||||||
// ->whereHas('company', function ($query) {
|
|
||||||
// $query->where('is_disabled', 0);
|
|
||||||
// })
|
|
||||||
->leftJoin('clients', function ($join) {
|
|
||||||
$join->on('recurring_invoices.client_id', '=', 'clients.id')
|
|
||||||
->where('clients.is_deleted', 0)
|
|
||||||
->whereNull('clients.deleted_at');
|
|
||||||
})
|
})
|
||||||
->leftJoin('companies', function ($join) {
|
->whereHas('company', function ($query) {
|
||||||
$join->on('recurring_invoices.company_id', '=', 'companies.id')
|
$query->where('is_disabled', 0);
|
||||||
->where('companies.is_disabled', 0);
|
|
||||||
})
|
})
|
||||||
->with('company')
|
->with('company')
|
||||||
->cursor();
|
->cursor();
|
||||||
|
@ -70,7 +70,7 @@ class CreateEDocument implements ShouldQueue
|
|||||||
if ($this->document instanceof Invoice) {
|
if ($this->document instanceof Invoice) {
|
||||||
switch ($e_document_type) {
|
switch ($e_document_type) {
|
||||||
case "PEPPOL":
|
case "PEPPOL":
|
||||||
return (new Peppol($this->document))->toXml();
|
return (new Peppol($this->document))->run()->toXml();
|
||||||
case "FACT1":
|
case "FACT1":
|
||||||
return (new RoEInvoice($this->document))->generateXml();
|
return (new RoEInvoice($this->document))->generateXml();
|
||||||
case "FatturaPA":
|
case "FatturaPA":
|
||||||
|
@ -11,14 +11,16 @@
|
|||||||
|
|
||||||
namespace App\Jobs\EDocument;
|
namespace App\Jobs\EDocument;
|
||||||
|
|
||||||
use App\Models\Expense;
|
|
||||||
use App\Services\EDocument\Imports\ZugferdEDocument;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Expense;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
|
use App\Services\EDocument\Imports\ZugferdEDocument;
|
||||||
|
|
||||||
class ImportEDocument implements ShouldQueue
|
class ImportEDocument implements ShouldQueue
|
||||||
{
|
{
|
||||||
@ -27,14 +29,10 @@ class ImportEDocument implements ShouldQueue
|
|||||||
use Queueable;
|
use Queueable;
|
||||||
use SerializesModels;
|
use SerializesModels;
|
||||||
|
|
||||||
public $deleteWhenMissingModels = true;
|
public $tries = 1;
|
||||||
private string $file_name;
|
|
||||||
private readonly string $file_content;
|
|
||||||
|
|
||||||
public function __construct(string $file_content, string $file_name)
|
public function __construct(private readonly string $file_content, private string $file_name, private Company $company)
|
||||||
{
|
{
|
||||||
$this->file_content = $file_content;
|
|
||||||
$this->file_name = $file_name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,20 +43,30 @@ class ImportEDocument implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
public function handle(): Expense
|
public function handle(): Expense
|
||||||
{
|
{
|
||||||
if (str_contains($this->file_name, ".xml")){
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case stristr($this->file_content, "urn:cen.eu:en16931:2017"):
|
case stristr($this->file_content, "urn:cen.eu:en16931:2017"):
|
||||||
case stristr($this->file_content, "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0"):
|
case stristr($this->file_content, "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0"):
|
||||||
case stristr($this->file_content, "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_2.1"):
|
case stristr($this->file_content, "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_2.1"):
|
||||||
case stristr($this->file_content, "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_2.0"):
|
case stristr($this->file_content, "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_2.0"):
|
||||||
return (new ZugferdEDocument($this->file_content, $this->file_name))->run();
|
return (new ZugferdEDocument($this->file_content, $this->file_name, $this->company))->run();
|
||||||
default:
|
default:
|
||||||
throw new Exception("E-Invoice standard not supported");
|
throw new Exception("E-Invoice standard not supported");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Exception("File type not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function middleware()
|
||||||
|
{
|
||||||
|
return [new WithoutOverlapping($this->company->company_key)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function failed($exception = null)
|
||||||
|
{
|
||||||
|
if ($exception) {
|
||||||
|
nlog("EXCEPTION:: ImportEDocument:: ".$exception->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
config(['queue.failed.driver' => null]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
use MakesHash;
|
use MakesHash;
|
||||||
|
|
||||||
public $tries = 4; //number of retries
|
public $tries = 4; //number of retries
|
||||||
|
|
||||||
public $deleteWhenMissingModels = true;
|
public $deleteWhenMissingModels = true;
|
||||||
|
|
||||||
/** @var null|\App\Models\Company $company **/
|
/** @var null|\App\Models\Company $company **/
|
||||||
@ -254,7 +255,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
private function incrementEmailCounter(): void
|
private function incrementEmailCounter(): void
|
||||||
{
|
{
|
||||||
if(in_array($this->mailer, ['default','mailgun','postmark'])) {
|
if(in_array($this->nmo->settings->email_sending_method, ['default','mailgun','postmark'])) {
|
||||||
Cache::increment("email_quota".$this->company->account->key);
|
Cache::increment("email_quota".$this->company->account->key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +299,7 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
|
|
||||||
/** Force free/trials onto specific mail driver */
|
/** Force free/trials onto specific mail driver */
|
||||||
|
|
||||||
if($this->mailer == 'default' && $this->company->account->isNewHostedAccount()) {
|
if($this->nmo->settings->email_sending_method == 'default' && $this->company->account->isNewHostedAccount()) {
|
||||||
$this->mailer = 'mailgun';
|
$this->mailer = 'mailgun';
|
||||||
$this->setHostedMailgunMailer();
|
$this->setHostedMailgunMailer();
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -84,7 +84,7 @@ class PaymentFailedMailer implements ShouldQueue
|
|||||||
$invoice = false;
|
$invoice = false;
|
||||||
|
|
||||||
if ($this->payment_hash) {
|
if ($this->payment_hash) {
|
||||||
// $amount = array_sum(array_column($this->payment_hash->invoices(), 'amount')) + $this->payment_hash->fee_total;
|
|
||||||
$amount = $this->payment_hash?->amount_with_fee() ?: 0;
|
$amount = $this->payment_hash?->amount_with_fee() ?: 0;
|
||||||
$invoice = Invoice::query()->whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
|
$invoice = Invoice::query()->whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
|
||||||
}
|
}
|
||||||
|
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