Merge into preview

This commit is contained in:
David Bomba 2023-05-27 15:39:19 +10:00
commit 665b1525f8
548 changed files with 434952 additions and 407597 deletions

View File

@ -23,7 +23,7 @@ jobs:
- name: Copy .env file - name: Copy .env file
run: | run: |
cp .env.example .env cp .env.example .env
- name: Install composer dependencies - name: Install composer dependencies
run: | run: |
composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
@ -65,8 +65,9 @@ jobs:
- name: Build project - name: Build project
run: | run: |
zip -r ./invoiceninja.zip .* -x "../*" zip -r /home/runner/work/invoiceninja/invoiceninja.zip .* -x "../*"
shopt -s dotglob
tar --exclude='public/storage' --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar *
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
@ -74,4 +75,5 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
files: | files: |
invoiceninja.zip /home/runner/work/invoiceninja/invoiceninja.tar
/home/runner/work/invoiceninja/invoiceninja.zip

4
.gitignore vendored
View File

@ -37,4 +37,6 @@ public/test.pdf
public/storage/test.pdf public/storage/test.pdf
/Modules /Modules
_ide_helper_models.php _ide_helper_models.php
_ide_helper.php _ide_helper.php
/composer.phar
.tx/

View File

@ -1 +1 @@
5.5.104 5.5.122

View File

@ -0,0 +1,27 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class EncryptedCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes)
{
return strlen($value) > 1 ? decrypt($value) : null;
}
public function set($model, string $key, $value, array $attributes)
{
return [$key => ! is_null($value) ? encrypt($value) : null];
}
}

View File

@ -11,11 +11,12 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Libraries\MultiDB; use App\Utils\Ninja;
use App\Models\Backup; use App\Models\Backup;
use App\Models\Client; use App\Models\Client;
use App\Models\Company; use App\Models\Company;
use App\Models\Document; use App\Models\Document;
use App\Libraries\MultiDB;
use App\Models\GroupSetting; use App\Models\GroupSetting;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@ -55,6 +56,9 @@ class BackupUpdate extends Command
{ {
//always return state to first DB //always return state to first DB
if(Ninja::isSelfHost())
return;
$current_db = config('database.default'); $current_db = config('database.default');
if (! config('ninja.db.multi_db_enabled')) { if (! config('ninja.db.multi_db_enabled')) {

View File

@ -12,36 +12,37 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App; use App;
use Exception;
use App\Models\User;
use App\Utils\Ninja;
use App\Models\Quote;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Vendor;
use App\Models\Account;
use App\Models\Company;
use App\Models\Contact;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\CompanyUser;
use Illuminate\Support\Str;
use App\Models\CompanyToken;
use App\Models\ClientContact;
use App\Models\CompanyLedger;
use App\Models\PurchaseOrder;
use App\Models\VendorContact;
use App\Models\BankTransaction;
use App\Models\QuoteInvitation;
use Illuminate\Console\Command;
use App\Models\CreditInvitation;
use App\Models\InvoiceInvitation;
use App\DataMapper\ClientSettings; use App\DataMapper\ClientSettings;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use App\Factory\ClientContactFactory; use App\Factory\ClientContactFactory;
use App\Factory\VendorContactFactory; use App\Factory\VendorContactFactory;
use App\Jobs\Company\CreateCompanyToken; use App\Jobs\Company\CreateCompanyToken;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyLedger;
use App\Models\CompanyToken;
use App\Models\CompanyUser;
use App\Models\Contact;
use App\Models\Credit;
use App\Models\CreditInvitation;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\Payment;
use App\Models\PurchaseOrder;
use App\Models\Quote;
use App\Models\QuoteInvitation;
use App\Models\RecurringInvoiceInvitation; use App\Models\RecurringInvoiceInvitation;
use App\Models\User;
use App\Models\Vendor;
use App\Models\VendorContact;
use App\Utils\Ninja;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
/* /*
@ -81,7 +82,7 @@ class CheckData extends Command
/** /**
* @var string * @var string
*/ */
protected $signature = 'ninja:check-data {--database=} {--fix=} {--portal_url=} {--client_id=} {--vendor_id=} {--paid_to_date=} {--client_balance=} {--ledger_balance=} {--balance_status=}'; protected $signature = 'ninja:check-data {--database=} {--fix=} {--portal_url=} {--client_id=} {--vendor_id=} {--paid_to_date=} {--client_balance=} {--ledger_balance=} {--balance_status=} {--bank_transaction=}';
/** /**
* @var string * @var string
@ -136,6 +137,10 @@ class CheckData extends Command
$this->checkOAuth(); $this->checkOAuth();
} }
if($this->option('bank_transaction')) {
$this->fixBankTransactions();
}
$this->logMessage('Done: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE)); $this->logMessage('Done: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE));
$this->logMessage('Total execution time in seconds: ' . (microtime(true) - $time_start)); $this->logMessage('Total execution time in seconds: ' . (microtime(true) - $time_start));
@ -410,8 +415,8 @@ class CheckData extends Command
$invitation->company_id = $invoice->company_id; $invitation->company_id = $invoice->company_id;
$invitation->user_id = $invoice->user_id; $invitation->user_id = $invoice->user_id;
$invitation->invoice_id = $invoice->id; $invitation->invoice_id = $invoice->id;
$invitation->contact_id = ClientContact::whereClientId($invoice->client_id)->first()->id; $invitation->client_contact_id = ClientContact::whereClientId($invoice->client_id)->first()->id;
$invitation->invitation_key = Str::random(config('ninja.key_length')); $invitation->key = Str::random(config('ninja.key_length'));
$invitation->save(); $invitation->save();
} }
} }
@ -442,7 +447,7 @@ class CheckData extends Command
$contact_id = 'client_contact_id'; $contact_id = 'client_contact_id';
$contact_class = ClientContact::class; $contact_class = ClientContact::class;
$entity_key = \Illuminate\Support\Str::of(class_basename($entity))->snake()->append('_id')->value; $entity_key = \Illuminate\Support\Str::of(class_basename($entity))->snake()->append('_id')->toString();
$entity_obj = get_class($entity).'Invitation'; $entity_obj = get_class($entity).'Invitation';
if ($entity instanceof PurchaseOrder) { if ($entity instanceof PurchaseOrder) {
@ -1090,4 +1095,23 @@ class CheckData extends Command
} }
}); });
} }
public function fixBankTransactions()
{
$this->logMessage("checking bank transactions");
BankTransaction::with('payment')->withTrashed()->where('invoice_ids', ',,,,,,,,')->cursor()->each(function ($bt){
if($bt->payment->exists()) {
$bt->invoice_ids = collect($bt->payment->invoices)->pluck('hashed_id')->implode(',');
$bt->save();
$this->logMessage("Fixing - {$bt->id}");
}
});
}
} }

View File

@ -42,11 +42,6 @@ class CreateAccount extends Command
*/ */
protected $signature = 'ninja:create-account {--email=} {--password=}'; protected $signature = 'ninja:create-account {--email=} {--password=}';
/**
* Create a new command instance.
*
* @param InvoiceRepository $invoice_repo
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();

View File

@ -60,7 +60,7 @@ class DesignUpdate extends Command
foreach (MultiDB::$dbs as $db) { foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db); MultiDB::setDB($db);
$this->handleOnDb($db); $this->handleOnDb();
} }
MultiDB::setDB($current_db); MultiDB::setDB($current_db);

View File

@ -105,7 +105,7 @@ class HostedMigrations extends Command
Import::dispatch($import_file, $user->companies()->first(), $user); Import::dispatch($import_file, $user->companies()->first(), $user);
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) { } catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
\Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage())); \Mail::to($user)->send(new MigrationFailed($e, $company));
if (app()->environment() !== 'production') { if (app()->environment() !== 'production') {
info($e->getMessage()); info($e->getMessage());

View File

@ -44,7 +44,6 @@ class HostedUsers extends Command
/** /**
* Execute the console command. * Execute the console command.
* *
* @return int
*/ */
public function handle() public function handle()
{ {

View File

@ -107,7 +107,7 @@ class ImportMigrations extends Command
Import::dispatch($import_file, $this->getUser()->companies()->first(), $this->getUser()); Import::dispatch($import_file, $this->getUser()->companies()->first(), $this->getUser());
// StartMigration::dispatch($file->getRealPath(), $this->getUser(), $this->getUser()->companies()->first()); // StartMigration::dispatch($file->getRealPath(), $this->getUser(), $this->getUser()->companies()->first());
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) { } catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
\Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage())); \Mail::to($user)->send(new MigrationFailed($e, $company));
if (app()->environment() !== 'production') { if (app()->environment() !== 'production') {
info($e->getMessage()); info($e->getMessage());

View File

@ -93,6 +93,17 @@ class MobileLocalization extends Command
$text = str_replace(['<i>', '</i>'], '', $text); $text = str_replace(['<i>', '</i>'], '', $text);
$text = str_replace(['<strong>', '</strong>'], '', $text); $text = str_replace(['<strong>', '</strong>'], '', $text);
//replace the three lines above with this
// if($language->locale == 'ar') {
// $text = str_replace('\n', " ", $text);
// }
// $text = str_replace(['<strong>', '</strong>','<i>', '</i>','<b>', '</b>'], '', $text);
// $text = str_replace('"', "'", $text);
echo "'$key': '$text',\n"; echo "'$key': '$text',\n";
} }

View File

@ -12,7 +12,6 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use DirectoryIterator; use DirectoryIterator;
use Faker\Factory;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@ -44,8 +43,6 @@ class OpenApiYaml extends Command
*/ */
public function __construct() public function __construct()
{ {
$this->faker = Factory::create();
parent::__construct(); parent::__construct();
} }

View File

@ -47,7 +47,6 @@ class S3Cleanup extends Command
/** /**
* Execute the console command. * Execute the console command.
* *
* @return int
*/ */
public function handle() public function handle()
{ {

View File

@ -57,7 +57,6 @@ class SendRemindersCron extends Command
/** /**
* Execute the console command. * Execute the console command.
* *
* @return int
*/ */
public function handle() public function handle()
{ {
@ -175,6 +174,9 @@ class SendRemindersCron extends Command
$invoice->calc()->getInvoice()->save(); $invoice->calc()->getInvoice()->save();
$invoice->fresh(); $invoice->fresh();
$invoice->service()->deletePdf()->save(); $invoice->service()->deletePdf()->save();
if ($invoice->client->getSetting('enable_e_invoice')){
$invoice->service()->deleteEInvoice()->save();
}
/* Refresh the client here to ensure the balance is fresh */ /* Refresh the client here to ensure the balance is fresh */
$client = $invoice->client; $client = $invoice->client;

View File

@ -37,7 +37,7 @@ class AccountCreated extends GenericCounter
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class AccountDeleted extends GenericCounter
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class AccountPlatform extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class AccountSignup extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class BankAccountsCreated extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class DbQuery extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class EmailCount extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class EmailFailure extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class EmailInvoiceFailure extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class EmailSuccess extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class LivePreview extends GenericCounter
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class LoginFailure extends GenericCounter
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class LoginSuccess extends GenericCounter
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class EmailBounce extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class EmailSpam extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class MigrationFailure extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class QueueSize extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class SendRecurringFailure extends GenericMixedMetric
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class TrialFinished extends GenericCounter
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -37,7 +37,7 @@ class TrialStarted extends GenericCounter
* *
* date("Y-m-d H:i:s") * date("Y-m-d H:i:s")
* *
* @var DateTime * @var \DateTime
*/ */
public $datetime; public $datetime;

View File

@ -475,7 +475,16 @@ class CompanySettings extends BaseSettings
public $sync_invoice_quote_columns = true; public $sync_invoice_quote_columns = true;
public $e_invoice_type = 'EN16931';
public $default_expense_payment_type_id = '0';
public $enable_e_invoice = false;
public static $casts = [ public static $casts = [
'enable_e_invoice' => 'bool',
'default_expense_payment_type_id' => 'string',
'e_invoice_type' => 'string',
'mailgun_endpoint' => 'string', 'mailgun_endpoint' => 'string',
'client_initiated_payments' => 'bool', 'client_initiated_payments' => 'bool',
'client_initiated_payments_minimum' => 'float', 'client_initiated_payments_minimum' => 'float',
@ -962,6 +971,12 @@ class CompanySettings extends BaseSettings
'$method', '$method',
'$statement_amount', '$statement_amount',
], ],
'statement_credit_columns' => [
'$credit.number',
'$credit.date',
'$total',
'$credit.balance',
],
]; ];
return json_decode(json_encode($variables)); return json_decode(json_encode($variables));

View File

@ -12,8 +12,11 @@
namespace App\DataMapper\Tax; namespace App\DataMapper\Tax;
use App\Models\Client; use App\Models\Client;
use App\Models\Invoice;
use App\Models\Product; use App\Models\Product;
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
{ {
@ -101,9 +104,6 @@ class BaseRule implements RuleInterface
/** EU TAXES */ /** EU TAXES */
/** US TAXES */
/** US TAXES */
public string $tax_name1 = ''; public string $tax_name1 = '';
public float $tax_rate1 = 0; public float $tax_rate1 = 0;
@ -115,7 +115,11 @@ class BaseRule implements RuleInterface
protected ?Client $client; protected ?Client $client;
protected ?Response $tax_data; public ?Response $tax_data;
public mixed $invoice;
private bool $should_calc_tax = true;
public function __construct() public function __construct()
{ {
@ -125,39 +129,146 @@ class BaseRule implements RuleInterface
{ {
return $this; return $this;
} }
public function setClient(Client $client): self public function shouldCalcTax(): bool
{ {
$this->client = $client; return $this->should_calc_tax;
}
/**
* Initializes the tax rule for the entity.
*
* @param mixed $invoice
* @return self
*/
public function setEntity(mixed $invoice): self
{
$this->invoice = $invoice;
$this->client = $invoice->client;
$this->resolveRegions(); $this->resolveRegions();
if(!$this->isTaxableRegion())
return $this;
$this->configTaxData();
$this->tax_data = new Response($this->invoice->tax_data);
return $this; return $this;
} }
public function setTaxData(Response $tax_data): self /**
* Configigures the Tax Data for the entity
*
* @return self
*/
private function configTaxData(): self
{ {
$this->tax_data = $tax_data; /* We should only apply taxes for configured states */
if(!array_key_exists($this->client->country->iso_3166_2, $this->region_codes)) {
nlog('Automatic tax calculations not supported for this country - defaulting to company country');
nlog("With new logic, we should never see this");
}
/** Harvest the client_region */
/** If the tax data is already set and the invoice is marked as sent, do not adjust the rates */
if($this->invoice->tax_data && $this->invoice->status_id > 1)
return $this;
/**
* Origin - Company Tax Data
* Destination - Client Tax Data
*
*/
$tax_data = false;
if($this->seller_region == 'US' && $this->client_region == 'US'){
$company = $this->invoice->company;
/** If no company tax data has been configured, lets do that now. */
/** We should never encounter this scenario */
if(!$company->origin_tax_data)
{
$this->should_calc_tax = false;
return $this;
}
/** If we are in a Origin based state, force the company tax here */
if($company->origin_tax_data?->originDestination == 'O' && ($company->tax_data?->seller_subregion == $this->client_subregion)) {
$tax_data = $company->origin_tax_data;
}
else{
/** Ensures the client tax data has been updated */
// if(!$this->client->tax_data && \DB::transactionLevel() == 0) {
// $tp = new TaxProvider($company, $this->client);
// $tp->updateClientTaxData();
// $this->client->fresh();
// }
if($this->client->tax_data)
$tax_data = $this->client->tax_data;
}
}
/** Applies the tax data to the invoice */
if($this->invoice instanceof Invoice && $tax_data) {
$this->invoice->tax_data = $tax_data;
if(\DB::transactionLevel() == 0)
$this->invoice->saveQuietly();
}
return $this; return $this;
} }
// Refactor to support switching between shipping / billing country / region / subregion
/**
* Resolve Regions & Subregions
*
* @return self
*/
private function resolveRegions(): self private function resolveRegions(): self
{ {
if(!array_key_exists($this->client->country->iso_3166_2, $this->region_codes))
throw new \Exception('Automatic tax calculations not supported for this country');
$this->client_region = $this->region_codes[$this->client->country->iso_3166_2]; $this->client_region = $this->region_codes[$this->client->country->iso_3166_2];
match($this->client_region){ match($this->client_region){
'US' => $this->client_subregion = $this->tax_data->geoState, 'US' => $this->client_subregion = strlen($this->invoice?->client?->tax_data?->geoState) > 1 ? $this->invoice->client->tax_data->geoState : $this->getUSState(),
'EU' => $this->client_subregion = $this->client->country->iso_3166_2, 'EU' => $this->client_subregion = $this->client->country->iso_3166_2,
default => $this->client->country->iso_3166_2, 'AU' => $this->client_subregion = 'AU',
default => $this->client_subregion = $this->client->country->iso_3166_2,
}; };
return $this; return $this;
}
private function getUSState(): string
{
try {
$states = USStates::$states;
if(isset($states[$this->client->state]))
return $this->client->state;
return USStates::getState(strlen($this->client->postal_code) > 1 ? $this->client->postal_code : $this->client->shipping_postal_code);
} catch (\Exception $e) {
return $this->client->company->country()->iso_3166_2 == 'US' ? $this->client->company->tax_data->seller_subregion : 'CA';
}
} }
public function isTaxableRegion(): bool public function isTaxableRegion(): bool
@ -168,7 +279,7 @@ class BaseRule implements RuleInterface
public function defaultForeign(): self public function defaultForeign(): self
{ {
if($this->client_region == 'US') { if($this->client_region == 'US' && isset($this->tax_data?->taxSales)) {
$this->tax_rate1 = $this->tax_data->taxSales * 100; $this->tax_rate1 = $this->tax_data->taxSales * 100;
$this->tax_name1 = "{$this->tax_data->geoState} Sales Tax"; $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
@ -176,32 +287,41 @@ class BaseRule implements RuleInterface
return $this; return $this;
} }
elseif($this->client_region == 'AU'){ //these are defaults and are only stubbed out for now, for AU we can actually remove these
$this->tax_rate1 = $this->client->company->tax_data->regions->AU->subregions->AU->tax_rate;
$this->tax_name1 = $this->client->company->tax_data->regions->AU->subregions->AU->tax_name;
return $this;
}
$this->tax_rate1 = $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_rate; if(isset($this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion})) {
$this->tax_name1 = $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name; $this->tax_rate1 = $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_rate;
$this->tax_name1 = $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name;
}
return $this; return $this;
} }
public function tax($item = null): self public function tax($item = null): self
{ {
nlog($this->client_region);
nlog($this->seller_region);
if ($this->client->is_tax_exempt) { if ($this->client->is_tax_exempt) {
return $this->taxExempt();
return $this->taxExempt($item);
} elseif($this->client_region == $this->seller_region && $this->isTaxableRegion()) { } elseif($this->client_region == $this->seller_region && $this->isTaxableRegion()) {
$this->taxByType($item->tax_id); $this->taxByType($item);
return $this; return $this;
} elseif($this->isTaxableRegion()) { //other regions outside of US } elseif($this->isTaxableRegion()) { //other regions outside of US
match($item->tax_id) { match(intval($item->tax_id)) {
Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt($item),
Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(), Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced($item),
Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(), Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override($item),
default => $this->defaultForeign(), default => $this->defaultForeign(),
}; };
@ -215,42 +335,42 @@ class BaseRule implements RuleInterface
return $this; return $this;
} }
public function taxReduced(): self public function taxReduced($item): self
{ {
return $this; return $this;
} }
public function taxExempt(): self public function taxExempt($item): self
{ {
return $this; return $this;
} }
public function taxDigital(): self public function taxDigital($item): self
{ {
return $this; return $this;
} }
public function taxService(): self public function taxService($item): self
{ {
return $this; return $this;
} }
public function taxShipping(): self public function taxShipping($item): self
{ {
return $this; return $this;
} }
public function taxPhysical(): self public function taxPhysical($item): self
{ {
return $this; return $this;
} }
public function default(): self public function default($item): self
{ {
return $this; return $this;
} }
public function override(): self public function override($item): self
{ {
return $this; return $this;
} }
@ -259,4 +379,10 @@ class BaseRule implements RuleInterface
{ {
return $this; return $this;
} }
public function regionWithNoTaxCoverage(string $iso_3166_2): bool
{
return ! in_array($iso_3166_2, array_merge($this->eu_country_codes, array_keys($this->region_codes)));
}
} }

View File

@ -30,10 +30,10 @@ class Rule extends BaseRule implements RuleInterface
public bool $eu_business_tax_exempt = true; public bool $eu_business_tax_exempt = true;
/** @var bool $foreign_business_tax_exempt */ /** @var bool $foreign_business_tax_exempt */
public bool $foreign_business_tax_exempt = true; public bool $foreign_business_tax_exempt = false;
/** @var bool $foreign_consumer_tax_exempt */ /** @var bool $foreign_consumer_tax_exempt */
public bool $foreign_consumer_tax_exempt = true; public bool $foreign_consumer_tax_exempt = false;
/** @var float $tax_rate */ /** @var float $tax_rate */
public float $tax_rate = 0; public float $tax_rate = 0;
@ -56,25 +56,27 @@ class Rule extends BaseRule implements RuleInterface
/** /**
* Sets the correct tax rate based on the product type. * Sets the correct tax rate based on the product type.
* *
* @param mixed $product_tax_type * @param mixed $item
* @return self * @return self
*/ */
public function taxByType($product_tax_type): self public function taxByType($item): self
{ {
if ($this->client->is_tax_exempt) { if ($this->client->is_tax_exempt) {
return $this->taxExempt(); return $this->taxExempt($item);
} }
match($product_tax_type){ match(intval($item->tax_id)){
Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt($item),
Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital($item),
Product::PRODUCT_TYPE_SERVICE => $this->taxService(), Product::PRODUCT_TYPE_SERVICE => $this->taxService($item),
Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping($item),
Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical($item),
Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(), Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced($item),
Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(), Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override($item),
default => $this->default(), Product::PRODUCT_TYPE_ZERO_RATED => $this->zeroRated($item),
Product::PRODUCT_TYPE_REVERSE_TAX => $this->reverseTax($item),
default => $this->default($item),
}; };
return $this; return $this;
@ -85,7 +87,20 @@ class Rule extends BaseRule implements RuleInterface
* *
* @return self * @return self
*/ */
public function taxReduced(): self public function reverseTax($item): self
{
$this->tax_rate1 = 0;
$this->tax_name1 = 'ermäßigte MwSt.';
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_rate1 = $this->reduced_tax_rate;
$this->tax_name1 = 'ermäßigte MwSt.'; $this->tax_name1 = 'ermäßigte MwSt.';
@ -93,12 +108,26 @@ class Rule extends BaseRule implements RuleInterface
return $this; 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 = 'ermäßigte MwSt.';
return $this;
}
/** /**
* Calculates the tax rate for a tax exempt product * Calculates the tax rate for a tax exempt product
* *
* @return self * @return self
*/ */
public function taxExempt(): self public function taxExempt($item): self
{ {
$this->tax_name1 = ''; $this->tax_name1 = '';
$this->tax_rate1 = 0; $this->tax_rate1 = 0;
@ -111,8 +140,12 @@ class Rule extends BaseRule implements RuleInterface
* *
* @return self * @return self
*/ */
public function taxDigital(): self public function taxDigital($item): self
{ {
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this; return $this;
} }
@ -121,8 +154,12 @@ class Rule extends BaseRule implements RuleInterface
* *
* @return self * @return self
*/ */
public function taxService(): self public function taxService($item): self
{ {
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this; return $this;
} }
@ -131,8 +168,12 @@ class Rule extends BaseRule implements RuleInterface
* *
* @return self * @return self
*/ */
public function taxShipping(): self public function taxShipping($item): self
{ {
$this->tax_rate1 = $this->tax_rate;
$this->tax_name1 = 'MwSt.';
return $this; return $this;
} }
@ -141,7 +182,7 @@ class Rule extends BaseRule implements RuleInterface
* *
* @return self * @return self
*/ */
public function taxPhysical(): self public function taxPhysical($item): self
{ {
$this->tax_rate1 = $this->tax_rate; $this->tax_rate1 = $this->tax_rate;
@ -155,7 +196,7 @@ class Rule extends BaseRule implements RuleInterface
* *
* @return self * @return self
*/ */
public function default(): self public function default($item): self
{ {
$this->tax_name1 = ''; $this->tax_name1 = '';
@ -169,7 +210,7 @@ class Rule extends BaseRule implements RuleInterface
* *
* @return self * @return self
*/ */
public function override(): self public function override($item): self
{ {
return $this; return $this;
} }
@ -182,38 +223,42 @@ 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->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;
} }
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 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) 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

@ -15,25 +15,25 @@ interface RuleInterface
{ {
public function init(); public function init();
public function tax($item = null); public function tax($item);
public function taxByType($type); public function taxByType($type);
public function taxExempt(); public function taxExempt($item);
public function taxDigital(); public function taxDigital($item);
public function taxService(); public function taxService($item);
public function taxShipping(); public function taxShipping($item);
public function taxPhysical(); public function taxPhysical($item);
public function taxReduced(); public function taxReduced($item);
public function default(); public function default($item);
public function override(); public function override($item);
public function calculateRates(); public function calculateRates();
} }

View File

@ -14,19 +14,19 @@ namespace App\DataMapper\Tax;
class TaxModel class TaxModel
{ {
/** @var mixed $seller_subregion */ /** @var string $seller_subregion */
public string $seller_subregion = 'CA'; public string $seller_subregion = 'CA';
/** @var mixed $version */ /** @var string $version */
public string $version = 'alpha'; public string $version = 'alpha';
/** @var mixed $regions */ /** @var object $regions */
public object $regions; public object $regions;
/** /**
* __construct * __construct
* *
* @param mixed $model * @param TaxModel $model
* @return void * @return void
*/ */
public function __construct(public ?TaxModel $model = null) public function __construct(public ?TaxModel $model = null)
@ -42,9 +42,9 @@ class TaxModel
/** /**
* Initializes the rules and builds any required data. * Initializes the rules and builds any required data.
* *
* @return void * @return object
*/ */
public function init() public function init(): object
{ {
$this->regions = new \stdClass(); $this->regions = new \stdClass();
$this->regions->US = new \stdClass(); $this->regions->US = new \stdClass();
@ -68,7 +68,7 @@ class TaxModel
$this->regions->AU = new \stdClass(); $this->regions->AU = new \stdClass();
$this->regions->AU->has_sales_above_threshold = false; $this->regions->AU->has_sales_above_threshold = false;
$this->regions->AU->tax_all_subregions = false; $this->regions->AU->tax_all_subregions = false;
$this->regions->AU->vat_threshold = 75000; $this->regions->AU->tax_threshold = 75000;
$this->auSubRegions(); $this->auSubRegions();
return $this; return $this;
@ -115,7 +115,7 @@ class TaxModel
$this->regions->EU->has_sales_above_threshold = false; $this->regions->EU->has_sales_above_threshold = false;
$this->regions->EU->tax_all_subregions = false; $this->regions->EU->tax_all_subregions = false;
$this->regions->EU->vat_threshold = 10000; $this->regions->EU->tax_threshold = 10000;
$this->euSubRegions(); $this->euSubRegions();
return $this; return $this;
@ -381,6 +381,7 @@ class TaxModel
$this->regions->EU->subregions->DK = new \stdClass(); $this->regions->EU->subregions->DK = new \stdClass();
$this->regions->EU->subregions->DK->tax_rate = 25; $this->regions->EU->subregions->DK->tax_rate = 25;
$this->regions->EU->subregions->DK->tax_name = 'Moms';
$this->regions->EU->subregions->DK->reduced_tax_rate = 0; $this->regions->EU->subregions->DK->reduced_tax_rate = 0;
$this->regions->EU->subregions->DK->apply_tax = false; $this->regions->EU->subregions->DK->apply_tax = false;

View File

@ -41,31 +41,39 @@ class Rule extends BaseRule implements RuleInterface
/** /**
* Override tax class, we use this when we do not modify the input taxes * Override tax class, we use this when we do not modify the input taxes
* *
* @param mixed $item
* @return self * @return self
*/ */
public function override(): self public function override($item): self
{ {
$this->tax_rate1 = $item->tax_rate1;
$this->tax_name1 = $item->tax_name1;
return $this; return $this;
} }
/** /**
* Sets the correct tax rate based on the product type. * Sets the correct tax rate based on the product type.
* *
* @param mixed $product_tax_type * @param mixed $item
* @return self * @return self
*/ */
public function taxByType($product_tax_type): self public function taxByType($item): self
{ {
match($product_tax_type) { match(intval($item->tax_id)) {
Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt($item),
Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital($item),
Product::PRODUCT_TYPE_SERVICE => $this->taxService(), Product::PRODUCT_TYPE_SERVICE => $this->taxService($item),
Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping($item),
Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical($item),
Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(), Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced($item),
Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(), Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override($item),
default => $this->default(), Product::PRODUCT_TYPE_ZERO_RATED => $this->zeroRated($item),
default => $this->default($item),
}; };
return $this; return $this;
@ -73,10 +81,11 @@ class Rule extends BaseRule implements RuleInterface
/** /**
* Sets the tax as exempt (0) * Sets the tax as exempt (0)
* @param mixed $item
* *
* @return self * @return self
*/ */
public function taxExempt(): self public function taxExempt($item): self
{ {
$this->tax_name1 = ''; $this->tax_name1 = '';
$this->tax_rate1 = 0; $this->tax_rate1 = 0;
@ -86,25 +95,27 @@ class Rule extends BaseRule implements RuleInterface
/** /**
* Calculates the tax rate for a digital product * Calculates the tax rate for a digital product
* @param mixed $item
* *
* @return self * @return self
*/ */
public function taxDigital(): self public function taxDigital($item): self
{ {
$this->default(); $this->default($item);
return $this; return $this;
} }
/** /**
* Calculates the tax rate for a service product * Calculates the tax rate for a service product
* @param mixed $item
* *
* @return self * @return self
*/ */
public function taxService(): self public function taxService($item): self
{ {
if($this->tax_data->txbService == 'Y') { if(in_array($this->tax_data?->txbService,['Y','L'])) {
$this->default(); $this->default($item);
} }
return $this; return $this;
@ -112,26 +123,31 @@ class Rule extends BaseRule implements RuleInterface
/** /**
* Calculates the tax rate for a shipping product * Calculates the tax rate for a shipping product
* @param mixed $item
* *
* @return self * @return self
*/ */
public function taxShipping(): self public function taxShipping($item): self
{ {
if($this->tax_data->txbFreight == 'Y') { if($this->tax_data?->txbFreight == 'Y') {
$this->default(); return $this->default($item);
} }
$this->tax_rate1 = 0;
$this->tax_name1 = '';
return $this; return $this;
} }
/** /**
* Calculates the tax rate for a physical product * Calculates the tax rate for a physical product
* @param mixed $item
* *
* @return self * @return self
*/ */
public function taxPhysical(): self public function taxPhysical($item): self
{ {
$this->default(); $this->default($item);
return $this; return $this;
} }
@ -141,27 +157,59 @@ class Rule extends BaseRule implements RuleInterface
* *
* @return self * @return self
*/ */
public function default(): self public function default($item): self
{ {
if($this->tax_data?->stateSalesTax == 0) {
$this->tax_rate1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_rate;
$this->tax_name1 = "Sales Tax";
// $this->tax_name1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name;
return $this;
}
$this->tax_rate1 = $this->tax_data->taxSales * 100; $this->tax_rate1 = $this->tax_data->taxSales * 100;
$this->tax_name1 = "{$this->tax_data->geoState} Sales Tax"; // $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
$this->tax_name1 = "Sales Tax";
return $this; return $this;
} }
public function zeroRated($item): self
{
$this->tax_rate1 = 0;
$this->tax_name1 = "{$this->tax_data->geoState} Zero Rated Tax";
return $this;
}
/** /**
* Calculates the tax rate for a reduced tax product * Calculates the tax rate for a reduced tax product
* *
* @return self * @return self
*/ */
public function taxReduced(): self public function taxReduced($item): self
{ {
$this->default(); $this->default($item);
return $this; return $this;
} }
/**
* Calculates the tax rate for a reverse tax product
*
* @return self
*/
public function reverseTax($item): self
{
$this->default($item);
return $this;
}
/** /**
* Calculates the tax rates to be applied * Calculates the tax rates to be applied
* *
@ -171,4 +219,5 @@ class Rule extends BaseRule implements RuleInterface
{ {
return $this; return $this;
} }
} }

View File

@ -65,6 +65,7 @@ class Response
public string $geoCounty = ""; public string $geoCounty = "";
public string $geoState = ""; public string $geoState = "";
public float $taxSales = 0; public float $taxSales = 0;
public string $taxName = "";
public float $taxUse = 0; public float $taxUse = 0;
public string $txbService = ""; // N = No, Y = Yes public string $txbService = ""; // N = No, Y = Yes
public string $txbFreight = ""; // N = No, Y = Yes public string $txbFreight = ""; // N = No, Y = Yes
@ -73,6 +74,8 @@ class Response
public float $citySalesTax = 0; public float $citySalesTax = 0;
public float $cityUseTax = 0; public float $cityUseTax = 0;
public string $cityTaxCode = ""; public string $cityTaxCode = "";
/* US SPECIFIC TAX CODES */
public float $countySalesTax = 0; public float $countySalesTax = 0;
public float $countyUseTax = 0; public float $countyUseTax = 0;
public string $countyTaxCode = ""; public string $countyTaxCode = "";
@ -93,13 +96,19 @@ class Response
public string $district5Code = ""; public string $district5Code = "";
public float $district5SalesTax = 0; public float $district5SalesTax = 0;
public float $district5UseTax = 0; public float $district5UseTax = 0;
public string $originDestination = ""; /* US SPECIFIC TAX CODES */
public function __construct($data) public string $originDestination = ""; // defines if the client origin is the locale where the tax is remitted to
public function __construct($data = null)
{ {
foreach($data as $key => $value){ if($data) {
$this->{$key} = $value;
foreach($data as $key => $value) {
$this->{$key} = $value;
}
} }
} }

View File

@ -106,7 +106,7 @@ region:
apply_tax: false apply_tax: false
EU: EU:
tax_all: false tax_all: false
vat_threshold: 10000 tax_threshold: 10000
has_sales_above_threshold: false has_sales_above_threshold: false
subregions: subregions:
AT: AT:

View File

@ -12,9 +12,11 @@
namespace App\DataProviders; namespace App\DataProviders;
use Illuminate\Support\Facades\Http;
class USStates class USStates
{ {
protected static array $states = [ public static array $states = [
'AL' => 'Alabama', 'AL' => 'Alabama',
'AK' => 'Alaska', 'AK' => 'Alaska',
'AZ' => 'Arizona', 'AZ' => 'Arizona',
@ -33866,11 +33868,146 @@ class USStates
return self::$states; return self::$states;
} }
public static function getState(string $zip): string public static function getState(?string $zip = '90210'): string
{ {
if(isset(self::$zip_code_map[$zip])) if(isset(self::$zip_code_map[$zip]))
return self::$zip_code_map[$zip]; return self::$zip_code_map[$zip];
$prefix_state = self::getStateFromThreeDigitPrefix($zip);
if($prefix_state)
return $prefix_state;
$zippo_response = self::getStateFromZippo($zip);
if($zippo_response)
return $zippo_response;
throw new \Exception('Zip code not found'); throw new \Exception('Zip code not found');
} }
/*
{
"post code": "90210",
"country": "United States",
"country abbreviation": "US",
"places": [
{
"place name": "Beverly Hills",
"longitude": "-118.4065",
"state": "California",
"state abbreviation": "CA",
"latitude": "34.0901"
}
]
}
*/
public static function getStateFromZippo($zip): mixed
{
$response = Http::get("https://api.zippopotam.us/us/{$zip}");
if($response->failed())
return false;
$data = $response->object();
if(isset($data->places[0])) {
return $data->places[0]->{'state abbreviation'};
}
return false;
}
public static function getStateFromThreeDigitPrefix($zip): mixed
{
/* 000 to 999 */
$zip_by_state = [
'--', '--', '--', '--', '--', 'NY', 'PR', 'PR', 'VI', 'PR', 'MA', 'MA', 'MA',
'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA', 'MA',
'MA', 'MA', 'RI', 'RI', 'NH', 'NH', 'NH', 'NH', 'NH', 'NH', 'NH', 'NH', 'NH',
'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'ME', 'VT', 'VT',
'VT', 'VT', 'VT', 'MA', 'VT', 'VT', 'VT', 'VT', 'CT', 'CT', 'CT', 'CT', 'CT',
'CT', 'CT', 'CT', 'CT', 'CT', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ',
'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'AE',
'AE', 'AE', 'AE', 'AE', 'AE', 'AE', 'AE', 'AE', '--', 'NY', 'NY', 'NY', 'NY',
'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY',
'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY',
'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY',
'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'NY', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA',
'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA',
'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA',
'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', 'PA', '--', 'PA', 'PA',
'PA', 'PA', 'DE', 'DE', 'DE', 'DC', 'VA', 'DC', 'DC', 'DC', 'DC', 'MD', 'MD',
'MD', 'MD', 'MD', 'MD', 'MD', '--', 'MD', 'MD', 'MD', 'MD', 'MD', 'MD', 'VA',
'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA',
'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA', 'VA',
'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV',
'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', 'WV', '--', 'NC', 'NC', 'NC',
'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC', 'NC',
'NC', 'NC', 'NC', 'NC', 'SC', 'SC', 'SC', 'SC', 'SC', 'SC', 'SC', 'SC', 'SC',
'SC', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA',
'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'GA', 'FL', 'FL', 'FL', 'FL', 'FL',
'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL', 'FL',
'FL', 'FL', 'AA', 'FL', 'FL', '--', 'FL', '--', 'FL', 'FL', '--', 'FL', 'AL',
'AL', 'AL', '--', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN',
'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'TN', 'MS', 'MS', 'MS', 'MS',
'MS', 'MS', 'MS', 'MS', 'MS', 'MS', 'MS', 'MS', 'GA', '--', 'KY', 'KY', 'KY',
'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY',
'KY', 'KY', 'KY', '--', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', 'KY', '--',
'--', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH',
'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH', 'OH',
'OH', 'OH', 'OH', 'OH', '--', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN',
'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'IN', 'MI',
'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'MI',
'MI', 'MI', 'MI', 'MI', 'MI', 'MI', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA',
'IA', 'IA', '--', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', '--', '--', '--',
'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', 'IA', '--', 'WI', 'WI', 'WI',
'--', 'WI', 'WI', '--', 'WI', 'WI', 'WI', 'WI', 'WI', 'WI', 'WI', 'WI', 'WI',
'WI', 'WI', 'WI', 'WI', 'MN', 'MN', '--', 'MN', 'MN', 'MN', 'MN', 'MN', 'MN',
'MN', 'MN', 'MN', 'MN', 'MN', 'MN', 'MN', 'MN', 'MN', '--', 'DC', 'SD', 'SD',
'SD', 'SD', 'SD', 'SD', 'SD', 'SD', '--', '--', 'ND', 'ND', 'ND', 'ND', 'ND',
'ND', 'ND', 'ND', 'ND', '--', 'MT', 'MT', 'MT', 'MT', 'MT', 'MT', 'MT', 'MT',
'MT', 'MT', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL',
'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'IL', '--', 'IL', 'IL',
'IL', 'IL', 'IL', 'IL', 'IL', 'IL', 'MO', 'MO', '--', 'MO', 'MO', 'MO', 'MO',
'MO', 'MO', 'MO', 'MO', 'MO', '--', '--', 'MO', 'MO', 'MO', 'MO', 'MO', '--',
'MO', 'MO', 'MO', 'MO', 'MO', 'MO', 'MO', 'MO', 'MO', '--', 'KS', 'KS', 'KS',
'--', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS', 'KS',
'KS', 'KS', 'KS', 'KS', 'NE', 'NE', '--', 'NE', 'NE', 'NE', 'NE', 'NE', 'NE',
'NE', 'NE', 'NE', 'NE', 'NE', '--', '--', '--', '--', '--', '--', 'LA', 'LA',
'--', 'LA', 'LA', 'LA', 'LA', 'LA', 'LA', '--', 'LA', 'LA', 'LA', 'LA', 'LA',
'--', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR', 'AR',
'AR', 'AR', 'OK', 'OK', '--', 'TX', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK',
'OK', '--', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK', 'TX', 'TX', 'TX', 'TX',
'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX',
'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX',
'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX',
'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'TX', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO',
'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', 'CO', '--', '--',
'--', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY', 'WY',
'ID', 'ID', 'ID', 'ID', 'ID', 'ID', 'ID', '--', 'UT', 'UT', '--', 'UT', 'UT',
'UT', 'UT', 'UT', '--', '--', 'AZ', 'AZ', 'AZ', 'AZ', '--', 'AZ', 'AZ', 'AZ',
'--', 'AZ', 'AZ', '--', '--', 'AZ', 'AZ', 'AZ', '--', '--', '--', '--', 'NM',
'NM', '--', 'NM', 'NM', 'NM', '--', 'NM', 'NM', 'NM', 'NM', 'NM', 'NM', 'NM',
'NM', 'NM', '--', '--', '--', '--', 'NV', 'NV', '--', 'NV', 'NV', 'NV', '--',
'NV', 'NV', '--', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', '--',
'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA',
'CA', 'CA', 'CA', 'CA', 'CA', 'CA', '--', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA',
'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA',
'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA', 'CA',
'AP', 'AP', 'AP', 'AP', 'AP', 'HI', 'HI', 'GU', 'OR', 'OR', 'OR', 'OR', 'OR',
'OR', 'OR', 'OR', 'OR', 'OR', 'WA', 'WA', 'WA', 'WA', 'WA', 'WA', 'WA', '--',
'WA', 'WA', 'WA', 'WA', 'WA', 'WA', 'WA', 'AK', 'AK', 'AK', 'AK', 'AK'
];
$prefix = substr($zip, 0, 3);
$index = intval($prefix);
/* converts prefix to integer */
return $zip_by_state[$index] == "--" ? false : $zip_by_state[$index];
}
} }

View File

@ -24,9 +24,6 @@ class AccountCreated
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var
*/
public $user; public $user;
public $company; public $company;
@ -47,13 +44,14 @@ class AccountCreated
$this->event_vars = $event_vars; $this->event_vars = $event_vars;
} }
/** // /**
* Get the channels the event should broadcast on. // * Get the channels the event should broadcast on.
* // *
* @return Channel|array // * @return Channel|array
*/ // */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} // return new PrivateChannel('channel-name');
}
} }

View File

@ -49,13 +49,13 @@ class ClientWasArchived
$this->event_vars = $event_vars; $this->event_vars = $event_vars;
} }
/** // /**
* Get the channels the event should broadcast on. // * Get the channels the event should broadcast on.
* // *
* @return Channel|array // * @return Channel|array
*/ // */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -42,8 +42,8 @@ class CompanyDocumentsDeleted
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -25,9 +25,6 @@ class ContactLoggedIn
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var
*/
public $client_contact; public $client_contact;
public $company; public $company;
@ -53,8 +50,8 @@ class ContactLoggedIn
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -32,7 +32,7 @@ class CreditWasEmailed
/** /**
* Create a new event instance. * Create a new event instance.
* *
* @param Credit $credit * @param CreditInvitation $invitation
* @param Company $company * @param Company $company
* @param array $event_vars * @param array $event_vars
*/ */

View File

@ -23,7 +23,7 @@ class CreditWasRestored
use SerializesModels; use SerializesModels;
/** /**
* @var Client * @var Credit
*/ */
public $credit; public $credit;

View File

@ -26,29 +26,8 @@ class DesignWasArchived
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/** public function __construct(public Design $design, public Company $company, public array $event_vars)
* @var Design
*/
public $design;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Design $design
* @param Company $company
* @param array $event_vars
*/
public function __construct(Design $design, Company $company, array $event_vars)
{ {
$this->design = $design;
$this->company = $company;
$this->event_vars = $event_vars;
} }
/** /**
@ -56,8 +35,8 @@ class DesignWasArchived
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -11,9 +11,10 @@
namespace App\Events\Design; namespace App\Events\Design;
use App\Models\Company;
use App\Models\Design; use App\Models\Design;
use App\Models\Company;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
/** /**
* Class DesignWasCreated. * Class DesignWasCreated.
@ -22,29 +23,8 @@ class DesignWasCreated
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public Design $design, public Company $company, public array $event_vars)
* @var Design
*/
public $design;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Design $design
* @param Company $company
* @param array $event_vars
*/
public function __construct(Design $design, Company $company, array $event_vars)
{ {
$this->design = $design;
$this->company = $company;
$this->event_vars = $event_vars;
} }
/** /**
@ -52,8 +32,8 @@ class DesignWasCreated
* *
* @return PrivateChannel * @return PrivateChannel
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -11,9 +11,10 @@
namespace App\Events\Design; namespace App\Events\Design;
use App\Models\Company;
use App\Models\Design; use App\Models\Design;
use App\Models\Company;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
/** /**
* Class DesignWasDeleted. * Class DesignWasDeleted.
@ -22,38 +23,17 @@ class DesignWasDeleted
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public Design $design, public Company $company, public array $event_vars)
* @var Design
*/
public $design;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Design $design
* @param Company $company
* @param array $event_vars
*/
public function __construct(Design $design, Company $company, array $event_vars)
{ {
$this->design = $design;
$this->company = $company;
$this->event_vars = $event_vars;
} }
/** /**
* Get the channels the event should broadcast on. * Get the channels the event should broadcast on.
* *
* @return PrivateChannel * @return PrivateChannel
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -11,9 +11,10 @@
namespace App\Events\Design; namespace App\Events\Design;
use App\Models\Company;
use App\Models\Design; use App\Models\Design;
use App\Models\Company;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
/** /**
* Class DesignWasRestored. * Class DesignWasRestored.
@ -22,33 +23,8 @@ class DesignWasRestored
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public Design $design, public bool $fromDeleted, public Company $company, public array $event_vars)
* @var Design
*/
public $design;
public $company;
public $event_vars;
public $fromDeleted;
/**
* Create a new event instance.
*
* @param Design $design
* @param Company $company
* @param array $event_vars
*/
public function __construct(Design $design, $fromDeleted, Company $company, array $event_vars)
{ {
$this->design = $design;
$this->fromDeleted = $fromDeleted;
$this->company = $company;
$this->event_vars = $event_vars;
} }
/** /**
@ -56,8 +32,8 @@ class DesignWasRestored
* *
* @return PrivateChannel * @return PrivateChannel
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -11,9 +11,10 @@
namespace App\Events\Design; namespace App\Events\Design;
use App\Models\Company;
use App\Models\Design; use App\Models\Design;
use App\Models\Company;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
/** /**
* Class DesignWasUpdated. * Class DesignWasUpdated.
@ -22,29 +23,8 @@ class DesignWasUpdated
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public Design $design, public Company $company, public array $event_vars)
* @var Design
*/
public $design;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Design $design
* @param Company $company
* @param array $event_vars
*/
public function __construct(Design $design, Company $company, array $event_vars)
{ {
$this->design = $design;
$this->company = $company;
$this->event_vars = $event_vars;
} }
/** /**
@ -52,8 +32,8 @@ class DesignWasUpdated
* *
* @return PrivateChannel * @return PrivateChannel
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -56,6 +56,6 @@ class DocumentWasArchived
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -22,29 +22,7 @@ class InvoiceReminderWasEmailed
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public InvoiceInvitation $invitation, public Company $company, public array $event_vars, public string $template)
* @var Invoice
*/
public $invitation;
public $reminder;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param InvoiceInvitation $invitation
* @param Company $company
* @param array $event_vars
*/
public function __construct(InvoiceInvitation $invitation, Company $company, array $event_vars, string $reminder)
{ {
$this->invitation = $invitation;
$this->company = $company;
$this->event_vars = $event_vars;
$this->reminder = $reminder;
} }
} }

View File

@ -56,17 +56,17 @@ class InvoiceWasCreated implements ShouldBroadcast
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return ['simple-channel']; return [];
} }
/** // /**
* Get the data to broadcast. // * Get the data to broadcast.
* // *
* @return array<string, mixed> // * @return array<string, mixed>
*/ // */
public function broadcastWith(): array // public function broadcastWith(): array
{ // {
return ['id' => 'value']; // return ['id' => 'value'];
} // }
} }

View File

@ -23,7 +23,7 @@ class InvoiceWasEmailed
use SerializesModels; use SerializesModels;
/** /**
* @var Invoice * @var InvoiceInvitation
*/ */
public $invitation; public $invitation;

View File

@ -21,34 +21,7 @@ class InvoiceWasEmailedAndFailed
{ {
use SerializesModels; use SerializesModels;
public $invitation; public function __construct(public mixed $invitation, public Company $company, public string $message, public string $template, public array $event_vars)
public $message;
public $company;
public $event_vars;
public $template;
/**
* Create a new event instance.
*
* @param $invitation
* @param Company $company
* @param string $errors
* @param array $event_vars
*/
public function __construct($invitation, Company $company, string $message, string $template, array $event_vars)
{ {
$this->invitation = $invitation;
$this->company = $company;
$this->message = $message;
$this->event_vars = $event_vars;
$this->template = $template;
} }
} }

View File

@ -22,14 +22,6 @@ class InvoiceWasViewed
{ {
use SerializesModels; use SerializesModels;
/**
* @var Invoice
*/
public $invitation;
public $company;
public $event_vars;
/** /**
* Create a new event instance. * Create a new event instance.
@ -38,10 +30,7 @@ class InvoiceWasViewed
* @param Company $company * @param Company $company
* @param array $event_vars * @param array $event_vars
*/ */
public function __construct(InvoiceInvitation $invitation, Company $company, array $event_vars) public function __construct(public InvoiceInvitation $invitation, public Company $company, public array $event_vars)
{ {
$this->invitation = $invitation;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -26,7 +26,7 @@ class MethodDeleted
/** /**
* @var ClientGatewayToken * @var ClientGatewayToken
*/ */
private $payment_method; public $payment_method;
public $company; public $company;
@ -51,8 +51,8 @@ class MethodDeleted
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -19,26 +19,7 @@ class ProductWasArchived
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public Product $product, public Company $company, public array $event_vars)
* @var Product
*/
public $product;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Product $product
* @param Company $company
* @param array $event_vars
*/
public function __construct(Product $product, Company $company, array $event_vars)
{ {
$this->product = $product;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -19,30 +19,7 @@ class ProductWasCreated
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public Product $product, public mixed $input, public Company $company, public array $event_vars)
* @var Product
*/
public $product;
public $input;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Product $product
* @param $input
* @param Company $company
* @param array $event_vars
*/
public function __construct(Product $product, $input, Company $company, array $event_vars)
{ {
$this->product = $product;
$this->input = $input;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -19,26 +19,7 @@ class ProductWasDeleted
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public Product $product, public Company $company, public array $event_vars)
* @var Product
*/
public $product;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Product $product
* @param Company $company
* @param array $event_vars
*/
public function __construct(Product $product, Company $company, array $event_vars)
{ {
$this->product = $product;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -22,29 +22,7 @@ class ProductWasRestored
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public Product $product, public bool $fromDeleted, public Company $company, public array $event_vars)
* @var Product
*/
public $invoice;
public $company;
public $event_vars;
public $fromDeleted;
/**
* Create a new event instance.
*
* @param Product $invoice
* @param Company $company
* @param array $event_vars
*/
public function __construct(Product $product, $fromDeleted, Company $company, array $event_vars)
{ {
$this->product = $product;
$this->fromDeleted = $fromDeleted;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -11,6 +11,7 @@
namespace App\Events\Product; namespace App\Events\Product;
use App\Models\Company;
use App\Models\Product; use App\Models\Product;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
@ -18,26 +19,7 @@ class ProductWasUpdated
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public Product $product, public Company $company, public array $event_vars)
* @var Product
*/
public $product;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Product $product
* @param Company $company
* @param array $event_vars
*/
public function __construct(Product $product, Company $company, array $event_vars)
{ {
$this->product = $product;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -12,7 +12,6 @@
namespace App\Events\PurchaseOrder; namespace App\Events\PurchaseOrder;
use App\Models\Company; use App\Models\Company;
use App\Models\PurchaseOrder;
use App\Models\PurchaseOrderInvitation; use App\Models\PurchaseOrderInvitation;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
@ -23,26 +22,7 @@ class PurchaseOrderWasEmailed
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public PurchaseOrderInvitation $invitation, public Company $company, public array $event_vars)
* @var PurchaseOrder
*/
public $invitation;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param PurchaseOrder $purchase_order
* @param Company $company
* @param array $event_vars
*/
public function __construct(PurchaseOrderInvitation $invitation, Company $company, array $event_vars)
{ {
$this->invitation = $invitation;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -22,29 +22,7 @@ class PurchaseOrderWasRestored
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public PurchaseOrder $purchase_order, public bool $fromDeleted, public Company $company, public array $event_vars)
* @var PurchaseOrder
*/
public $purchase_order;
public $company;
public $event_vars;
public $fromDeleted;
/**
* Create a new event instance.
*
* @param PurchaseOrder $purchase_order
* @param Company $company
* @param array $event_vars
*/
public function __construct(PurchaseOrder $purchase_order, $fromDeleted, Company $company, array $event_vars)
{ {
$this->purchase_order = $purchase_order;
$this->fromDeleted = $fromDeleted;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -22,26 +22,7 @@ class PurchaseOrderWasUpdated
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public PurchaseOrder $purchase_order, public Company $company, public array $event_vars)
* @var PurchaseOrder
*/
public $purchase_order;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param PurchaseOrder $purchase_order
* @param Company $company
* @param array $event_vars
*/
public function __construct(PurchaseOrder $purchase_order, Company $company, array $event_vars)
{ {
$this->purchase_order = $purchase_order;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -23,26 +23,7 @@ class PurchaseOrderWasViewed
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public PurchaseOrderInvitation $invitation, public Company $company, public array $event_vars)
* @var PurchaseOrder
*/
public $invitation;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param PurchaseOrder $purchase_order
* @param Company $company
* @param array $event_vars
*/
public function __construct(PurchaseOrderInvitation $invitation, Company $company, array $event_vars)
{ {
$this->invitation = $invitation;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -23,27 +23,7 @@ class QuoteWasEmailed
{ {
use SerializesModels; use SerializesModels;
public $invitation; public function __construct(public QuoteInvitation $invitation, public Company $company, public array $event_vars, public string $template)
public $company;
public $event_vars;
public $template;
/**
* Create a new event instance.
*
* @param Quote $quote
* @param string $notes
* @param Company $company
* @param array $event_vars
*/
public function __construct(QuoteInvitation $invitation, Company $company, array $event_vars, string $template)
{ {
$this->invitation = $invitation;
$this->company = $company;
$this->event_vars = $event_vars;
$this->template = $template;
} }
} }

View File

@ -22,26 +22,7 @@ class RecurringInvoiceWasArchived
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public RecurringInvoice $recurring_invoice, public Company $company, public array $event_vars)
* @var Invoice
*/
public $recurring_invoice;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Invoice $recurring_invoice
* @param Company $company
* @param array $event_vars
*/
public function __construct(RecurringInvoice $recurring_invoice, Company $company, array $event_vars)
{ {
$this->recurring_invoice = $recurring_invoice;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -21,27 +21,8 @@ use Illuminate\Queue\SerializesModels;
class RecurringInvoiceWasDeleted class RecurringInvoiceWasDeleted
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public RecurringInvoice $recurring_invoice, public Company $company, public array $event_vars)
* @var RecurringInvoice
*/
public $recurring_invoice;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Invoice $invoice
* @param Company $company
* @param array $event_vars
*/
public function __construct(RecurringInvoice $recurring_invoice, Company $company, array $event_vars)
{ {
$this->recurring_invoice = $recurring_invoice;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -22,30 +22,7 @@ class RecurringInvoiceWasRestored
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public RecurringInvoice $recurring_invoice, public bool $fromDeleted, public Company $company, public array $event_vars)
* @var RecurringInvoice
*/
public $recurring_invoice;
public $fromDeleted;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Invoice $invoice
* @param $fromDeleted
* @param Company $company
* @param array $event_vars
*/
public function __construct(RecurringInvoice $recurring_invoice, $fromDeleted, Company $company, array $event_vars)
{ {
$this->recurring_invoice = $recurring_invoice;
$this->fromDeleted = $fromDeleted;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -24,26 +24,7 @@ class RecurringInvoiceWasUpdated
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/** public function __construct(public RecurringInvoice $recurring_invoice, public Company $company, public array $event_vars)
* @var Invoice
*/
public $recurring_invoice;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param RecurringInvoice $recurring_invoice
* @param Company $company
* @param array $event_vars
*/
public function __construct(RecurringInvoice $recurring_invoice, Company $company, array $event_vars)
{ {
$this->recurring_invoice = $recurring_invoice;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -23,7 +23,7 @@ class RecurringQuoteWasArchived
use SerializesModels; use SerializesModels;
/** /**
* @var Invoice * @var RecurringQuote
*/ */
public $recurring_quote; public $recurring_quote;
@ -34,7 +34,7 @@ class RecurringQuoteWasArchived
/** /**
* Create a new event instance. * Create a new event instance.
* *
* @param Invoice $recurring_quote * @param RecurringQuote $recurring_quote
* @param Company $company * @param Company $company
* @param array $event_vars * @param array $event_vars
*/ */

View File

@ -22,26 +22,7 @@ class RecurringQuoteWasDeleted
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public RecurringQuote $recurring_quote, public Company $company, public array $event_vars)
* @var RecurringQuote
*/
public $recurring_quote;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Invoice $invoice
* @param Company $company
* @param array $event_vars
*/
public function __construct(RecurringQuote $recurring_quote, Company $company, array $event_vars)
{ {
$this->recurring_quote = $recurring_quote;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -22,30 +22,7 @@ class RecurringQuoteWasRestored
{ {
use SerializesModels; use SerializesModels;
/** public function __construct(public RecurringQuote $recurring_quote, public bool $fromDeleted, public Company $company, public array $event_vars)
* @var RecurringQuote
*/
public $recurring_quote;
public $fromDeleted;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param Invoice $invoice
* @param $fromDeleted
* @param Company $company
* @param array $event_vars
*/
public function __construct(RecurringQuote $recurring_quote, $fromDeleted, Company $company, array $event_vars)
{ {
$this->recurring_quote = $recurring_quote;
$this->fromDeleted = $fromDeleted;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -24,26 +24,8 @@ class RecurringQuoteWasUpdated
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var Invoice
*/
public $recurring_quote;
public $company; public function __construct(public RecurringQuote $recurring_quote, public Company $company, public array $event_vars)
public $event_vars;
/**
* Create a new event instance.
*
* @param RecurringQuote $recurring_quote
* @param Company $company
* @param array $event_vars
*/
public function __construct(RecurringQuote $recurring_quote, Company $company, array $event_vars)
{ {
$this->recurring_quote = $recurring_quote;
$this->company = $company;
$this->event_vars = $event_vars;
} }
} }

View File

@ -45,8 +45,8 @@ class SubscriptionWasCreated
* *
* @return \Illuminate\Broadcasting\Channel|array * @return \Illuminate\Broadcasting\Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -26,27 +26,8 @@ class UserLoggedIn
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/** public function __construct(public User $user, public Company $company, public array $event_vars)
* @var
*/
public $user;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param User $user
* @param Company $company
* @param array $event_vars
*/
public function __construct(User $user, Company $company, array $event_vars)
{ {
$this->user = $user;
$this->company = $company;
$this->event_vars = $event_vars;
} }
/** /**
@ -54,8 +35,8 @@ class UserLoggedIn
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -26,30 +26,8 @@ class UserWasArchived
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/** public function __construct(public User $user, public User $creating_user, public Company $company, public array $event_vars)
* @var
*/
public $user;
public $creating_user;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param User $user
* @param Company $company
* @param array $event_vars
*/
public function __construct(User $user, User $creating_user, Company $company, array $event_vars)
{ {
$this->user = $user;
$this->creating_user = $creating_user;
$this->company = $company;
$this->event_vars = $event_vars;
} }
/** /**
@ -57,8 +35,8 @@ class UserWasArchived
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -26,30 +26,8 @@ class UserWasCreated
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/** public function __construct(public User $user, public User $creating_user, public Company $company, public array $event_vars)
* @var
*/
public $user;
public $creating_user;
public $company;
public $event_vars;
/**
* Create a new event instance.
*
* @param User $user
* @param Company $company
* @param array $event_vars
*/
public function __construct(User $user, User $creating_user, Company $company, array $event_vars)
{ {
$this->user = $user;
$this->creating_user = $creating_user;
$this->company = $company;
$this->event_vars = $event_vars;
} }
/** /**
@ -57,8 +35,8 @@ class UserWasCreated
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -26,9 +26,6 @@ class UserWasDeleted
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var
*/
public $user; public $user;
public $creating_user; public $creating_user;
@ -57,8 +54,8 @@ class UserWasDeleted
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -26,9 +26,6 @@ class UserWasRestored
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var
*/
public $user; public $user;
public $company; public $company;
@ -57,8 +54,8 @@ class UserWasRestored
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -26,9 +26,6 @@ class UserWasUpdated
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var
*/
public $user; public $user;
public $creating_user; public $creating_user;
@ -57,8 +54,8 @@ class UserWasUpdated
* *
* @return Channel|array * @return Channel|array
*/ */
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel('channel-name'); return [];
} }
} }

View File

@ -11,42 +11,44 @@
namespace App\Exceptions; namespace App\Exceptions;
use Throwable;
use PDOException;
use App\Utils\Ninja; use App\Utils\Ninja;
use Illuminate\Auth\Access\AuthorizationException; use Sentry\State\Scope;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use Sentry\Laravel\Integration;
use Illuminate\Support\Facades\Schema;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Auth\AuthenticationException; use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException; use League\Flysystem\UnableToCreateDirectory;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Queue\MaxAttemptsExceededException;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Symfony\Component\Process\Exception\RuntimeException;
use Illuminate\Database\Eloquent\RelationNotFoundException; use Illuminate\Database\Eloquent\RelationNotFoundException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Queue\MaxAttemptsExceededException;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Schema;
use Illuminate\Validation\ValidationException;
use League\Flysystem\UnableToCreateDirectory;
use PDOException;
use Sentry\Laravel\Integration;
use Sentry\State\Scope;
use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\CommandNotFoundException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Throwable; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
use InvalidArgumentException;
class Handler extends ExceptionHandler class Handler extends ExceptionHandler
{ {
/** /**
* A list of the exception types that are not reported. * A list of the exception types that are not reported.
* *
* @var array * @var array<int, class-string<Throwable>>
*/ */
protected $dontReport = [ protected $dontReport = [
PDOException::class, // PDOException::class,
MaxAttemptsExceededException::class, MaxAttemptsExceededException::class,
CommandNotFoundException::class, CommandNotFoundException::class,
ValidationException::class, ValidationException::class,
ModelNotFoundException::class, // ModelNotFoundException::class,
NotFoundHttpException::class, NotFoundHttpException::class,
]; ];
@ -59,10 +61,9 @@ class Handler extends ExceptionHandler
ModelNotFoundException::class, ModelNotFoundException::class,
NotFoundHttpException::class, NotFoundHttpException::class,
UnableToCreateDirectory::class, UnableToCreateDirectory::class,
GuzzleHttp\Exception\ConnectException::class, ConnectException::class,
Symfony\Component\Process\Exception\RuntimeException::class,
InvalidArgumentException::class,
RuntimeException::class, RuntimeException::class,
InvalidArgumentException::class,
Aws\Exception\CredentialsException::class, Aws\Exception\CredentialsException::class,
]; ];
@ -78,7 +79,7 @@ class Handler extends ExceptionHandler
/** /**
* A list of the inputs that are never flashed for validation exceptions. * A list of the inputs that are never flashed for validation exceptions.
* *
* @var array * @var array<1, string>
*/ */
protected $dontFlash = [ protected $dontFlash = [
'current_password', 'current_password',
@ -101,6 +102,11 @@ class Handler extends ExceptionHandler
} }
if (Ninja::isHosted()) { if (Ninja::isHosted()) {
if($exception instanceof ThrottleRequestsException && class_exists(\Modules\Admin\Events\ThrottledExceptionRaised::class)) {
event(new \Modules\Admin\Events\ThrottledExceptionRaised(auth()->user()->account->key));
}
Integration::configureScope(function (Scope $scope): void { Integration::configureScope(function (Scope $scope): void {
$name = 'hosted@invoiceninja.com'; $name = 'hosted@invoiceninja.com';
@ -201,7 +207,6 @@ class Handler extends ExceptionHandler
* *
* @param Request $request * @param Request $request
* @param Throwable $exception * @param Throwable $exception
* @return Response
* @throws Throwable * @throws Throwable
*/ */
public function render($request, Throwable $exception) public function render($request, Throwable $exception)

View File

@ -0,0 +1,121 @@
<?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\Export\CSV;
use App\Models\Task;
use App\Utils\Ninja;
use League\Csv\Writer;
use App\Models\Company;
use App\Models\Activity;
use App\Libraries\MultiDB;
use App\Models\DateFormat;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\App;
use App\Transformers\ActivityTransformer;
class ActivityExport extends BaseExport
{
private Company $company;
private $entity_transformer;
public string $date_key = 'created_at';
private string $date_format = 'YYYY-MM-DD';
public Writer $csv;
public array $entity_keys = [
'date' => 'date',
'activity' => 'activity',
'address' => 'address',
];
private array $decorate_keys = [
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
$this->input = $input;
$this->entity_transformer = new ActivityTransformer();
}
public function run()
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
$this->date_format = DateFormat::find($this->company->settings->date_format_id)->format;
//load the CSV document from a string
$this->csv = Writer::createFromString();
ksort($this->entity_keys);
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Activity::query()
->where('company_id', $this->company->id);
$query = $this->addDateRange($query);
$query->cursor()
->each(function ($entity) {
$this->buildRow($entity);
});
return $this->csv->toString();
}
private function buildRow(Activity $activity)
{
$this->csv->insertOne([
Carbon::parse($activity->created_at)->format($this->date_format),
ctrans("texts.activity_{$activity->activity_type_id}",[
'client' => $activity->client ? $activity->client->present()->name() : '',
'contact' => $activity->contact ? $activity->contact->present()->name() : '',
'quote' => $activity->quote ? $activity->quote->number : '',
'user' => $activity->user ? $activity->user->present()->name() : 'System',
'expense' => $activity->expense ? $activity->expense->number : '',
'invoice' => $activity->invoice ? $activity->invoice->number : '',
'recurring_invoice' => $activity->recurring_invoice ? $activity->recurring_invoice->number : '',
'payment' => $activity->payment ? $activity->payment->number : '',
'credit' => $activity->credit ? $activity->credit->number : '',
'task' => $activity->task ? $activity->task->number : '',
'vendor' => $activity->vendor ? $activity->vendor->present()->name() : '',
'purchase_order' => $activity->purchase_order ? $activity->purchase_order->number : '',
'subscription' => $activity->subscription ? $activity->subscription->name : '',
'vendor_contact' => $activity->vendor_contact ? $activity->vendor_contact->present()->name() : '',
'recurring_expense' => $activity->recurring_expense ? $activity->recurring_expense->number : '',
]),
$activity->ip,
]);
}
private function decorateAdvancedFields(Task $task, array $entity) :array
{
return $entity;
}
}

View File

@ -12,8 +12,10 @@
namespace App\Export\CSV; namespace App\Export\CSV;
use App\Models\Client; use App\Models\Client;
use App\Utils\Traits\MakesHash; use App\Models\Invoice;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Builder;
class BaseExport class BaseExport
{ {
@ -31,6 +33,8 @@ class BaseExport
public string $client_description = 'All Clients'; public string $client_description = 'All Clients';
public array $forced_keys = [];
protected function filterByClients($query) protected function filterByClients($query)
{ {
if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') { if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') {
@ -46,6 +50,60 @@ class BaseExport
return $query; return $query;
} }
protected function addInvoiceStatusFilter($query, $status): Builder
{
$status_parameters = explode(',', $status);
if(in_array('all', $status_parameters))
return $query;
$query->where(function ($nested) use ($status_parameters) {
$invoice_filters = [];
if (in_array('draft', $status_parameters)) {
$invoice_filters[] = Invoice::STATUS_DRAFT;
}
if (in_array('sent', $status_parameters)) {
$invoice_filters[] = Invoice::STATUS_SENT;
}
if (in_array('paid', $status_parameters)) {
$invoice_filters[] = Invoice::STATUS_PAID;
}
if (in_array('unpaid', $status_parameters)) {
$invoice_filters[] = Invoice::STATUS_SENT;
$invoice_filters[] = Invoice::STATUS_PARTIAL;
}
if (count($invoice_filters) > 0) {
$nested->whereIn('status_id', $invoice_filters);
}
if (in_array('overdue', $status_parameters)) {
$nested->orWhereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('due_date', '<', Carbon::now())
->orWhere('partial_due_date', '<', Carbon::now());
}
if(in_array('viewed', $status_parameters)){
$nested->whereHas('invitations', function ($q){
$q->whereNotNull('viewed_date')->whereNotNull('deleted_at');
});
}
});
return $query;
}
protected function addDateRange($query) protected function addDateRange($query)
{ {
$date_range = $this->input['date_range']; $date_range = $this->input['date_range'];
@ -114,7 +172,7 @@ class BaseExport
{ {
$header = []; $header = [];
foreach ($this->input['report_keys'] as $value) { foreach (array_merge($this->input['report_keys'], $this->forced_keys) as $value) {
$key = array_search($value, $this->entity_keys); $key = array_search($value, $this->entity_keys);
$key = str_replace('item.', '', $key); $key = str_replace('item.', '', $key);

View File

@ -81,6 +81,10 @@ class ClientExport extends BaseExport
'client.industry', 'client.industry',
]; ];
public array $forced_keys = [
'status',
];
public function __construct(Company $company, array $input) public function __construct(Company $company, array $input)
{ {
$this->company = $company; $this->company = $company;
@ -103,7 +107,7 @@ class ClientExport extends BaseExport
if (count($this->input['report_keys']) == 0) { if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys); $this->input['report_keys'] = array_values($this->entity_keys);
} }
//insert the header //insert the header
$this->csv->insertOne($this->buildHeader()); $this->csv->insertOne($this->buildHeader());

View File

@ -11,13 +11,15 @@
namespace App\Export\CSV; namespace App\Export\CSV;
use App\Libraries\MultiDB; use App\Utils\Ninja;
use App\Utils\Number;
use League\Csv\Writer;
use App\Models\Company; use App\Models\Company;
use App\Models\Invoice; use App\Models\Invoice;
use App\Transformers\InvoiceTransformer; use App\Libraries\MultiDB;
use App\Utils\Ninja; use App\Export\CSV\BaseExport;
use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\App;
use League\Csv\Writer; use App\Transformers\InvoiceTransformer;
class InvoiceExport extends BaseExport class InvoiceExport extends BaseExport
{ {
@ -63,6 +65,10 @@ class InvoiceExport extends BaseExport
'terms' => 'terms', 'terms' => 'terms',
'total_taxes' => 'total_taxes', 'total_taxes' => 'total_taxes',
'currency_id' => 'currency_id', 'currency_id' => 'currency_id',
'payment_number' => 'payment_number',
'payment_date' => 'payment_date',
'payment_amount' => 'payment_amount',
'method' => 'method',
]; ];
private array $decorate_keys = [ private array $decorate_keys = [
@ -107,6 +113,10 @@ class InvoiceExport extends BaseExport
$query = $this->addDateRange($query); $query = $this->addDateRange($query);
if(isset($this->input['status'])){
$query = $this->addInvoiceStatusFilter($query, $this->input['status']);
}
$query->cursor() $query->cursor()
->each(function ($invoice) { ->each(function ($invoice) {
$this->csv->insertOne($this->buildRow($invoice)); $this->csv->insertOne($this->buildRow($invoice));
@ -151,7 +161,17 @@ class InvoiceExport extends BaseExport
if (in_array('status_id', $this->input['report_keys'])) { if (in_array('status_id', $this->input['report_keys'])) {
$entity['status'] = $invoice->stringStatus($invoice->status_id); $entity['status'] = $invoice->stringStatus($invoice->status_id);
} }
$payment_exists = $invoice->payments()->exists();
$entity['payment_number'] = $payment_exists ? $invoice->payments()->pluck('number')->implode(',') : '';
$entity['payment_date'] = $payment_exists ? $invoice->payments()->pluck('date')->implode(',') : '';
$entity['payment_amount'] = $payment_exists ? Number::formatMoney($invoice->payments()->sum('paymentables.amount'), $invoice->company) : ctrans('texts.unpaid');
$entity['method'] = $payment_exists ? $invoice->payments()->first()->translatedType() : "";
return $entity; return $entity;
} }
} }

View File

@ -49,6 +49,7 @@ class PaymentExport extends BaseExport
'transaction_reference' => 'transaction_reference', 'transaction_reference' => 'transaction_reference',
'type' => 'type_id', 'type' => 'type_id',
'vendor' => 'vendor_id', 'vendor' => 'vendor_id',
'invoices' => 'invoices',
]; ];
private array $decorate_keys = [ private array $decorate_keys = [
@ -59,6 +60,7 @@ class PaymentExport extends BaseExport
'currency', 'currency',
'exchange_currency', 'exchange_currency',
'type', 'type',
'invoices',
]; ];
public function __construct(Company $company, array $input) public function __construct(Company $company, array $input)
@ -154,6 +156,8 @@ class PaymentExport extends BaseExport
$entity['gateway'] = $payment->gateway_type ? $payment->gateway_type->name : 'Unknown Type'; $entity['gateway'] = $payment->gateway_type ? $payment->gateway_type->name : 'Unknown Type';
} }
$entity['invoices'] = $payment->invoices()->exists() ? $payment->invoices->pluck('number')->implode(',') : '';
return $entity; return $entity;
} }
} }

View File

@ -120,9 +120,9 @@ class ProductExport extends BaseExport
$entity['vendor'] = $product->vendor()->exists() ? $product->vendor->name : ''; $entity['vendor'] = $product->vendor()->exists() ? $product->vendor->name : '';
} }
if (array_key_exists('project_id', $this->input['report_keys'])) { // if (array_key_exists('project_id', $this->input['report_keys'])) {
$entity['project'] = $product->project()->exists() ? $product->project->name : ''; // $entity['project'] = $product->project()->exists() ? $product->project->name : '';
} // }
return $entity; return $entity;
} }

View File

@ -229,7 +229,7 @@ class ProductSalesExport extends BaseExport
/** /**
* calculateTax * calculateTax
* *
* @param mixed $invoice * @param Invoice $invoice
* @param float $amount * @param float $amount
* @param float $tax_rate * @param float $tax_rate
* @return float * @return float
@ -250,7 +250,7 @@ class ProductSalesExport extends BaseExport
/** /**
* calculateDiscount * calculateDiscount
* *
* @param mixed $invoice * @param Invoice $invoice
* @param mixed $entity * @param mixed $entity
* @return float * @return float
*/ */

View File

@ -11,12 +11,13 @@
namespace App\Factory; namespace App\Factory;
use App\DataMapper\ClientRegistrationFields;
use App\DataMapper\CompanySettings;
use App\Libraries\MultiDB;
use App\Models\Company;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Models\Company;
use App\Libraries\MultiDB;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\DataMapper\Tax\TaxModel;
use App\DataMapper\CompanySettings;
use App\DataMapper\ClientRegistrationFields;
class CompanyFactory class CompanyFactory
{ {
@ -29,12 +30,10 @@ class CompanyFactory
public function create(int $account_id) :Company public function create(int $account_id) :Company
{ {
$company = new Company; $company = new Company;
// $company->name = '';
$company->account_id = $account_id; $company->account_id = $account_id;
$company->company_key = $this->createHash(); $company->company_key = $this->createHash();
$company->settings = CompanySettings::defaults(); $company->settings = CompanySettings::defaults();
$company->db = config('database.default'); $company->db = config('database.default');
//$company->custom_fields = (object) ['invoice1' => '1', 'invoice2' => '2', 'client1'=>'3'];
$company->custom_fields = (object) []; $company->custom_fields = (object) [];
$company->client_registration_fields = ClientRegistrationFields::generate(); $company->client_registration_fields = ClientRegistrationFields::generate();
@ -48,7 +47,8 @@ class CompanyFactory
$company->default_password_timeout = 1800000; $company->default_password_timeout = 1800000;
$company->markdown_email_enabled = true; $company->markdown_email_enabled = true;
$company->markdown_enabled = false; $company->markdown_enabled = false;
$company->tax_data = new TaxModel();
return $company; return $company;
} }
} }

View File

@ -40,7 +40,7 @@ class RecurringExpenseToExpenseFactory
$expense->tax_name3 = $recurring_expense->tax_name3; $expense->tax_name3 = $recurring_expense->tax_name3;
$expense->tax_rate3 = $recurring_expense->tax_rate3; $expense->tax_rate3 = $recurring_expense->tax_rate3;
$expense->date = now()->format('Y-m-d'); $expense->date = now()->format('Y-m-d');
$expense->payment_date = $recurring_expense->payment_date; $expense->payment_date = $recurring_expense->payment_date ?: now()->format('Y-m-d');
$expense->amount = $recurring_expense->amount; $expense->amount = $recurring_expense->amount;
$expense->foreign_amount = $recurring_expense->foreign_amount ?: 0; $expense->foreign_amount = $recurring_expense->foreign_amount ?: 0;

View File

@ -57,5 +57,6 @@ class RecurringInvoiceFactory
$invoice->auto_bill = 'off'; $invoice->auto_bill = 'off';
return $invoice; return $invoice;
} }
} }

View File

@ -36,7 +36,7 @@ class BankIntegrationFilters extends QueryFilters
/** /**
* Filter based on search text. * Filter based on search text.
* *
* @param string query filter * @param string $filter
* @return Builder * @return Builder
* @deprecated * @deprecated
*/ */
@ -55,7 +55,7 @@ class BankIntegrationFilters extends QueryFilters
* Filters the list based on the status * Filters the list based on the status
* archived, active, deleted. * archived, active, deleted.
* *
* @param string filter * @param string $filter
* @return Builder * @return Builder
*/ */
public function status(string $filter = ''): Builder public function status(string $filter = ''): Builder
@ -86,7 +86,7 @@ class BankIntegrationFilters extends QueryFilters
/** /**
* Sorts the list based on $sort. * Sorts the list based on $sort.
* *
* @param string sort formatted as column|asc * @param string $sort formatted as column|asc
* @return Builder * @return Builder
*/ */
public function sort(string $sort = ''): Builder public function sort(string $sort = ''): Builder
@ -103,7 +103,7 @@ class BankIntegrationFilters extends QueryFilters
/** /**
* Filters the query by the users company ID. * Filters the query by the users company ID.
* *
* @return Illuminate\Database\Query\Builder * @return Builder
*/ */
public function entityFilter(): Builder public function entityFilter(): Builder
{ {

View File

@ -18,6 +18,7 @@ use Illuminate\Database\Eloquent\Builder;
*/ */
class ClientFilters extends QueryFilters class ClientFilters extends QueryFilters
{ {
/** /**
* Filter by name. * Filter by name.
* *
@ -53,7 +54,7 @@ class ClientFilters extends QueryFilters
/** /**
* Filter between balances. * Filter between balances.
* *
* @param string balance * @param string $balance
* @return Builder * @return Builder
*/ */
public function between_balance(string $balance = ''): Builder public function between_balance(string $balance = ''): Builder
@ -108,7 +109,7 @@ class ClientFilters extends QueryFilters
/** /**
* Filter based on search text. * Filter based on search text.
* *
* @param string query filter * @param string $filter
* @return Builder * @return Builder
* @deprecated * @deprecated
*/ */
@ -136,7 +137,7 @@ class ClientFilters extends QueryFilters
/** /**
* Sorts the list based on $sort. * Sorts the list based on $sort.
* *
* @param string sort formatted as column|asc * @param string $sort formatted as column|asc
* @return Builder * @return Builder
*/ */
public function sort(string $sort = ''): Builder public function sort(string $sort = ''): Builder
@ -157,9 +158,9 @@ class ClientFilters extends QueryFilters
/** /**
* Filters the query by the users company ID. * Filters the query by the users company ID.
* *
* @return Illuminate\Database\Query\Builder * @return Builder
*/ */
public function entityFilter() public function entityFilter(): Builder
{ {
return $this->builder->company(); return $this->builder->company();
} }

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