Merge branch 'v5-develop' into preview

This commit is contained in:
David Bomba 2023-06-10 09:56:50 +10:00
commit a4ae273bed
272 changed files with 349617 additions and 544818 deletions

View File

@ -1 +1 @@
5.5.122
5.6.1

View File

@ -127,7 +127,8 @@ class CheckData extends Command
$this->checkClientSettings();
$this->checkCompanyTokens();
$this->checkUserState();
$this->checkContactEmailAndSendEmailStatus();
if (Ninja::isHosted()) {
$this->checkAccountStatuses();
$this->checkNinjaPortalUrls();
@ -434,11 +435,38 @@ class CheckData extends Command
private function checkEntityInvitations()
{
RecurringInvoiceInvitation::where('deleted_at', "0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
InvoiceInvitation::where('deleted_at', "0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
QuoteInvitation::where('deleted_at', "0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
CreditInvitation::where('deleted_at', "0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
InvoiceInvitation::where('sent_date', '0000-00-00 00:00:00')->cursor()->each(function ($ii){
$ii->sent_date = null;
$ii->saveQuietly();
});
InvoiceInvitation::where('viewed_date', '0000-00-00 00:00:00')->cursor()->each(function ($ii) {
$ii->viewed_date = null;
$ii->saveQuietly();
});
QuoteInvitation::where('sent_date', '0000-00-00 00:00:00')->cursor()->each(function ($ii) {
$ii->sent_date = null;
$ii->saveQuietly();
});
QuoteInvitation::where('viewed_date', '0000-00-00 00:00:00')->cursor()->each(function ($ii) {
$ii->viewed_date = null;
$ii->saveQuietly();
});
CreditInvitation::where('sent_date', '0000-00-00 00:00:00')->cursor()->each(function ($ii) {
$ii->sent_date = null;
$ii->saveQuietly();
});
CreditInvitation::where('viewed_date', '0000-00-00 00:00:00')->cursor()->each(function ($ii) {
$ii->viewed_date = null;
$ii->saveQuietly();
});
collect([Invoice::class, Quote::class, Credit::class, PurchaseOrder::class])->each(function ($entity) {
if ($entity::doesntHave('invitations')->count() > 0) {
@ -1114,4 +1142,23 @@ class CheckData extends Command
});
}
public function checkContactEmailAndSendEmailStatus()
{
$q = ClientContact::whereNull('email')
->where('send_email', true);
$this->logMessage($q->count() . " Contacts with Send Email = true but no email address");
if ($this->option('fix') == 'true') {
$q->cursor()->each(function ($c){
$c->send_email = false;
$c->saveQuietly();
$this->logMessage("Fixing - {$c->id}");
});
}
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\AT;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'USt';
}

View File

@ -0,0 +1,258 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\AU;
use App\DataMapper\Tax\BaseRule;
use App\DataMapper\Tax\RuleInterface;
use App\Models\Product;
class Rule extends BaseRule implements RuleInterface
{
/** @var string $seller_region */
public string $seller_region = 'AU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
/**
* Initializes the rules and builds any required data.
*
* @return self
*/
public function init(): self
{
$this->calculateRates();
return $this;
}
/**
* Sets the correct tax rate based on the product type.
*
* @param mixed $item
* @return self
*/
public function taxByType($item): self
{
if ($this->client->is_tax_exempt) {
return $this->taxExempt($item);
}
match(intval($item->tax_id)) {
Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt($item),
Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital($item),
Product::PRODUCT_TYPE_SERVICE => $this->taxService($item),
Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping($item),
Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical($item),
Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced($item),
Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override($item),
Product::PRODUCT_TYPE_ZERO_RATED => $this->zeroRated($item),
Product::PRODUCT_TYPE_REVERSE_TAX => $this->reverseTax($item),
default => $this->default($item),
};
return $this;
}
/**
* Calculates the tax rate for a reduced tax product
*
* @return self
*/
public function reverseTax($item): self
{
$this->tax_rate1 = 10;
$this->tax_name1 = 'GST';
return $this;
}
/**
* Calculates the tax rate for a reduced tax product
*
* @return self
*/
public function taxReduced($item): self
{
$this->tax_rate1 = $this->reduced_tax_rate;
$this->tax_name1 = 'GST';
return $this;
}
/**
* Calculates the tax rate for a zero rated tax product
*
* @return self
*/
public function zeroRated($item): self
{
$this->tax_rate1 = 0;
$this->tax_name1 = 'GST';
return $this;
}
/**
* Calculates the tax rate for a tax exempt product
*
* @return self
*/
public function taxExempt($item): self
{
$this->tax_name1 = '';
$this->tax_rate1 = 0;
return $this;
}
/**
* Calculates the tax rate for a digital product
*
* @return self
*/
public function taxDigital($item): self
{
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'GST';
return $this;
}
/**
* Calculates the tax rate for a service product
*
* @return self
*/
public function taxService($item): self
{
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'GST';
return $this;
}
/**
* Calculates the tax rate for a shipping product
*
* @return self
*/
public function taxShipping($item): self
{
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'GST';
return $this;
}
/**
* Calculates the tax rate for a physical product
*
* @return self
*/
public function taxPhysical($item): self
{
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'GST';
return $this;
}
/**
* Calculates the tax rate for a default product
*
* @return self
*/
public function default($item): self
{
$this->tax_name1 = '';
$this->tax_rate1 = 0;
return $this;
}
/**
* Calculates the tax rate for an override product
*
* @return self
*/
public function override($item): self
{
return $this;
}
/**
* Calculates the tax rates based on the client's location.
*
* @return self
*/
public function calculateRates(): self
{
if ($this->client->is_tax_exempt) {
// nlog("tax exempt");
$this->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->has_valid_vat_number && $this->eu_business_tax_exempt) {
// nlog("euro zone and tax exempt");
// $this->tax_rate = 0;
// $this->reduced_tax_rate = 0;
// } elseif(!in_array($this->client_subregion, $this->eu_country_codes) && ($this->foreign_consumer_tax_exempt || $this->foreign_business_tax_exempt)) { //foreign + tax exempt
// nlog("foreign and tax exempt");
// $this->tax_rate = 0;
// $this->reduced_tax_rate = 0;
// } elseif(!in_array($this->client_subregion, $this->eu_country_codes)) {
// $this->defaultForeign();
// } elseif(in_array($this->client_subregion, $this->eu_country_codes) && !$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) {
// 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;
// $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_tax_rate;
// } else {
// nlog("EU with intra-community supply ie DE to DE");
// $this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate;
// $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate;
// }
} else {
$this->tax_rate = $this->client->company->tax_data->regions->AU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate;
$this->reduced_tax_rate = $this->client->company->tax_data->regions->AU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate;
}
return $this;
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\BE;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'BTW';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\BG;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'НДС';
}

View File

@ -16,7 +16,6 @@ use App\Models\Invoice;
use App\Models\Product;
use App\DataProviders\USStates;
use App\DataMapper\Tax\ZipTax\Response;
use App\Services\Tax\Providers\TaxProvider;
class BaseRule implements RuleInterface
{
@ -203,18 +202,9 @@ class BaseRule implements RuleInterface
$tax_data = $company->origin_tax_data;
}
else{
/** Ensures the client tax data has been updated */
// if(!$this->client->tax_data && \DB::transactionLevel() == 0) {
// $tp = new TaxProvider($company, $this->client);
// $tp->updateClientTaxData();
// $this->client->fresh();
// }
if($this->client->tax_data)
$tax_data = $this->client->tax_data;
elseif($this->client->tax_data){
$tax_data = $this->client->tax_data;
}
@ -273,7 +263,8 @@ class BaseRule implements RuleInterface
public function isTaxableRegion(): bool
{
return $this->client->company->tax_data->regions->{$this->client_region}->tax_all_subregions || $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->apply_tax;
return $this->client->company->tax_data->regions->{$this->client_region}->tax_all_subregions ||
(property_exists($this->client->company->tax_data->regions->{$this->client_region}->subregions, $this->client_subregion) && $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->apply_tax);
}
public function defaultForeign(): self

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\CY;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'ΦΠΑ';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\CZ;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'DPH';
}

View File

@ -41,6 +41,7 @@ class Rule extends BaseRule implements RuleInterface
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'MwSt.';
/**
* Initializes the rules and builds any required data.
*
@ -90,7 +91,6 @@ class Rule extends BaseRule implements RuleInterface
public function reverseTax($item): self
{
$this->tax_rate1 = 0;
$this->tax_name1 = 'ermäßigte MwSt.';
return $this;
}
@ -103,8 +103,7 @@ class Rule extends BaseRule implements RuleInterface
public function taxReduced($item): self
{
$this->tax_rate1 = $this->reduced_tax_rate;
$this->tax_name1 = 'ermäßigte MwSt.';
return $this;
}
@ -116,8 +115,7 @@ class Rule extends BaseRule implements RuleInterface
public function zeroRated($item): self
{
$this->tax_rate1 = 0;
$this->tax_name1 = 'ermäßigte MwSt.';
return $this;
}
@ -144,8 +142,7 @@ class Rule extends BaseRule implements RuleInterface
{
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this;
}
@ -158,8 +155,7 @@ class Rule extends BaseRule implements RuleInterface
{
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this;
}
@ -172,8 +168,7 @@ class Rule extends BaseRule implements RuleInterface
{
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this;
}
@ -186,8 +181,7 @@ class Rule extends BaseRule implements RuleInterface
{
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this;
}
@ -223,19 +217,20 @@ class Rule extends BaseRule implements RuleInterface
public function calculateRates(): self
{
if ($this->client->is_tax_exempt) {
nlog("tax exempt");
// nlog("tax exempt");
$this->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->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->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->reduced_tax_rate = 0;
}
elseif(!in_array($this->client_subregion, $this->eu_country_codes) && ($this->foreign_consumer_tax_exempt || $this->foreign_business_tax_exempt)) //foreign + tax exempt
{
nlog("foreign and tax exempt");
// nlog("foreign and tax exempt");
$this->tax_rate = 0;
$this->reduced_tax_rate = 0;
}
@ -243,22 +238,22 @@ class Rule extends BaseRule implements RuleInterface
{
$this->defaultForeign();
}
elseif(in_array($this->client_subregion, $this->eu_country_codes) && !$this->client->has_valid_vat_number) //eu country / no valid vat
elseif(in_array($this->client_subregion, $this->eu_country_codes) && !$this->client->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)
{
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;
$this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_tax_rate;
}
else {
nlog("EU with intra-community supply ie DE to DE");
// nlog("EU with intra-community supply ie DE to DE");
$this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate;
$this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate;
}
}
else {
nlog("default tax");
// nlog("default tax");
$this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate;
$this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate;
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\DK;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'Moms';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\EE;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'KM';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\ES;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'IVA';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\FI;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'ALV';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\FR;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'TVA';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\GR;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'IVA';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\HR;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'PDV';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\HU;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'ÁFA';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\IE;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'VAT';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\IT;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'IVA';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\LT;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'PVM';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\LU;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'TVA';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\LV;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'PVN';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\MT;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'VAT';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\NL;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'BTW';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\PT;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'IVA';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\RO;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'TVA';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\SE;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'Moms';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\SI;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'DDV';
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Tax\SK;
use App\DataMapper\Tax\DE\Rule as DERule;
class Rule extends DERule
{
/** @var string $seller_region */
public string $seller_region = 'EU';
/** @var bool $consumer_tax_exempt */
public bool $consumer_tax_exempt = false;
/** @var bool $business_tax_exempt */
public bool $business_tax_exempt = false;
/** @var bool $eu_business_tax_exempt */
public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */
public float $tax_rate = 0;
/** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0;
public string $tax_name1 = 'DPH';
}

View File

@ -344,9 +344,9 @@ class TaxModel
$this->regions->EU->subregions = new \stdClass();
$this->regions->EU->subregions->AT = new \stdClass();
$this->regions->EU->subregions->AT->tax_rate = 21;
$this->regions->EU->subregions->AT->tax_rate = 20;
$this->regions->EU->subregions->AT->tax_name = 'USt';
$this->regions->EU->subregions->AT->reduced_tax_rate = 11;
$this->regions->EU->subregions->AT->reduced_tax_rate = 10;
$this->regions->EU->subregions->AT->apply_tax = false;
$this->regions->EU->subregions->BE = new \stdClass();

View File

@ -164,7 +164,6 @@ class Rule extends BaseRule implements RuleInterface
$this->tax_rate1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_rate;
$this->tax_name1 = "Sales Tax";
// $this->tax_name1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name;
return $this;
}
@ -220,4 +219,4 @@ class Rule extends BaseRule implements RuleInterface
return $this;
}
}
}

View File

@ -67,8 +67,8 @@ class Response
public float $taxSales = 0;
public string $taxName = "";
public float $taxUse = 0;
public string $txbService = ""; // N = No, Y = Yes
public string $txbFreight = ""; // N = No, Y = Yes
public string $txbService = "Y"; // N = No, Y = Yes
public string $txbFreight = "Y"; // N = No, Y = Yes
public float $stateSalesTax = 0;
public float $stateUseTax = 0;
public float $citySalesTax = 0;
@ -98,7 +98,7 @@ class Response
public float $district5UseTax = 0;
/* US SPECIFIC TAX CODES */
public string $originDestination = ""; // defines if the client origin is the locale where the tax is remitted to
public string $originDestination = "D"; // defines if the client origin is the locale where the tax is remitted to
public function __construct($data = null)
{

View File

@ -104,7 +104,8 @@ class Handler extends ExceptionHandler
if (Ninja::isHosted()) {
if($exception instanceof ThrottleRequestsException && class_exists(\Modules\Admin\Events\ThrottledExceptionRaised::class)) {
event(new \Modules\Admin\Events\ThrottledExceptionRaised(auth()->user()->account->key));
$uri = urldecode(request()->getRequestUri());
event(new \Modules\Admin\Events\ThrottledExceptionRaised(auth()->user()?->account?->key, $uri, request()->ip()));
}
Integration::configureScope(function (Scope $scope): void {

View File

@ -106,6 +106,16 @@ class ClientFilters extends QueryFilters
return $this->builder->where('number', $number);
}
public function group(string $group_id = ''): Builder
{
if (strlen($group_id) == 0) {
return $this->builder;
}
return $this->builder->where('group_settings_id', $this->decodePrimaryKey($group_id));
}
/**
* Filter based on search text.
*

View File

@ -102,8 +102,13 @@ class PaymentFilters extends QueryFilters
if (count($payment_filters) >0) {
$query->whereIn('status_id', $payment_filters);
}
if(in_array('partially_unapplied', $status_parameters)) {
$query->where('amount', '>', 'applied')->where('refunded', 0);
}
});
return $this->builder;
}

View File

@ -138,6 +138,7 @@ abstract class QueryFilters
*/
public function status(string $filter = ''): Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
@ -146,17 +147,17 @@ abstract class QueryFilters
return $this->builder->where(function ($query) use ($filters) {
if (in_array(self::STATUS_ACTIVE, $filters)) {
$query->orWhereNull('deleted_at');
$query = $query->orWhereNull('deleted_at');
}
if (in_array(self::STATUS_ARCHIVED, $filters)) {
$query->orWhere(function ($query) {
$query->whereNotNull('deleted_at')->where('is_deleted', 0);
$query = $query->orWhere(function ($q) {
$q->whereNotNull('deleted_at')->where('is_deleted', 0);
});
}
if (in_array(self::STATUS_DELETED, $filters)) {
$query->orWhere('is_deleted', 1);
$query = $query->orWhere('is_deleted', 1);
}
});
}

View File

@ -60,33 +60,33 @@ class InvoiceItemSum
];
private array $tax_jurisdictions = [
// 'AT', // Austria
// 'BE', // Belgium
// 'BG', // Bulgaria
// 'CY', // Cyprus
// 'CZ', // Czech Republic
'AT', // Austria
'BE', // Belgium
'BG', // Bulgaria
'CY', // Cyprus
'CZ', // Czech Republic
'DE', // Germany
// 'DK', // Denmark
// 'EE', // Estonia
// 'ES', // Spain
// 'FI', // Finland
// 'FR', // France
// 'GR', // Greece
// 'HR', // Croatia
// 'HU', // Hungary
// 'IE', // Ireland
// 'IT', // Italy
// 'LT', // Lithuania
// 'LU', // Luxembourg
// 'LV', // Latvia
// 'MT', // Malta
// 'NL', // Netherlands
// 'PL', // Poland
// 'PT', // Portugal
// 'RO', // Romania
// 'SE', // Sweden
// 'SI', // Slovenia
// 'SK', // Slovakia
'DK', // Denmark
'EE', // Estonia
'ES', // Spain
'FI', // Finland
'FR', // France
'GR', // Greece
'HR', // Croatia
'HU', // Hungary
'IE', // Ireland
'IT', // Italy
'LT', // Lithuania
'LU', // Luxembourg
'LV', // Latvia
'MT', // Malta
'NL', // Netherlands
'PL', // Poland
'PT', // Portugal
'RO', // Romania
'SE', // Sweden
'SI', // Slovenia
'SK', // Slovakia
'US', // USA
@ -170,7 +170,7 @@ class InvoiceItemSum
private function shouldCalculateTax(): self
{
if (!$this->invoice->company->calculate_taxes || $this->invoice->company->account->isFreeHostedClient()) {
if (!$this->invoice->company?->calculate_taxes || $this->invoice->company->account->isFreeHostedClient()) {
$this->calc_tax = false;
return $this;
}

View File

@ -11,12 +11,13 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Libraries\MultiDB;
use App\Models\Account;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use App\Models\Company;
use App\Libraries\MultiDB;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Password;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{

View File

@ -365,21 +365,22 @@ class LoginController extends BaseController
private function hydrateCompanyUser(): Builder
{
$cu = CompanyUser::query()->where('user_id', auth()->user()->id);
/** @var \App\Models\User $user */
$user = auth()->user();
$cu = CompanyUser::query()->where('user_id', $user->id);
if ($cu->count() == 0) {
return $cu;
}
if (CompanyUser::query()->where('user_id', auth()->user()->id)->where('company_id', auth()->user()->account->default_company_id)->exists()) {
$set_company = auth()->user()->account->default_company;
if (CompanyUser::query()->where('user_id', $user->id)->where('company_id', $user->account->default_company_id)->exists()) {
$set_company = $user->account->default_company;
} else {
$set_company = $cu->first()->company;
$set_company = CompanyUser::query()->where('user_id', $user->id)->first()->company;
}
/** @var \App\Models\User $user */
$user = auth()->user();
$user->setCompany($set_company);
$this->setLoginCache($user);
@ -389,15 +390,15 @@ class LoginController extends BaseController
$truth->setUser($user);
$truth->setCompany($set_company);
$cu->first()->account->companies->each(function ($company) use ($cu) {
$user->account->companies->each(function ($company) use ($user) {
if ($company->tokens()->where('is_system', true)->count() == 0) {
(new CreateCompanyToken($company, $cu->first()->user, request()->server('HTTP_USER_AGENT')))->handle();
(new CreateCompanyToken($company, $user, request()->server('HTTP_USER_AGENT')))->handle();
}
});
$truth->setCompanyToken(CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $set_company->id)->first());
$truth->setCompanyToken(CompanyToken::where('user_id', $user->id)->where('company_id', $set_company->id)->first());
return $cu;
return CompanyUser::query()->where('user_id', $user->id);
}
private function handleMicrosoftOauth()
@ -639,8 +640,8 @@ class LoginController extends BaseController
$parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url') . "/auth/microsoft"];
}
if(request()->hasHeader('X-REACT'))
Cache::put("react_redir:".auth()->user()->account->key, 'true', 300);
if(request()->hasHeader('X-REACT') || request()->query('react'))
Cache::put("react_redir:".auth()->user()?->account->key, 'true', 300);
if (request()->has('code')) {
return $this->handleProviderCallback($provider);
@ -696,7 +697,7 @@ class LoginController extends BaseController
$redirect_url = '/#/';
$request_from_react = Cache::pull("react_redir:".auth()->user()->account->key);
$request_from_react = Cache::pull("react_redir:".auth()->user()?->account?->key);
if($request_from_react)
$redirect_url = config('ninja.react_url')."/#/settings/user_details/connect";

View File

@ -0,0 +1,28 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
class PasswordTimeoutController extends Controller
{
public function __invoke()
{
$cached = Cache::get(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in');
return $cached ? response()->json(['message' => 'Password is valid'], 200) : response()->json(['message' => 'Invalid Password'], 412);
}
}

View File

@ -69,7 +69,7 @@ class ResetPasswordController extends Controller
$account = Account::first();
}
return $this->render('auth.passwords.reset', ['root' => 'themes', 'token' => $token, 'account' => $account]);
return $this->render('auth.passwords.reset', ['root' => 'themes', 'token' => $token, 'account' => $account, 'email' => $request->email]);
}
/**
@ -111,4 +111,28 @@ class ResetPasswordController extends Controller
return redirect('/');
}
/**
* Get the response for a successful password reset.
*
* @param \Illuminate\Http\Request $request
* @param string $response
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetResponse(Request $request, $response)
{
if ($request->wantsJson()) {
return new JsonResponse(['message' => trans($response)], 200);
}
if(Ninja::isHosted() && $request->hasHeader('X-React')){
return redirect('https://app.invoicing.co/#/login');
}
elseif($request->hasHeader('X-React'))
return redirect('/#/login');
return redirect($this->redirectPath())
->with('status', trans($response));
}
}

View File

@ -422,8 +422,12 @@ class CompanyController extends BaseController
if($request->has('e_invoice_certificate') && !is_null($request->file("e_invoice_certificate"))){
$company->e_invoice_certificate = base64_encode($request->file("e_invoice_certificate")->get());
$company->save();
$settings = $company->settings;
$settings->enable_e_invoice = true;
$company->save();
}
$this->uploadLogo($request->file('company_logo'), $company, $company);

View File

@ -748,33 +748,12 @@ class InvoiceController extends BaseController
break;
case 'email':
//check query parameter for email_type and set the template else use calculateTemplate
// if (request()->has('email_type') && in_array(request()->input('email_type'), ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'custom1', 'custom2', 'custom3'])) {
if (request()->has('email_type') && property_exists($invoice->company->settings, request()->input('email_type'))) {
$this->reminder_template = $invoice->client->getSetting(request()->input('email_type'));
} else {
$this->reminder_template = $invoice->calculateTemplate('invoice');
}
BulkInvoiceJob::dispatch($invoice, $this->reminder_template);
if (! $bulk) {
return response()->json(['message' => 'email sent'], 200);
}
break;
case 'send_email':
//check query parameter for email_type and set the template else use calculateTemplate
$template = request()->has('email_type') ? request()->input('email_type') : $invoice->calculateTemplate('invoice');
if (request()->has('email_type') && property_exists($invoice->company->settings, request()->input('email_type'))) {
$this->reminder_template = $invoice->client->getSetting(request()->input('email_type'));
} else {
$this->reminder_template = $invoice->calculateTemplate('invoice');
}
BulkInvoiceJob::dispatch($invoice, $this->reminder_template);
BulkInvoiceJob::dispatch($invoice, $template);
if (! $bulk) {
return response()->json(['message' => 'email sent'], 200);

View File

@ -436,6 +436,14 @@ class MigrationController extends BaseController
StartMigration::dispatch($migration_file, $user, $fresh_company);
}
}
return response()->json([
'_id' => Str::uuid(),
'method' => config('queue.default'),
'started_at' => now(),
], 200);
}
}
}

View File

@ -176,7 +176,9 @@ class PreviewController extends BaseController
if (Ninja::isHosted() && !in_array($request->getHost(), ['preview.invoicing.co','staging.invoicing.co'])) {
return response()->json(['message' => 'This server cannot handle this request.'], 400);
}
$start = microtime(true);
/** @var \App\Models\User $user */
$user = auth()->user();
@ -322,6 +324,8 @@ class PreviewController extends BaseController
$response = Response::make($file_path, 200);
$response->header('Content-Type', 'application/pdf');
$response->header('Server-Timing', microtime(true)-$start);
return $response;
}

View File

@ -15,44 +15,18 @@ use App\Libraries\MultiDB;
class SubdomainController extends BaseController
{
private $protected = [
'www',
'app',
'ninja',
'sentry',
'sentry2',
'staging',
'pdf',
'demo',
'docs',
'client_domain',
'custom_domain',
'preview',
'invoiceninja',
'cname',
'sandbox',
'stage',
'html',
'lb',
'shopify',
'beta',
'prometh',
'license',
'socket',
];
public function __construct()
{
}
/**
* Display a listing of the resource.
* Return if a subdomain is available.
*
* @return void
*/
public function index()
{
if (in_array(request()->input('subdomain'), $this->protected) || MultiDB::findAndSetDbByDomain(['subdomain' => request()->input('subdomain')])) {
if (!MultiDB::checkDomainAvailable(request()->input('subdomain'))) {
return response()->json(['message' => 'Domain not available'], 401);
}

View File

@ -84,6 +84,7 @@ trait VerifiesUserEmail
return $this->render('auth.confirmed', [
'root' => 'themes',
'message' => ctrans('texts.security_confirmation'),
'redirect_url' => request()->hasHeader('X-React') ? 'https://app.invoicing.co/#/' : url('/'),
]);
}
}

View File

@ -11,10 +11,11 @@
namespace App\Http\Controllers;
use App\Http\Requests\TwoFactor\EnableTwoFactorRequest;
use App\Models\User;
use App\Transformers\UserTransformer;
use App\Utils\Ninja;
use PragmaRX\Google2FA\Google2FA;
use App\Transformers\UserTransformer;
use App\Http\Requests\TwoFactor\EnableTwoFactorRequest;
class TwoFactorController extends BaseController
{
@ -24,11 +25,15 @@ class TwoFactorController extends BaseController
public function setupTwoFactor()
{
/** @var \App\Models\User $user */
$user = auth()->user();
if ($user->google_2fa_secret) {
return response()->json(['message' => '2FA already enabled'], 400);
} elseif (! $user->phone) {
} elseif(Ninja::isSelfHost()){
}
elseif (! $user->phone) {
return response()->json(['message' => ctrans('texts.set_phone_for_two_factor')], 400);
} elseif (! $user->isVerified()) {
return response()->json(['message' => 'Please confirm your account first'], 400);

View File

@ -231,8 +231,11 @@ class UserController extends BaseController
$return_user_collection = collect();
$users->each(function ($user, $key) use ($action, $return_user_collection) {
if (auth()->user()->can('edit', $user)) {
/** @var \App\Models\User $logged_in_user */
$logged_in_user = auth()->user();
$users->each(function ($user, $key) use ($logged_in_user, $action, $return_user_collection) {
if ($logged_in_user->can('edit', $user)) {
$this->user_repo->{$action}($user);
$return_user_collection->push($user->id);
@ -251,12 +254,11 @@ class UserController extends BaseController
*/
public function detach(DetachCompanyUserRequest $request, User $user)
{
// if ($request->entityIsDeleted($user)) {
// return $request->disallowUpdate();
// }
/** @var \App\Models\User $logged_in_user */
$logged_in_user = auth()->user();
$company_user = CompanyUser::whereUserId($user->id)
->whereCompanyId(auth()->user()->companyId())
->whereCompanyId($logged_in_user->companyId())
->withTrashed()
->first();

View File

@ -146,25 +146,30 @@ class PurchaseOrderController extends Controller
$purchase_orders = PurchaseOrder::query()
->whereIn('id', $this->transformKeys($data['purchase_orders']))
->where('company_id', auth()->guard('vendor')->user()->vendor->company_id)
->whereIn('status_id', [PurchaseOrder::STATUS_DRAFT, PurchaseOrder::STATUS_SENT])
->where('is_deleted', 0)
->withTrashed()
->cursor()->each(function ($purchase_order) {
$purchase_order->service()
->markSent()
->applyNumber()
->setStatus(PurchaseOrder::STATUS_ACCEPTED)
->save();
->withTrashed();
if (request()->has('signature') && ! is_null(request()->signature) && ! empty(request()->signature)) {
InjectSignature::dispatch($purchase_order, request()->signature);
}
$purchase_count_query = clone $purchase_orders;
event(new PurchaseOrderWasAccepted($purchase_order, auth()->guard('vendor')->user(), $purchase_order->company, Ninja::eventVars()));
});
$purchase_orders->whereIn('status_id', [PurchaseOrder::STATUS_DRAFT, PurchaseOrder::STATUS_SENT])
->cursor()->each(function ($purchase_order) {
$purchase_order->service()
->markSent()
->applyNumber()
->setStatus(PurchaseOrder::STATUS_ACCEPTED)
->save();
if (count($data['purchase_orders']) == 1) {
$purchase_order = PurchaseOrder::withTrashed()->where('is_deleted', 0)->whereIn('id', $this->transformKeys($data['purchase_orders']))->first();
if (request()->has('signature') && ! is_null(request()->signature) && ! empty(request()->signature)) {
(new InjectSignature($purchase_order, request()->signature))->handle();
// InjectSignature::dispatch($purchase_order, request()->signature);
}
event(new PurchaseOrderWasAccepted($purchase_order, auth()->guard('vendor')->user(), $purchase_order->company, Ninja::eventVars()));
});
if ($purchase_count_query->count() == 1) {
$purchase_order = $purchase_count_query->first();
return redirect()->route('vendor.purchase_order.show', ['purchase_order' => $purchase_order->hashed_id]);
} else {

View File

@ -15,7 +15,7 @@ class Cors
// ALLOW OPTIONS METHOD
$headers = [
'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Headers'=> 'X-API-PASSWORD-BASE64,X-API-COMPANY-KEY,X-CLIENT-VERSION,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Disposition,Range,X-CSRF-TOKEN,X-XSRF-TOKEN,X-LIVEWIRE',
'Access-Control-Allow-Headers'=> 'X-React,X-API-PASSWORD-BASE64,X-API-COMPANY-KEY,X-CLIENT-VERSION,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Disposition,Range,X-CSRF-TOKEN,X-XSRF-TOKEN,X-LIVEWIRE',
];
return Response::make('OK', 200, $headers);
@ -25,7 +25,7 @@ class Cors
$response->headers->set('Access-Control-Allow-Origin', '*');
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
$response->headers->set('Access-Control-Allow-Headers', 'X-API-PASSWORD-BASE64,X-API-COMPANY-KEY,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Disposition,Range,X-CSRF-TOKEN,X-XSRF-TOKEN,X-LIVEWIRE');
$response->headers->set('Access-Control-Allow-Headers', 'X-React,X-API-PASSWORD-BASE64,X-API-COMPANY-KEY,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Disposition,Range,X-CSRF-TOKEN,X-XSRF-TOKEN,X-LIVEWIRE');
$response->headers->set('Access-Control-Expose-Headers', 'X-APP-VERSION,X-MINIMUM-CLIENT-VERSION,Content-Disposition');
$response->headers->set('X-APP-VERSION', config('ninja.app_version'));
$response->headers->set('X-MINIMUM-CLIENT-VERSION', config('ninja.minimum_client_version'));

View File

@ -37,6 +37,7 @@ class PasswordProtection
'errors' => new stdClass,
];
/** @var \App\Models\User auth()->user() */
$timeout = auth()->user()->company()->default_password_timeout;
if ($timeout == 0) {

View File

@ -23,6 +23,7 @@ use Turbo124\Beacon\Facades\LightLogs;
*/
class QueryLogging
{
/**
* Handle an incoming request.
*
@ -33,26 +34,30 @@ class QueryLogging
*/
public function handle(Request $request, Closure $next)
{
// Enable query logging for development
if (! Ninja::isHosted() || ! config('beacon.enabled')) {
return $next($request);
}
$timeStart = microtime(true);
DB::enableQueryLog();
return $next($request);
}
public function terminate($request, $response)
{
if (! Ninja::isHosted() || ! config('beacon.enabled'))
return;
$response = $next($request);
// hide requests made by debugbar
if (strstr($request->url(), '_debugbar') === false) {
$queries = DB::getQueryLog();
$count = count($queries);
$timeEnd = microtime(true);
$time = $timeEnd - $timeStart;
$time = $timeEnd - LARAVEL_START;
// nlog("Query count = {$count}");
// nlog($queries);
// nlog($request->url());
if ($count > 175) {
nlog("Query count = {$count}");
nlog($queries);
@ -60,18 +65,17 @@ class QueryLogging
$ip = '';
if (request()->hasHeader('Cf-Connecting-Ip')) {
$ip = request()->header('Cf-Connecting-Ip');
} elseif (request()->hasHeader('X-Forwarded-For')) {
$ip = request()->header('Cf-Connecting-Ip');
if ($request->hasHeader('Cf-Connecting-Ip')) {
$ip = $request->header('Cf-Connecting-Ip');
} elseif ($request->hasHeader('X-Forwarded-For')) {
$ip = $request->header('Cf-Connecting-Ip');
} else {
$ip = request()->ip();
$ip = $request->ip();
}
LightLogs::create(new DbQuery($request->method(), substr(urldecode($request->url()), 0, 180), $count, $time, $ip))
->batch();
->batch();
}
return $response;
}
}

View File

@ -37,7 +37,9 @@ class UpdateCompanyRequest extends Request
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->company);
/** @var \App\Models\User $user */
$user = auth()->user();
return $user->can('edit', $this->company);
}
public function rules()
@ -59,14 +61,11 @@ class UpdateCompanyRequest extends Request
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
$rules['portal_domain'] = 'sometimes|url';
} else {
if (Ninja::isHosted()) {
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain($this->all())];
} else {
$rules['subdomain'] = 'nullable|regex:/^[a-zA-Z0-9.-]+[a-zA-Z0-9]$/';
}
}
if (Ninja::isHosted())
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain()];
return $rules;
}
@ -83,6 +82,10 @@ class UpdateCompanyRequest extends Request
$input['settings'] = (array)$this->filterSaveableSettings($input['settings']);
}
if(array_key_exists('subdomain', $input) && $this->subdomain == $input['subdomain']) {
unset($input['subdomain']);
}
if(array_key_exists('e_invoice_certificate_passphrase', $input) && empty($input['e_invoice_certificate_passphrase'])) {
unset($input['e_invoice_certificate_passphrase']);
}

View File

@ -24,7 +24,8 @@ class BulkInvoiceRequest extends Request
{
return [
'action' => 'required|string',
'ids' => 'required'
'ids' => 'required',
'email_type' => 'sometimes|in:reminder1,reminder2,reminder3,reminder_endless,custom1,custom2,custom3,invoice,quote,credit,payment,payment_partial,statement,purchase_order'
];
}
}

View File

@ -72,6 +72,9 @@ class UpdateInvoiceRequest extends Request
$rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
// not needed.
// $rules['partial_due_date'] = 'bail|sometimes|required_unless:partial,0,null';
return $rules;
}

View File

@ -59,6 +59,11 @@ class StoreSchedulerRequest extends Request
if (array_key_exists('next_run', $input) && is_string($input['next_run'])) {
$this->merge(['next_run_client' => $input['next_run']]);
}
if($input['template'] == 'email_record'){
$input['frequency_id'] = 0;
}
$this->replace($input);
}
}

View File

@ -57,5 +57,12 @@ class UpdateSchedulerRequest extends Request
$this->merge(['next_run_client' => $input['next_run']]);
}
if($input['template'] == 'email_record') {
$input['frequency_id'] = 0;
}
$this->replace($input);
}
}

View File

@ -19,25 +19,18 @@ use Illuminate\Contracts\Validation\Rule;
*/
class ValidSubdomain implements Rule
{
/**
* @param string $attribute
* @param mixed $value
* @return bool
*/
private $input;
public function __construct($input)
public function __construct()
{
$this->input = $input;
}
public function passes($attribute, $value)
{
if (empty($input['subdomain'])) {
if (empty($value)) {
return true;
}
return MultiDB::checkDomainAvailable($input['subdomain']);
return MultiDB::checkDomainAvailable($value);
}
/**

View File

@ -11,6 +11,7 @@
namespace App\Jobs\Client;
use App\DataMapper\Tax\ZipTax\Response;
use App\Models\Client;
use App\Models\Company;
use App\Libraries\MultiDB;
@ -51,9 +52,9 @@ class UpdateTaxData implements ShouldQueue
{
MultiDB::setDb($this->company->db);
if(!config('services.tax.zip_tax.key'))
if($this->company->account->isFreeHostedClient())
return;
$tax_provider = new \App\Services\Tax\Providers\TaxProvider($this->company, $this->client);
try {
@ -63,8 +64,7 @@ class UpdateTaxData implements ShouldQueue
if (!$this->client->state && $this->client->postal_code) {
$this->client->state = USStates::getState($this->client->postal_code);
$this->client->save();
$this->client->saveQuietly();
}
@ -73,11 +73,80 @@ class UpdateTaxData implements ShouldQueue
nlog("problem getting tax data => ".$e->getMessage());
}
/** Set static tax information */
if(!$tax_provider->updatedTaxStatus() && $this->client->country_id == 840){
$calculated_state = false;
/** State must be calculated else default to the company state for taxes */
if(array_key_exists($this->client->shipping_state, USStates::get())) {
$calculated_state = $this->client->shipping_state;
$calculated_postal_code = $this->client->shipping_postal_code;
$calculated_city = $this->client->shipping_city;
}
elseif(array_key_exists($this->client->state, USStates::get())){
$calculated_state = $this->client->state;
$calculated_postal_code = $this->client->postal_code;
$calculated_city = $this->client->city;
}
else {
try{
$calculated_state = USStates::getState($this->client->shipping_postal_code);
$calculated_postal_code = $this->client->shipping_postal_code;
$calculated_city = $this->client->shipping_city;
}
catch(\Exception $e){
nlog("could not calculate state from postal code => {$this->client->shipping_postal_code} or from state {$this->client->shipping_state}");
}
if(!$calculated_state) {
try {
$calculated_state = USStates::getState($this->client->postal_code);
$calculated_postal_code = $this->client->postal_code;
$calculated_city = $this->client->city;
} catch(\Exception $e) {
nlog("could not calculate state from postal code => {$this->client->postal_code} or from state {$this->client->state}");
}
}
if($this->company->tax_data?->seller_subregion)
$calculated_state = $this->company->tax_data?->seller_subregion;
nlog("i am trying");
if(!$calculated_state) {
nlog("could not determine state");
return;
}
}
$data = [
'seller_subregion' => $this->company->tax_data?->seller_subregion ?: '',
'geoPostalCode' => $this->client->postal_code ?? '',
'geoCity' => $this->client->city ?? '',
'geoState' => $calculated_state,
'taxSales' => $this->company->tax_data->regions->US->subregions?->{$calculated_state}?->taxSales ?? 0,
];
$tax_data = new Response($data);
$this->client->tax_data = $tax_data;
$this->client->saveQuietly();
}
}
public function middleware()
{
return [new WithoutOverlapping($this->company->id)];
return [new WithoutOverlapping($this->client->id.$this->company->id)];
}
public function failed($exception)
{
nlog("UpdateTaxData failed => ".$exception->getMessage());
}
}

View File

@ -11,12 +11,12 @@
namespace App\Jobs\Company;
use App\Models\Client;
use App\Models\Company;
use App\Libraries\MultiDB;
use Illuminate\Bus\Queueable;
use App\Jobs\Client\UpdateTaxData;
use App\DataProviders\USStates;
use Illuminate\Queue\SerializesModels;
use App\DataMapper\Tax\ZipTax\Response;
use Illuminate\Queue\InteractsWithQueue;
use App\Services\Tax\Providers\TaxProvider;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -40,33 +40,52 @@ class CompanyTaxRate implements ShouldQueue
public function handle()
{
if(!config('services.tax.zip_tax.key')) {
return;
}
MultiDB::setDB($this->company->db);
$tp = new TaxProvider($this->company);
$tp->updateCompanyTaxData();
$tp = null;
Client::query()
->where('company_id', $this->company->id)
->where('is_deleted', false)
->where('country_id', 840)
->whereNotNull('postal_code')
->whereNull('tax_data')
->where('is_tax_exempt', false)
->cursor()
->each(function ($client) {
(new UpdateTaxData($client, $this->company))->handle();
});
if(!$tp->updatedTaxStatus() && $this->company->settings->country_id == '840') {
$calculated_state = false;
/** State must be calculated else default to the company state for taxes */
if(array_key_exists($this->company->settings->state, USStates::get())) {
$calculated_state = $this->company->setting->state;
}
else {
try{
$calculated_state = USStates::getState($this->company->settings->postal_code);
}
catch(\Exception $e){
nlog("could not calculate state from postal code => {$this->company->settings->postal_code} or from state {$this->company->settings->state}");
}
if(!$calculated_state && $this->company->tax_data?->seller_subregion)
$calculated_state = $this->company->tax_data?->seller_subregion;
if(!$calculated_state)
return;
}
$data = [
'seller_subregion' => $this->company->origin_tax_data?->seller_subregion ?: '',
'geoPostalCode' => $this->company->settings->postal_code ?? '',
'geoCity' => $this->company->settings->city ?? '',
'geoState' => $calculated_state,
'taxSales' => $this->company->tax_data->regions->US->subregions?->{$calculated_state}?->taxSales ?? 0,
];
$tax_data = new Response($data);
$this->company->origin_tax_data = $tax_data;
$this->company->saveQuietly();
}
}
public function middleware()
@ -74,4 +93,7 @@ class CompanyTaxRate implements ShouldQueue
return [new WithoutOverlapping($this->company->id)];
}
public function failed($e){
nlog($e->getMessage());
}
}

View File

@ -152,12 +152,14 @@ class CreateCompany
$company->save();
$user = $this->account->users()->orderBy('id','asc')->first();
//user does not exist yet.
// MultiDB::setDb($company->db);
// $user = \App\Models\User::where('account_id', $company->account_id)->first();
$tax_rate = TaxRateFactory::create($company->id, $user->id);
$tax_rate->name = $company->tax_data->regions->EU->subregions->ES->tax_name;
$tax_rate->rate = $company->tax_data->regions->EU->subregions->ES->tax_rate;
$tax_rate->save();
// $tax_rate = TaxRateFactory::create($company->id, $user->id);
// $tax_rate->name = $company->tax_data->regions->EU->subregions->ES->tax_name;
// $tax_rate->rate = $company->tax_data->regions->EU->subregions->ES->tax_rate;
// $tax_rate->save();
return $company;
@ -191,18 +193,23 @@ class CreateCompany
$company->save();
$user = $company->account->users()->first();
//$user = $company->account->users()->first();
//user does not exist yet.
// MultiDB::setDb($company->db);
// $user = \App\Models\User::where('account_id', $company->account_id)->first();
$tax_rate = TaxRateFactory::create($company->id, $user->id);
$tax_rate->name = $company->tax_data->regions->AU->subregions->AU->tax_name;
$tax_rate->rate = $company->tax_data->regions->AU->subregions->AU->tax_rate;
$tax_rate->save();
// $tax_rate = TaxRateFactory::create($company->id, $user->id);
// $tax_rate->name = $company->tax_data->regions->AU->subregions->AU->tax_name;
// $tax_rate->rate = $company->tax_data->regions->AU->subregions->AU->tax_rate;
// $tax_rate->save();
return $company;
}
catch(\Exception $e){
nlog("SETUP: could not complete setup for Spanish Locale");
nlog($e->getMessage());
nlog("SETUP: could not complete setup for Australian Locale");
}
$company->save();

View File

@ -11,6 +11,7 @@
namespace App\Jobs\Cron;
use App\Models\Payment;
use App\Models\Project;
use App\Libraries\MultiDB;
use Illuminate\Support\Facades\Auth;
@ -65,7 +66,6 @@ class UpdateCalculatedFields
$project->save();
});
}
}
}

View File

@ -216,7 +216,7 @@ class CreateEntityPdf implements ShouldQueue
(new CreateEInvoice($this->entity, true))->handle();
}
$this->invitation = null;
$this->entity = null;
// $this->entity = null;
$this->company = null;
$this->client = null;
$this->contact = null;

View File

@ -144,12 +144,12 @@ class AdjustProductInventory implements ShouldQueue
private function notifyStocklevels(Product $product, string $notification_level)
{
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new InventoryNotificationObject($product, $notification_level))->build());
$nmo->company = $this->company;
$nmo->settings = $this->company->settings;
$this->company->company_users->each(function ($cu) use ($product, $nmo) {
$this->company->company_users->each(function ($cu) use ($product, $nmo, $notification_level) {
if ($this->checkNotificationExists($cu, $product, ['inventory_all', 'inventory_user', 'inventory_threshold_all', 'inventory_threshold_user'])) {
$nmo->mailable = new NinjaMailer((new InventoryNotificationObject($product, $notification_level, $cu->portalType()))->build());
$nmo->to_user = $cu->user;
NinjaMailerJob::dispatch($nmo);
}

View File

@ -41,7 +41,6 @@ class CheckGatewayFee implements ShouldQueue
*/
public function handle()
{
nlog("Checking Gateway Fees for Invoice Id = {$this->invoice_id}");
MultiDB::setDb($this->db);

View File

@ -3,6 +3,8 @@
namespace App\Jobs\Invoice;
use App\Jobs\Entity\CreateEntityPdf;
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
use App\Models\PurchaseOrder;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
@ -52,6 +54,11 @@ class InjectSignature implements ShouldQueue
$invitation->signature_base64 = $this->signature;
$invitation->save();
CreateEntityPdf::dispatch($invitation);
$this->entity->refresh()->service()->touchPdf(true);
// if($this->entity instanceof PurchaseOrder)
// (new CreatePurchaseOrderPdf($invitation))->handle();
// else
// (new CreateEntityPdf($invitation))->handle();
}
}

View File

@ -92,7 +92,7 @@ class PaymentFailedMailer implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]);
$mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $amount, $this->payment_hash))->build();
$mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $amount, $this->payment_hash, $company_user->portalType()))->build();
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer($mail_obj);

View File

@ -95,7 +95,6 @@ class QuoteCheckExpired implements ShouldQueue
private function queueExpiredQuoteNotification(Quote $quote)
{
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new QuoteExpiredObject($quote, $quote->company))->build());
$nmo->company = $quote->company;
$nmo->settings = $quote->company->settings;
@ -107,6 +106,8 @@ class QuoteCheckExpired implements ShouldQueue
if (! $user) {
continue;
}
$nmo->mailable = new NinjaMailer((new QuoteExpiredObject($quote, $quote->company, $company_user->portalType()))->build());
/* Returns an array of notification methods */
$methods = $this->findUserNotificationTypes($quote->invitations()->first(), $company_user, 'quote', ['all_notifications', 'quote_expired', 'quote_expired_all', 'quote_expired_user']);

View File

@ -100,10 +100,7 @@ class Import implements ShouldQueue
use Uploadable;
use SavesDocuments;
/**
* @var array
*/
private $file_path; //the file path - using a different JSON parser here.
private string $file_path; //the file path - using a different JSON parser here.
/**
* @var Company
@ -202,7 +199,11 @@ class Import implements ShouldQueue
nlog($this->company->id);
auth()->login($this->user, false);
auth()->user()->setCompany($this->company);
/** @var \App\Models\User $user */
$user = auth()->user();
$user->setCompany($this->company);
$array = json_decode(file_get_contents($this->file_path), 1);
$data = $array['data'];

View File

@ -11,26 +11,27 @@
namespace App\Jobs\Util;
use App\Exceptions\ClientHostedMigrationException;
use App\Exceptions\MigrationValidatorFailed;
use App\Exceptions\NonExistingMigrationFile;
use App\Exceptions\ProcessingMigrationArchiveFailed;
use App\Exceptions\ResourceDependencyMissing;
use App\Exceptions\ResourceNotAvailableForMigration;
use App\Libraries\MultiDB;
use App\Mail\MigrationFailed;
use App\Models\Company;
use ZipArchive;
use App\Models\User;
use App\Utils\Ninja;
use App\Models\Company;
use App\Libraries\MultiDB;
use App\Mail\MigrationFailed;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Cache;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
use ZipArchive;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Exceptions\MigrationValidatorFailed;
use App\Exceptions\NonExistingMigrationFile;
use App\Exceptions\ResourceDependencyMissing;
use App\Exceptions\ClientHostedMigrationException;
use App\Exceptions\ProcessingMigrationArchiveFailed;
use App\Exceptions\ResourceNotAvailableForMigration;
class StartMigration implements ShouldQueue
{
@ -79,6 +80,8 @@ class StartMigration implements ShouldQueue
{
nlog('Inside Migration Job');
Cache::put("migration-{$this->company->company_key}", "started", 86400);
set_time_limit(0);
MultiDB::setDb($this->company->db);
@ -124,6 +127,8 @@ class StartMigration implements ShouldQueue
$this->company->update_products = $update_product_flag;
$this->company->save();
Cache::put("migration-{$this->company->company_key}", "completed", 86400);
App::forgetInstance('translator');
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
@ -131,6 +136,8 @@ class StartMigration implements ShouldQueue
$this->company->update_products = $update_product_flag;
$this->company->save();
Cache::put("migration-{$this->company->company_key}", "failed", 86400);
if (Ninja::isHosted()) {
app('sentry')->captureException($e);
}
@ -147,6 +154,9 @@ class StartMigration implements ShouldQueue
if (app()->environment() !== 'production') {
info($e->getMessage());
}
Storage::deleteDirectory(public_path("storage/migrations/{$filename}"));
}
//always make sure we unset the migration as running

View File

@ -45,6 +45,32 @@ class MultiDB
public static $dbs = ['db-ninja-01', 'db-ninja-02'];
private static $protected_domains = [
'www',
'app',
'ninja',
'sentry',
'sentry2',
'staging',
'pdf',
'demo',
'docs',
'client_domain',
'custom_domain',
'preview',
'invoiceninja',
'cname',
'sandbox',
'stage',
'html',
'lb',
'shopify',
'beta',
'prometh',
'license',
'socket',
];
/**
* @return array
*/
@ -55,10 +81,15 @@ class MultiDB
public static function checkDomainAvailable($subdomain) : bool
{
if (! config('ninja.db.multi_db_enabled')) {
return Company::whereSubdomain($subdomain)->count() == 0;
}
if (in_array($subdomain, self::$protected_domains)) {
return false;
}
$current_db = config('database.default');
foreach (self::$dbs as $db) {

View File

@ -71,21 +71,21 @@ class OAuth
public static function providerToString(int $social_provider): string
{
switch ($social_provider) {
case SOCIAL_GOOGLE:
case self::SOCIAL_GOOGLE:
return 'google';
case SOCIAL_FACEBOOK:
case self::SOCIAL_FACEBOOK:
return 'facebook';
case SOCIAL_GITHUB:
case self::SOCIAL_GITHUB:
return 'github';
case SOCIAL_LINKEDIN:
case self::SOCIAL_LINKEDIN:
return 'linkedin';
case SOCIAL_TWITTER:
case self::SOCIAL_TWITTER:
return 'twitter';
case SOCIAL_BITBUCKET:
case self::SOCIAL_BITBUCKET:
return 'bitbucket';
case SOCIAL_MICROSOFT:
case self::SOCIAL_MICROSOFT:
return 'microsoft';
case SOCIAL_APPLE:
case self::SOCIAL_APPLE:
return 'apple';
}
}
@ -94,21 +94,21 @@ class OAuth
{
switch ($social_provider) {
case 'google':
return SOCIAL_GOOGLE;
return self::SOCIAL_GOOGLE;
case 'facebook':
return SOCIAL_FACEBOOK;
return self::SOCIAL_FACEBOOK;
case 'github':
return SOCIAL_GITHUB;
return self::SOCIAL_GITHUB;
case 'linkedin':
return SOCIAL_LINKEDIN;
return self::SOCIAL_LINKEDIN;
case 'twitter':
return SOCIAL_TWITTER;
return self::SOCIAL_TWITTER;
case 'bitbucket':
return SOCIAL_BITBUCKET;
return self::SOCIAL_BITBUCKET;
case 'microsoft':
return SOCIAL_MICROSOFT;
return self::SOCIAL_MICROSOFT;
case 'apple':
return SOCIAL_APPLE;
return self::SOCIAL_APPLE;
}
}

View File

@ -40,8 +40,6 @@ class CreditCreatedNotification implements ShouldQueue
{
MultiDB::setDb($event->company->db);
// $first_notification_sent = true;
$credit = $event->credit;
/* We loop through each user and determine whether they need to be notified */
@ -60,7 +58,7 @@ class CreditCreatedNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($credit, 'credit'))->build());
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($credit, 'credit', $company_user->portalType()))->build());
$nmo->company = $credit->company;
$nmo->settings = $credit->company->settings;
$nmo->to_user = $user;

View File

@ -56,7 +56,7 @@ class CreditEmailedNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'credit', $event->template))->build());
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'credit', $event->template, $company_user->portalType()))->build());
$nmo->company = $credit->company;
$nmo->settings = $credit->company->settings;
$nmo->to_user = $user;

View File

@ -24,7 +24,7 @@ class InvoiceCreatedNotification implements ShouldQueue
{
use UserNotifies;
public $delay = 7;
public $delay = 3;
public function __construct()
{
@ -48,11 +48,11 @@ class InvoiceCreatedNotification implements ShouldQueue
foreach ($event->company->company_users as $company_user) {
/* The User */
$user = $company_user->user;
if (! $user) {
continue;
}
/* This is only here to handle the alternate message channels - ie Slack */
// $notification = new EntitySentNotification($event->invitation, 'invoice');
@ -64,7 +64,7 @@ class InvoiceCreatedNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($invoice, 'invoice'))->build());
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($invoice, 'invoice', $company_user->portalType()))->build());
$nmo->company = $invoice->company;
$nmo->settings = $invoice->company->settings;
$nmo->to_user = $user;

View File

@ -64,7 +64,7 @@ class InvoiceEmailedNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'invoice', $event->template))->build());
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'invoice', $event->template, $company_user->portalType()))->build());
$nmo->company = $invoice->company;
$nmo->settings = $invoice->company->settings;
$nmo->to_user = $user;

View File

@ -49,7 +49,7 @@ class InvoiceFailedEmailNotification
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntityFailedSendObject($event->invitation->withoutRelations(), 'invoice', $event->template, $event->message))->build());
$nmo->mailable = new NinjaMailer((new EntityFailedSendObject($event->invitation->withoutRelations(), 'invoice', $event->template, $event->message, $company_user->portalType()))->build());
$nmo->company = $invoice->company->withoutRelations();
$nmo->settings = $invoice->company->settings;
$nmo->to_user = $user;

View File

@ -70,7 +70,7 @@ class InvitationViewedListener implements ShouldQueue
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntityViewedObject($invitation, $entity_name))->build());
$nmo->mailable = new NinjaMailer((new EntityViewedObject($invitation, $entity_name, $company_user->portalType()))->build());
$nmo->company = $invitation->company;
$nmo->settings = $invitation->company->settings;

View File

@ -76,7 +76,7 @@ class PaymentNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntityPaidObject($payment))->build());
$nmo->mailable = new NinjaMailer((new EntityPaidObject($payment, $company_user->portalType()))->build());
$nmo->company = $event->company;
$nmo->settings = $event->company->settings;
$nmo->to_user = $user;
@ -108,7 +108,7 @@ class PaymentNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntityPaidObject($payment))->build());
$nmo->mailable = new NinjaMailer((new EntityPaidObject($payment, $company_user->portalType()))->build());
$nmo->company = $event->company;
$nmo->settings = $event->company->settings;
$nmo->to_user = $user;
@ -161,11 +161,12 @@ class PaymentNotification implements ShouldQueue
}
/**
* @param $data
* @param string $url
*/
private function sendAnalytics($data)
{
$data = utf8_encode($data);
private function sendAnalytics($url)
{
$data = mb_convert_encoding($url, 'UTF-8');
// $data = utf8_encode($data);
$curl = curl_init();
$opts = [

View File

@ -59,7 +59,7 @@ class PurchaseOrderAcceptedListener implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new PurchaseOrderAcceptedObject($purchase_order, $event->company))->build());
$nmo->mailable = new NinjaMailer((new PurchaseOrderAcceptedObject($purchase_order, $event->company, $company_user->portalType()))->build());
$nmo->company = $event->company;
$nmo->settings = $event->company->settings;

View File

@ -45,7 +45,6 @@ class PurchaseOrderCreatedListener implements ShouldQueue
$purchase_order = $event->purchase_order;
/* We loop through each user and determine whether they need to be notified */
foreach ($event->company->company_users as $company_user) {
/* The User */
@ -66,7 +65,7 @@ class PurchaseOrderCreatedListener implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($purchase_order, 'purchase_order'))->build());
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($purchase_order, 'purchase_order', $company_user->portalType()))->build());
$nmo->company = $purchase_order->company;
$nmo->settings = $purchase_order->company->settings;

View File

@ -62,7 +62,7 @@ class PurchaseOrderEmailedNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'purchase_order', 'purchase_order'))->build());
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'purchase_order', 'purchase_order', $company_user->portalType()))->build());
$nmo->company = $purchase_order->company;
$nmo->settings = $purchase_order->company->settings;
$nmo->to_user = $user;

View File

@ -61,7 +61,7 @@ class QuoteApprovedNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new QuoteApprovedObject($quote, $event->company))->build());
$nmo->mailable = new NinjaMailer((new QuoteApprovedObject($quote, $event->company, $company_user->portalType()))->build());
$nmo->company = $quote->company;
$nmo->settings = $quote->company->settings;

View File

@ -54,6 +54,12 @@ class QuoteCreatedNotification implements ShouldQueue
continue;
}
$use_react_link = false;
if(isset($company_user->react_settings->react_notification_link) && $company_user->react_settings->react_notification_link) {
$use_react_link = true;
}
/* This is only here to handle the alternate message channels - ie Slack */
// $notification = new EntitySentNotification($event->invitation, 'quote');
@ -65,7 +71,7 @@ class QuoteCreatedNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($quote, 'quote'))->build());
$nmo->mailable = new NinjaMailer((new EntityCreatedObject($quote, 'quote', $company_user->portalType()))->build());
$nmo->company = $quote->company;
$nmo->settings = $quote->company->settings;

View File

@ -54,7 +54,7 @@ class QuoteEmailedNotification implements ShouldQueue
unset($methods[$key]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'quote', $event->template))->build());
$nmo->mailable = new NinjaMailer((new EntitySentObject($event->invitation, 'quote', $event->template, $company_user->portalType()))->build());
$nmo->company = $quote->company;
$nmo->settings = $quote->company->settings;
$nmo->to_user = $user;

View File

@ -11,19 +11,16 @@
namespace App\Mail\Admin;
use App\Models\User;
use App\Utils\Ninja;
use App\Models\Company;
use Illuminate\Support\Facades\App;
class AccountCreatedObject
{
public $user;
public $company;
public function __construct($user, $company)
public function __construct(public User $user, public Company $company)
{
$this->user = $user;
$this->company = $company;
}
public function build()

View File

@ -17,7 +17,7 @@ use App\Utils\Ninja;
use App\Utils\Traits\MakesHash;
use Illuminate\Support\Facades\App;
use stdClass;
//@deprecated
class AutoBillingFailureObject
{
use MakesHash;

View File

@ -33,10 +33,13 @@ class EntityCreatedObject
private $template_body;
public function __construct($entity, $entity_type)
protected bool $use_react_link;
public function __construct($entity, $entity_type, $use_react_link = false)
{
$this->entity_type = $entity_type;
$this->entity = $entity;
$this->use_react_link = $use_react_link;
}
/**
@ -81,7 +84,7 @@ class EntityCreatedObject
'purchase_order' => $this->entity->number,
]
),
'url' => $this->entity->invitations()->first()->getAdminLink(),
'url' => $this->entity->invitations()->first()->getAdminLink($this->use_react_link),
'button' => ctrans("texts.view_{$this->entity_type}"),
'signature' => $this->company->settings->email_signature,
'logo' => $this->company->present()->logo(),
@ -165,7 +168,7 @@ class EntityCreatedObject
return [
'title' => $this->getSubject(),
'message' => $this->getMessage(),
'url' => $this->entity->invitations()->first()->getAdminLink(),
'url' => $this->entity->invitations()->first()->getAdminLink($this->use_react_link),
'button' => ctrans("texts.view_{$this->entity_type}"),
'signature' => $settings->email_signature,
'logo' => $this->company->present()->logo(),

View File

@ -39,7 +39,9 @@ class EntityFailedSendObject
private $message;
public function __construct($invitation, $entity_type, $template, $message)
protected $use_react_url;
public function __construct($invitation, $entity_type, $template, $message, $use_react_url)
{
$this->invitation = $invitation;
$this->entity_type = $entity_type;
@ -48,6 +50,7 @@ class EntityFailedSendObject
$this->company = $invitation->company;
$this->template = $template;
$this->message = $message;
$this->use_react_url = $use_react_url;
}
public function build()
@ -149,7 +152,7 @@ class EntityFailedSendObject
'contact' => $this->contact->present()->name(),
]
),
'url' => $this->invitation->getAdminLink(),
'url' => $this->invitation->getAdminLink($this->use_react_url),
'button' => ctrans("texts.view_{$this->entity_type}"),
'signature' => $signature,
'logo' => $this->company->present()->logo(),

View File

@ -30,7 +30,7 @@ class EntityPaidObject
public $settings;
public function __construct(public Payment $payment)
public function __construct(public Payment $payment, protected bool $use_react_url)
{
$this->payment = $payment;
$this->company = $payment->company;
@ -98,7 +98,7 @@ class EntityPaidObject
'invoice' => $invoice_texts,
]
),
'url' => config('ninja.app_url'),
'url' => $this->payment->portalUrl($this->use_react_url),
'button' => ctrans('texts.view_payment'),
'signature' => $settings->email_signature,
'logo' => $this->company->present()->logo(),
@ -117,4 +117,5 @@ class EntityPaidObject
return $signature;
}
}

View File

@ -36,7 +36,9 @@ class EntitySentObject
private $template_body;
public function __construct($invitation, $entity_type, $template)
protected $use_react_url;
public function __construct($invitation, $entity_type, $template, $use_react_url)
{
$this->invitation = $invitation;
$this->entity_type = $entity_type;
@ -44,6 +46,7 @@ class EntitySentObject
$this->contact = $invitation->contact;
$this->company = $invitation->company;
$this->template = $template;
$this->use_react_url = $use_react_url;
}
public function build()
@ -78,7 +81,7 @@ class EntitySentObject
'purchase_order' => $this->entity->number,
]
),
'url' => $this->invitation->getAdminLink(),
'url' => $this->invitation->getAdminLink($this->use_react_url),
'button' => ctrans("texts.view_{$this->entity_type}"),
'signature' => $this->company->settings->email_signature,
'logo' => $this->company->present()->logo(),
@ -185,7 +188,7 @@ class EntitySentObject
return [
'title' => $this->getSubject(),
'message' => $this->getMessage(),
'url' => $this->invitation->getAdminLink(),
'url' => $this->invitation->getAdminLink($this->use_react_url),
'button' => ctrans("texts.view_{$this->entity_type}"),
'signature' => $settings->email_signature,
'logo' => $this->company->present()->logo(),

View File

@ -30,13 +30,16 @@ class EntityViewedObject
public $settings;
public function __construct($invitation, $entity_type)
protected $use_react_url;
public function __construct($invitation, $entity_type, $use_react_url)
{
$this->invitation = $invitation;
$this->entity_type = $entity_type;
$this->entity = $invitation->{$entity_type};
$this->contact = $invitation->contact;
$this->company = $invitation->company;
$this->use_react_url = $use_react_url;
}
public function build()
@ -104,7 +107,7 @@ class EntityViewedObject
'invoice' => $this->entity->number,
]
),
'url' => $this->invitation->getAdminLink(),
'url' => $this->invitation->getAdminLink($this->use_react_url),
'button' => ctrans("texts.view_{$this->entity_type}"),
'signature' => $settings->email_signature,
'logo' => $this->company->present()->logo(),

View File

@ -12,7 +12,6 @@
namespace App\Mail\Admin;
use App\Models\Company;
use App\Models\Product;
use App\Utils\Ninja;
use Illuminate\Support\Facades\App;
@ -20,19 +19,9 @@ use stdClass;
class InventoryNotificationObject
{
public Product $product;
public Company $company;
public $settings;
public string $notification_level;
public function __construct(Product $product, string $notification_level)
public function __construct(protected Product $product, public string $notification_level, protected bool $use_react_url)
{
$this->product = $product;
$this->company = $product->company;
$this->settings = $this->company->settings;
}
public function build()
@ -41,16 +30,16 @@ class InventoryNotificationObject
/* Init a new copy of the translator*/
$t = app('translator');
/* Set the locale*/
App::setLocale($this->company->getLocale());
App::setLocale($this->product->company->getLocale());
/* Set customized translations _NOW_ */
$t->replace(Ninja::transformTranslations($this->company->settings));
$t->replace(Ninja::transformTranslations($this->product->company->settings));
$mail_obj = new stdClass;
$mail_obj->amount = $this->getAmount();
$mail_obj->subject = $this->getSubject();
$mail_obj->data = $this->getData();
$mail_obj->markdown = 'email.admin.generic';
$mail_obj->tag = $this->company->company_key;
$mail_obj->tag = $this->product->company->company_key;
return $mail_obj;
}
@ -79,12 +68,12 @@ class InventoryNotificationObject
'product' => $this->product->product_key.': '.$this->product->notes,
]
),
'url' => config('ninja.app_url'),
'button' => ctrans('texts.login'),
'signature' => $this->settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $this->settings,
'whitelabel' => $this->company->account->isPaid() ? true : false,
'url' => $this->product->portalUrl($this->use_react_url),
'button' => $this->use_react_url ? ctrans('texts.product_library') : ctrans('ninja.app_url'),
'signature' => $this->product->company->settings->email_signature,
'logo' => $this->product->company->present()->logo(),
'settings' => $this->product->company->settings,
'whitelabel' => $this->product->company->account->isPaid() ? true : false,
];
return $data;

View File

@ -24,17 +24,6 @@ class PaymentFailureObject
{
use MakesHash;
public Client $client;
public string $error;
public Company $company;
public $amount;
public ?PaymentHash $payment_hash;
// private $invoices;
/**
* Create a new job instance.
*
@ -43,19 +32,8 @@ class PaymentFailureObject
* @param $company
* @param $amount
*/
public function __construct(Client $client, string $error, Company $company, $amount, ?PaymentHash $payment_hash)
public function __construct(public Client $client, public string $error, public Company $company, public float $amount, public ?PaymentHash $payment_hash, protected bool $use_react_url)
{
$this->client = $client;
$this->error = $error;
$this->company = $company;
$this->amount = $amount;
$this->company = $company;
$this->payment_hash = $payment_hash;
}
public function build()
@ -115,8 +93,8 @@ class PaymentFailureObject
'logo' => $this->company->present()->logo(),
'settings' => $this->client->getMergedSettings(),
'whitelabel' => $this->company->account->isPaid() ? true : false,
'url' => config('ninja.app_url'),
'button' => ctrans('texts.login'),
'url' => $this->client->portalUrl($this->use_react_url),
'button' => $this->use_react_url ? ctrans('texts.view_client') : ctrans('texts.login'),
'additional_info' => $this->error,
];

View File

@ -21,16 +21,9 @@ use stdClass;
class PurchaseOrderAcceptedObject
{
public $purchase_order;
public $company;
public $settings;
public function __construct(PurchaseOrder $purchase_order, Company $company)
public function __construct(public PurchaseOrder $purchase_order, public Company $company, protected bool $use_react_url)
{
$this->purchase_order = $purchase_order;
$this->company = $company;
}
public function build()
@ -90,7 +83,7 @@ class PurchaseOrderAcceptedObject
'purchase_order' => $this->purchase_order->number,
]
),
'url' => $this->purchase_order->invitations->first()->getAdminLink(),
'url' => $this->purchase_order->invitations->first()->getAdminLink($this->use_react_url),
'button' => ctrans('texts.view_purchase_order'),
'signature' => $settings->email_signature,
'logo' => $this->company->present()->logo(),

View File

@ -21,16 +21,9 @@ use stdClass;
class QuoteApprovedObject
{
public $quote;
public $company;
public $settings;
public function __construct(Quote $quote, Company $company)
public function __construct(public Quote $quote, public Company $company, public bool $use_react_url)
{
$this->quote = $quote;
$this->company = $company;
}
public function build()
@ -90,7 +83,7 @@ class QuoteApprovedObject
'invoice' => $this->quote->number,
]
),
'url' => $this->quote->invitations->first()->getAdminLink(),
'url' => $this->quote->invitations->first()->getAdminLink($this->use_react_url),
'button' => ctrans('texts.view_quote'),
'signature' => $settings->email_signature,
'logo' => $this->company->present()->logo(),

View File

@ -21,16 +21,9 @@ use stdClass;
class QuoteExpiredObject
{
public $quote;
public $company;
public $settings;
public function __construct(Quote $quote, Company $company)
public function __construct(public Quote $quote, public Company $company, public bool $use_react_url)
{
$this->quote = $quote;
$this->company = $company;
}
public function build()
@ -90,8 +83,8 @@ class QuoteExpiredObject
'invoice' => $this->quote->number,
]
),
'url' => $this->quote->invitations->first()->getAdminLink(),
'button' => ctrans('texts.view_quote'),
'url' => $this->quote->invitations->first()->getAdminLink($this->use_react_url),
'button' => $this->use_react_url ? ctrans('texts.view_quote') : ctrans('texts.login'),
'signature' => $settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $settings,

View File

@ -11,15 +11,16 @@
namespace App\Mail\Engine;
use App\DataMapper\EmailTemplateDefaults;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Account;
use App\Utils\Helpers;
use App\Utils\Ninja;
use App\Utils\Number;
use App\Utils\Helpers;
use App\Models\Account;
use App\Utils\Traits\MakesDates;
use App\Jobs\Entity\CreateRawPdf;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Storage;
use App\DataMapper\EmailTemplateDefaults;
class PaymentEmailEngine extends BaseEmailEngine
{
@ -105,6 +106,18 @@ class PaymentEmailEngine extends BaseEmailEngine
}
}
}
if($this->client->getSetting('enable_e_invoice'))
{
$e_invoice_filepath = $invoice->service()->getEInvoice($this->contact);
if(Storage::disk(config('filesystems.default'))->exists($e_invoice_filepath)) {
$this->setAttachments([['path' => Storage::disk(config('filesystems.default'))->path($e_invoice_filepath), 'name' => $invoice->getFileName("xml"), 'mime' => null]]);
}
}
});
}

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