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,6 +127,7 @@ class CheckData extends Command
$this->checkClientSettings(); $this->checkClientSettings();
$this->checkCompanyTokens(); $this->checkCompanyTokens();
$this->checkUserState(); $this->checkUserState();
$this->checkContactEmailAndSendEmailStatus();
if (Ninja::isHosted()) { if (Ninja::isHosted()) {
$this->checkAccountStatuses(); $this->checkAccountStatuses();
@ -434,11 +435,38 @@ class CheckData extends Command
private function checkEntityInvitations() private function checkEntityInvitations()
{ {
RecurringInvoiceInvitation::where('deleted_at', "0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]); 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]); 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]); 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]); 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) { collect([Invoice::class, Quote::class, Credit::class, PurchaseOrder::class])->each(function ($entity) {
if ($entity::doesntHave('invitations')->count() > 0) { 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\Models\Product;
use App\DataProviders\USStates; use App\DataProviders\USStates;
use App\DataMapper\Tax\ZipTax\Response; use App\DataMapper\Tax\ZipTax\Response;
use App\Services\Tax\Providers\TaxProvider;
class BaseRule implements RuleInterface class BaseRule implements RuleInterface
{ {
@ -203,18 +202,9 @@ class BaseRule implements RuleInterface
$tax_data = $company->origin_tax_data; $tax_data = $company->origin_tax_data;
} }
else{ elseif($this->client->tax_data){
/** Ensures the client tax data has been updated */ $tax_data = $this->client->tax_data;
// 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;
} }
@ -273,7 +263,8 @@ class BaseRule implements RuleInterface
public function isTaxableRegion(): bool 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 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 */ /** @var float $reduced_tax_rate */
public float $reduced_tax_rate = 0; public float $reduced_tax_rate = 0;
public string $tax_name1 = 'MwSt.';
/** /**
* Initializes the rules and builds any required data. * Initializes the rules and builds any required data.
* *
@ -90,7 +91,6 @@ class Rule extends BaseRule implements RuleInterface
public function reverseTax($item): self public function reverseTax($item): self
{ {
$this->tax_rate1 = 0; $this->tax_rate1 = 0;
$this->tax_name1 = 'ermäßigte MwSt.';
return $this; return $this;
} }
@ -103,7 +103,6 @@ class Rule extends BaseRule implements RuleInterface
public function taxReduced($item): self public function taxReduced($item): self
{ {
$this->tax_rate1 = $this->reduced_tax_rate; $this->tax_rate1 = $this->reduced_tax_rate;
$this->tax_name1 = 'ermäßigte MwSt.';
return $this; return $this;
} }
@ -116,7 +115,6 @@ class Rule extends BaseRule implements RuleInterface
public function zeroRated($item): self public function zeroRated($item): self
{ {
$this->tax_rate1 = 0; $this->tax_rate1 = 0;
$this->tax_name1 = 'ermäßigte MwSt.';
return $this; return $this;
} }
@ -144,7 +142,6 @@ class Rule extends BaseRule implements RuleInterface
{ {
$this->tax_rate1 = $this->tax_rate; $this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this; return $this;
} }
@ -158,7 +155,6 @@ class Rule extends BaseRule implements RuleInterface
{ {
$this->tax_rate1 = $this->tax_rate; $this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this; return $this;
} }
@ -172,7 +168,6 @@ class Rule extends BaseRule implements RuleInterface
{ {
$this->tax_rate1 = $this->tax_rate; $this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this; return $this;
} }
@ -186,7 +181,6 @@ class Rule extends BaseRule implements RuleInterface
{ {
$this->tax_rate1 = $this->tax_rate; $this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this; return $this;
} }
@ -223,19 +217,20 @@ class Rule extends BaseRule implements RuleInterface
public function calculateRates(): self public function calculateRates(): self
{ {
if ($this->client->is_tax_exempt) { if ($this->client->is_tax_exempt) {
nlog("tax exempt"); // nlog("tax exempt");
$this->tax_rate = 0; $this->tax_rate = 0;
$this->reduced_tax_rate = 0; $this->reduced_tax_rate = 0;
} }
elseif($this->client_subregion != $this->client->company->tax_data->seller_subregion && in_array($this->client_subregion, $this->eu_country_codes) && $this->client->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->tax_rate = 0;
$this->reduced_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 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->tax_rate = 0;
$this->reduced_tax_rate = 0; $this->reduced_tax_rate = 0;
} }
@ -243,22 +238,22 @@ class Rule extends BaseRule implements RuleInterface
{ {
$this->defaultForeign(); $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) 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->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; $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_tax_rate;
} }
else { 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->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; $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate;
} }
} }
else { 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->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; $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 = new \stdClass();
$this->regions->EU->subregions->AT = 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->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->AT->apply_tax = false;
$this->regions->EU->subregions->BE = new \stdClass(); $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_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 = "Sales Tax";
// $this->tax_name1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name;
return $this; return $this;
} }

View File

@ -67,8 +67,8 @@ class Response
public float $taxSales = 0; public float $taxSales = 0;
public string $taxName = ""; public string $taxName = "";
public float $taxUse = 0; public float $taxUse = 0;
public string $txbService = ""; // N = No, Y = Yes public string $txbService = "Y"; // N = No, Y = Yes
public string $txbFreight = ""; // N = No, Y = Yes public string $txbFreight = "Y"; // N = No, Y = Yes
public float $stateSalesTax = 0; public float $stateSalesTax = 0;
public float $stateUseTax = 0; public float $stateUseTax = 0;
public float $citySalesTax = 0; public float $citySalesTax = 0;
@ -98,7 +98,7 @@ class Response
public float $district5UseTax = 0; public float $district5UseTax = 0;
/* US SPECIFIC TAX CODES */ /* 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) public function __construct($data = null)
{ {

View File

@ -104,7 +104,8 @@ class Handler extends ExceptionHandler
if (Ninja::isHosted()) { if (Ninja::isHosted()) {
if($exception instanceof ThrottleRequestsException && class_exists(\Modules\Admin\Events\ThrottledExceptionRaised::class)) { 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 { Integration::configureScope(function (Scope $scope): void {

View File

@ -106,6 +106,16 @@ class ClientFilters extends QueryFilters
return $this->builder->where('number', $number); 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. * Filter based on search text.
* *

View File

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

View File

@ -138,6 +138,7 @@ abstract class QueryFilters
*/ */
public function status(string $filter = ''): Builder public function status(string $filter = ''): Builder
{ {
if (strlen($filter) == 0) { if (strlen($filter) == 0) {
return $this->builder; return $this->builder;
} }
@ -146,17 +147,17 @@ abstract class QueryFilters
return $this->builder->where(function ($query) use ($filters) { return $this->builder->where(function ($query) use ($filters) {
if (in_array(self::STATUS_ACTIVE, $filters)) { if (in_array(self::STATUS_ACTIVE, $filters)) {
$query->orWhereNull('deleted_at'); $query = $query->orWhereNull('deleted_at');
} }
if (in_array(self::STATUS_ARCHIVED, $filters)) { if (in_array(self::STATUS_ARCHIVED, $filters)) {
$query->orWhere(function ($query) { $query = $query->orWhere(function ($q) {
$query->whereNotNull('deleted_at')->where('is_deleted', 0); $q->whereNotNull('deleted_at')->where('is_deleted', 0);
}); });
} }
if (in_array(self::STATUS_DELETED, $filters)) { 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 = [ private array $tax_jurisdictions = [
// 'AT', // Austria 'AT', // Austria
// 'BE', // Belgium 'BE', // Belgium
// 'BG', // Bulgaria 'BG', // Bulgaria
// 'CY', // Cyprus 'CY', // Cyprus
// 'CZ', // Czech Republic 'CZ', // Czech Republic
'DE', // Germany 'DE', // Germany
// 'DK', // Denmark 'DK', // Denmark
// 'EE', // Estonia 'EE', // Estonia
// 'ES', // Spain 'ES', // Spain
// 'FI', // Finland 'FI', // Finland
// 'FR', // France 'FR', // France
// 'GR', // Greece 'GR', // Greece
// 'HR', // Croatia 'HR', // Croatia
// 'HU', // Hungary 'HU', // Hungary
// 'IE', // Ireland 'IE', // Ireland
// 'IT', // Italy 'IT', // Italy
// 'LT', // Lithuania 'LT', // Lithuania
// 'LU', // Luxembourg 'LU', // Luxembourg
// 'LV', // Latvia 'LV', // Latvia
// 'MT', // Malta 'MT', // Malta
// 'NL', // Netherlands 'NL', // Netherlands
// 'PL', // Poland 'PL', // Poland
// 'PT', // Portugal 'PT', // Portugal
// 'RO', // Romania 'RO', // Romania
// 'SE', // Sweden 'SE', // Sweden
// 'SI', // Slovenia 'SI', // Slovenia
// 'SK', // Slovakia 'SK', // Slovakia
'US', // USA 'US', // USA
@ -170,7 +170,7 @@ class InvoiceItemSum
private function shouldCalculateTax(): self 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; $this->calc_tax = false;
return $this; return $this;
} }

View File

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

View File

@ -365,21 +365,22 @@ class LoginController extends BaseController
private function hydrateCompanyUser(): Builder 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) { if ($cu->count() == 0) {
return $cu; return $cu;
} }
if (CompanyUser::query()->where('user_id', auth()->user()->id)->where('company_id', auth()->user()->account->default_company_id)->exists()) { if (CompanyUser::query()->where('user_id', $user->id)->where('company_id', $user->account->default_company_id)->exists()) {
$set_company = auth()->user()->account->default_company; $set_company = $user->account->default_company;
} else { } 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); $user->setCompany($set_company);
$this->setLoginCache($user); $this->setLoginCache($user);
@ -389,15 +390,15 @@ class LoginController extends BaseController
$truth->setUser($user); $truth->setUser($user);
$truth->setCompany($set_company); $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) { 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() private function handleMicrosoftOauth()
@ -639,8 +640,8 @@ class LoginController extends BaseController
$parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url') . "/auth/microsoft"]; $parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url') . "/auth/microsoft"];
} }
if(request()->hasHeader('X-REACT')) if(request()->hasHeader('X-REACT') || request()->query('react'))
Cache::put("react_redir:".auth()->user()->account->key, 'true', 300); Cache::put("react_redir:".auth()->user()?->account->key, 'true', 300);
if (request()->has('code')) { if (request()->has('code')) {
return $this->handleProviderCallback($provider); return $this->handleProviderCallback($provider);
@ -696,7 +697,7 @@ class LoginController extends BaseController
$redirect_url = '/#/'; $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) if($request_from_react)
$redirect_url = config('ninja.react_url')."/#/settings/user_details/connect"; $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(); $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('/'); 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,6 +422,10 @@ class CompanyController extends BaseController
if($request->has('e_invoice_certificate') && !is_null($request->file("e_invoice_certificate"))){ 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->e_invoice_certificate = base64_encode($request->file("e_invoice_certificate")->get());
$settings = $company->settings;
$settings->enable_e_invoice = true;
$company->save(); $company->save();
} }

View File

@ -748,33 +748,12 @@ class InvoiceController extends BaseController
break; break;
case 'email': 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': case 'send_email':
//check query parameter for email_type and set the template else use calculateTemplate //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'))) { BulkInvoiceJob::dispatch($invoice, $template);
$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) { if (! $bulk) {
return response()->json(['message' => 'email sent'], 200); return response()->json(['message' => 'email sent'], 200);

View File

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

View File

@ -177,6 +177,8 @@ class PreviewController extends BaseController
return response()->json(['message' => 'This server cannot handle this request.'], 400); return response()->json(['message' => 'This server cannot handle this request.'], 400);
} }
$start = microtime(true);
/** @var \App\Models\User $user */ /** @var \App\Models\User $user */
$user = auth()->user(); $user = auth()->user();
@ -322,6 +324,8 @@ class PreviewController extends BaseController
$response = Response::make($file_path, 200); $response = Response::make($file_path, 200);
$response->header('Content-Type', 'application/pdf'); $response->header('Content-Type', 'application/pdf');
$response->header('Server-Timing', microtime(true)-$start);
return $response; return $response;
} }

View File

@ -15,44 +15,18 @@ use App\Libraries\MultiDB;
class SubdomainController extends BaseController 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() public function __construct()
{ {
} }
/** /**
* Display a listing of the resource. * Return if a subdomain is available.
* *
* @return void
*/ */
public function index() 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); return response()->json(['message' => 'Domain not available'], 401);
} }

View File

@ -84,6 +84,7 @@ trait VerifiesUserEmail
return $this->render('auth.confirmed', [ return $this->render('auth.confirmed', [
'root' => 'themes', 'root' => 'themes',
'message' => ctrans('texts.security_confirmation'), '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; namespace App\Http\Controllers;
use App\Http\Requests\TwoFactor\EnableTwoFactorRequest;
use App\Models\User; use App\Models\User;
use App\Transformers\UserTransformer; use App\Utils\Ninja;
use PragmaRX\Google2FA\Google2FA; use PragmaRX\Google2FA\Google2FA;
use App\Transformers\UserTransformer;
use App\Http\Requests\TwoFactor\EnableTwoFactorRequest;
class TwoFactorController extends BaseController class TwoFactorController extends BaseController
{ {
@ -24,11 +25,15 @@ class TwoFactorController extends BaseController
public function setupTwoFactor() public function setupTwoFactor()
{ {
/** @var \App\Models\User $user */
$user = auth()->user(); $user = auth()->user();
if ($user->google_2fa_secret) { if ($user->google_2fa_secret) {
return response()->json(['message' => '2FA already enabled'], 400); 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); return response()->json(['message' => ctrans('texts.set_phone_for_two_factor')], 400);
} elseif (! $user->isVerified()) { } elseif (! $user->isVerified()) {
return response()->json(['message' => 'Please confirm your account first'], 400); return response()->json(['message' => 'Please confirm your account first'], 400);

View File

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

View File

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

View File

@ -15,7 +15,7 @@ class Cors
// ALLOW OPTIONS METHOD // ALLOW OPTIONS METHOD
$headers = [ $headers = [
'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE', '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); 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-Origin', '*');
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); $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('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-APP-VERSION', config('ninja.app_version'));
$response->headers->set('X-MINIMUM-CLIENT-VERSION', config('ninja.minimum_client_version')); $response->headers->set('X-MINIMUM-CLIENT-VERSION', config('ninja.minimum_client_version'));

View File

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

View File

@ -23,6 +23,7 @@ use Turbo124\Beacon\Facades\LightLogs;
*/ */
class QueryLogging class QueryLogging
{ {
/** /**
* Handle an incoming request. * Handle an incoming request.
* *
@ -33,25 +34,29 @@ class QueryLogging
*/ */
public function handle(Request $request, Closure $next) public function handle(Request $request, Closure $next)
{ {
// Enable query logging for development // Enable query logging for development
if (! Ninja::isHosted() || ! config('beacon.enabled')) { if (! Ninja::isHosted() || ! config('beacon.enabled')) {
return $next($request); return $next($request);
} }
$timeStart = microtime(true);
DB::enableQueryLog(); 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 // hide requests made by debugbar
if (strstr($request->url(), '_debugbar') === false) { if (strstr($request->url(), '_debugbar') === false) {
$queries = DB::getQueryLog(); $queries = DB::getQueryLog();
$count = count($queries); $count = count($queries);
$timeEnd = microtime(true); $timeEnd = microtime(true);
$time = $timeEnd - $timeStart; $time = $timeEnd - LARAVEL_START;
// nlog("Query count = {$count}");
// nlog($queries);
// nlog($request->url());
if ($count > 175) { if ($count > 175) {
nlog("Query count = {$count}"); nlog("Query count = {$count}");
@ -60,18 +65,17 @@ class QueryLogging
$ip = ''; $ip = '';
if (request()->hasHeader('Cf-Connecting-Ip')) { if ($request->hasHeader('Cf-Connecting-Ip')) {
$ip = request()->header('Cf-Connecting-Ip'); $ip = $request->header('Cf-Connecting-Ip');
} elseif (request()->hasHeader('X-Forwarded-For')) { } elseif ($request->hasHeader('X-Forwarded-For')) {
$ip = request()->header('Cf-Connecting-Ip'); $ip = $request->header('Cf-Connecting-Ip');
} else { } else {
$ip = request()->ip(); $ip = $request->ip();
} }
LightLogs::create(new DbQuery($request->method(), substr(urldecode($request->url()), 0, 180), $count, $time, $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 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() public function rules()
@ -59,14 +61,11 @@ class UpdateCompanyRequest extends Request
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) { if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
$rules['portal_domain'] = 'sometimes|url'; $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; return $rules;
} }
@ -83,6 +82,10 @@ class UpdateCompanyRequest extends Request
$input['settings'] = (array)$this->filterSaveableSettings($input['settings']); $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'])) { if(array_key_exists('e_invoice_certificate_passphrase', $input) && empty($input['e_invoice_certificate_passphrase'])) {
unset($input['e_invoice_certificate_passphrase']); unset($input['e_invoice_certificate_passphrase']);
} }

View File

@ -24,7 +24,8 @@ class BulkInvoiceRequest extends Request
{ {
return [ return [
'action' => 'required|string', '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_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = '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; return $rules;
} }

View File

@ -60,5 +60,10 @@ class StoreSchedulerRequest extends Request
$this->merge(['next_run_client' => $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']]); $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 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) public function passes($attribute, $value)
{ {
if (empty($input['subdomain'])) { if (empty($value)) {
return true; return true;
} }
return MultiDB::checkDomainAvailable($input['subdomain']); return MultiDB::checkDomainAvailable($value);
} }
/** /**

View File

@ -11,6 +11,7 @@
namespace App\Jobs\Client; namespace App\Jobs\Client;
use App\DataMapper\Tax\ZipTax\Response;
use App\Models\Client; use App\Models\Client;
use App\Models\Company; use App\Models\Company;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
@ -51,7 +52,7 @@ class UpdateTaxData implements ShouldQueue
{ {
MultiDB::setDb($this->company->db); MultiDB::setDb($this->company->db);
if(!config('services.tax.zip_tax.key')) if($this->company->account->isFreeHostedClient())
return; return;
$tax_provider = new \App\Services\Tax\Providers\TaxProvider($this->company, $this->client); $tax_provider = new \App\Services\Tax\Providers\TaxProvider($this->company, $this->client);
@ -63,8 +64,7 @@ class UpdateTaxData implements ShouldQueue
if (!$this->client->state && $this->client->postal_code) { if (!$this->client->state && $this->client->postal_code) {
$this->client->state = USStates::getState($this->client->postal_code); $this->client->state = USStates::getState($this->client->postal_code);
$this->client->saveQuietly();
$this->client->save();
} }
@ -73,11 +73,80 @@ class UpdateTaxData implements ShouldQueue
nlog("problem getting tax data => ".$e->getMessage()); 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() 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; namespace App\Jobs\Company;
use App\Models\Client;
use App\Models\Company; use App\Models\Company;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use App\Jobs\Client\UpdateTaxData; use App\DataProviders\USStates;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use App\DataMapper\Tax\ZipTax\Response;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use App\Services\Tax\Providers\TaxProvider; use App\Services\Tax\Providers\TaxProvider;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
@ -41,31 +41,50 @@ class CompanyTaxRate implements ShouldQueue
public function handle() public function handle()
{ {
if(!config('services.tax.zip_tax.key')) {
return;
}
MultiDB::setDB($this->company->db); MultiDB::setDB($this->company->db);
$tp = new TaxProvider($this->company); $tp = new TaxProvider($this->company);
$tp->updateCompanyTaxData(); $tp->updateCompanyTaxData();
$tp = null; if(!$tp->updatedTaxStatus() && $this->company->settings->country_id == '840') {
Client::query() $calculated_state = false;
->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(); /** 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();
}
} }
@ -74,4 +93,7 @@ class CompanyTaxRate implements ShouldQueue
return [new WithoutOverlapping($this->company->id)]; return [new WithoutOverlapping($this->company->id)];
} }
public function failed($e){
nlog($e->getMessage());
}
} }

View File

@ -152,12 +152,14 @@ class CreateCompany
$company->save(); $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 = TaxRateFactory::create($company->id, $user->id);
$tax_rate->name = $company->tax_data->regions->EU->subregions->ES->tax_name; // $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->rate = $company->tax_data->regions->EU->subregions->ES->tax_rate;
$tax_rate->save(); // $tax_rate->save();
return $company; return $company;
@ -191,18 +193,23 @@ class CreateCompany
$company->save(); $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 = TaxRateFactory::create($company->id, $user->id);
$tax_rate->rate = $company->tax_data->regions->AU->subregions->AU->tax_rate; // $tax_rate->name = $company->tax_data->regions->AU->subregions->AU->tax_name;
$tax_rate->save(); // $tax_rate->rate = $company->tax_data->regions->AU->subregions->AU->tax_rate;
// $tax_rate->save();
return $company; return $company;
} }
catch(\Exception $e){ 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(); $company->save();

View File

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

View File

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

View File

@ -144,12 +144,12 @@ class AdjustProductInventory implements ShouldQueue
private function notifyStocklevels(Product $product, string $notification_level) private function notifyStocklevels(Product $product, string $notification_level)
{ {
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new InventoryNotificationObject($product, $notification_level))->build());
$nmo->company = $this->company; $nmo->company = $this->company;
$nmo->settings = $this->company->settings; $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'])) { 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; $nmo->to_user = $cu->user;
NinjaMailerJob::dispatch($nmo); NinjaMailerJob::dispatch($nmo);
} }

View File

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

View File

@ -3,6 +3,8 @@
namespace App\Jobs\Invoice; namespace App\Jobs\Invoice;
use App\Jobs\Entity\CreateEntityPdf; use App\Jobs\Entity\CreateEntityPdf;
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
use App\Models\PurchaseOrder;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
@ -52,6 +54,11 @@ class InjectSignature implements ShouldQueue
$invitation->signature_base64 = $this->signature; $invitation->signature_base64 = $this->signature;
$invitation->save(); $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) { if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]); 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 = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer($mail_obj); $nmo->mailable = new NinjaMailer($mail_obj);

View File

@ -95,7 +95,6 @@ class QuoteCheckExpired implements ShouldQueue
private function queueExpiredQuoteNotification(Quote $quote) private function queueExpiredQuoteNotification(Quote $quote)
{ {
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new QuoteExpiredObject($quote, $quote->company))->build());
$nmo->company = $quote->company; $nmo->company = $quote->company;
$nmo->settings = $quote->company->settings; $nmo->settings = $quote->company->settings;
@ -108,6 +107,8 @@ class QuoteCheckExpired implements ShouldQueue
continue; continue;
} }
$nmo->mailable = new NinjaMailer((new QuoteExpiredObject($quote, $quote->company, $company_user->portalType()))->build());
/* Returns an array of notification methods */ /* 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']); $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 Uploadable;
use SavesDocuments; use SavesDocuments;
/** private string $file_path; //the file path - using a different JSON parser here.
* @var array
*/
private $file_path; //the file path - using a different JSON parser here.
/** /**
* @var Company * @var Company
@ -202,7 +199,11 @@ class Import implements ShouldQueue
nlog($this->company->id); nlog($this->company->id);
auth()->login($this->user, false); 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); $array = json_decode(file_get_contents($this->file_path), 1);
$data = $array['data']; $data = $array['data'];

View File

@ -11,26 +11,27 @@
namespace App\Jobs\Util; namespace App\Jobs\Util;
use App\Exceptions\ClientHostedMigrationException; use ZipArchive;
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 App\Models\User; use App\Models\User;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Models\Company;
use App\Libraries\MultiDB;
use App\Mail\MigrationFailed;
use Illuminate\Bus\Queueable; 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\App;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Cache;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage; 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 class StartMigration implements ShouldQueue
{ {
@ -79,6 +80,8 @@ class StartMigration implements ShouldQueue
{ {
nlog('Inside Migration Job'); nlog('Inside Migration Job');
Cache::put("migration-{$this->company->company_key}", "started", 86400);
set_time_limit(0); set_time_limit(0);
MultiDB::setDb($this->company->db); MultiDB::setDb($this->company->db);
@ -124,6 +127,8 @@ class StartMigration implements ShouldQueue
$this->company->update_products = $update_product_flag; $this->company->update_products = $update_product_flag;
$this->company->save(); $this->company->save();
Cache::put("migration-{$this->company->company_key}", "completed", 86400);
App::forgetInstance('translator'); App::forgetInstance('translator');
$t = app('translator'); $t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings)); $t->replace(Ninja::transformTranslations($this->company->settings));
@ -131,6 +136,8 @@ class StartMigration implements ShouldQueue
$this->company->update_products = $update_product_flag; $this->company->update_products = $update_product_flag;
$this->company->save(); $this->company->save();
Cache::put("migration-{$this->company->company_key}", "failed", 86400);
if (Ninja::isHosted()) { if (Ninja::isHosted()) {
app('sentry')->captureException($e); app('sentry')->captureException($e);
} }
@ -147,6 +154,9 @@ class StartMigration implements ShouldQueue
if (app()->environment() !== 'production') { if (app()->environment() !== 'production') {
info($e->getMessage()); info($e->getMessage());
} }
Storage::deleteDirectory(public_path("storage/migrations/{$filename}"));
} }
//always make sure we unset the migration as running //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']; 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 * @return array
*/ */
@ -55,10 +81,15 @@ class MultiDB
public static function checkDomainAvailable($subdomain) : bool public static function checkDomainAvailable($subdomain) : bool
{ {
if (! config('ninja.db.multi_db_enabled')) { if (! config('ninja.db.multi_db_enabled')) {
return Company::whereSubdomain($subdomain)->count() == 0; return Company::whereSubdomain($subdomain)->count() == 0;
} }
if (in_array($subdomain, self::$protected_domains)) {
return false;
}
$current_db = config('database.default'); $current_db = config('database.default');
foreach (self::$dbs as $db) { foreach (self::$dbs as $db) {

View File

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

View File

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

View File

@ -56,7 +56,7 @@ class CreditEmailedNotification implements ShouldQueue
unset($methods[$key]); unset($methods[$key]);
$nmo = new NinjaMailerObject; $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->company = $credit->company;
$nmo->settings = $credit->company->settings; $nmo->settings = $credit->company->settings;
$nmo->to_user = $user; $nmo->to_user = $user;

View File

@ -24,7 +24,7 @@ class InvoiceCreatedNotification implements ShouldQueue
{ {
use UserNotifies; use UserNotifies;
public $delay = 7; public $delay = 3;
public function __construct() public function __construct()
{ {
@ -64,7 +64,7 @@ class InvoiceCreatedNotification implements ShouldQueue
unset($methods[$key]); unset($methods[$key]);
$nmo = new NinjaMailerObject; $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->company = $invoice->company;
$nmo->settings = $invoice->company->settings; $nmo->settings = $invoice->company->settings;
$nmo->to_user = $user; $nmo->to_user = $user;

View File

@ -64,7 +64,7 @@ class InvoiceEmailedNotification implements ShouldQueue
unset($methods[$key]); unset($methods[$key]);
$nmo = new NinjaMailerObject; $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->company = $invoice->company;
$nmo->settings = $invoice->company->settings; $nmo->settings = $invoice->company->settings;
$nmo->to_user = $user; $nmo->to_user = $user;

View File

@ -49,7 +49,7 @@ class InvoiceFailedEmailNotification
unset($methods[$key]); unset($methods[$key]);
$nmo = new NinjaMailerObject; $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->company = $invoice->company->withoutRelations();
$nmo->settings = $invoice->company->settings; $nmo->settings = $invoice->company->settings;
$nmo->to_user = $user; $nmo->to_user = $user;

View File

@ -70,7 +70,7 @@ class InvitationViewedListener implements ShouldQueue
$nmo = new NinjaMailerObject; $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->company = $invitation->company;
$nmo->settings = $invitation->company->settings; $nmo->settings = $invitation->company->settings;

View File

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

View File

@ -59,7 +59,7 @@ class PurchaseOrderAcceptedListener implements ShouldQueue
unset($methods[$key]); unset($methods[$key]);
$nmo = new NinjaMailerObject; $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->company = $event->company;
$nmo->settings = $event->company->settings; $nmo->settings = $event->company->settings;

View File

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

View File

@ -62,7 +62,7 @@ class PurchaseOrderEmailedNotification implements ShouldQueue
unset($methods[$key]); unset($methods[$key]);
$nmo = new NinjaMailerObject; $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->company = $purchase_order->company;
$nmo->settings = $purchase_order->company->settings; $nmo->settings = $purchase_order->company->settings;
$nmo->to_user = $user; $nmo->to_user = $user;

View File

@ -61,7 +61,7 @@ class QuoteApprovedNotification implements ShouldQueue
unset($methods[$key]); unset($methods[$key]);
$nmo = new NinjaMailerObject; $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->company = $quote->company;
$nmo->settings = $quote->company->settings; $nmo->settings = $quote->company->settings;

View File

@ -54,6 +54,12 @@ class QuoteCreatedNotification implements ShouldQueue
continue; 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 */ /* This is only here to handle the alternate message channels - ie Slack */
// $notification = new EntitySentNotification($event->invitation, 'quote'); // $notification = new EntitySentNotification($event->invitation, 'quote');
@ -65,7 +71,7 @@ class QuoteCreatedNotification implements ShouldQueue
unset($methods[$key]); unset($methods[$key]);
$nmo = new NinjaMailerObject; $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->company = $quote->company;
$nmo->settings = $quote->company->settings; $nmo->settings = $quote->company->settings;

View File

@ -54,7 +54,7 @@ class QuoteEmailedNotification implements ShouldQueue
unset($methods[$key]); unset($methods[$key]);
$nmo = new NinjaMailerObject; $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->company = $quote->company;
$nmo->settings = $quote->company->settings; $nmo->settings = $quote->company->settings;
$nmo->to_user = $user; $nmo->to_user = $user;

View File

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

View File

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

View File

@ -33,10 +33,13 @@ class EntityCreatedObject
private $template_body; 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_type = $entity_type;
$this->entity = $entity; $this->entity = $entity;
$this->use_react_link = $use_react_link;
} }
/** /**
@ -81,7 +84,7 @@ class EntityCreatedObject
'purchase_order' => $this->entity->number, '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}"), 'button' => ctrans("texts.view_{$this->entity_type}"),
'signature' => $this->company->settings->email_signature, 'signature' => $this->company->settings->email_signature,
'logo' => $this->company->present()->logo(), 'logo' => $this->company->present()->logo(),
@ -165,7 +168,7 @@ class EntityCreatedObject
return [ return [
'title' => $this->getSubject(), 'title' => $this->getSubject(),
'message' => $this->getMessage(), '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}"), 'button' => ctrans("texts.view_{$this->entity_type}"),
'signature' => $settings->email_signature, 'signature' => $settings->email_signature,
'logo' => $this->company->present()->logo(), 'logo' => $this->company->present()->logo(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,15 +11,16 @@
namespace App\Mail\Engine; 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\Ninja;
use App\Utils\Number; use App\Utils\Number;
use App\Utils\Helpers;
use App\Models\Account;
use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesDates;
use App\Jobs\Entity\CreateRawPdf;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Storage;
use App\DataMapper\EmailTemplateDefaults;
class PaymentEmailEngine extends BaseEmailEngine 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