diff --git a/VERSION.txt b/VERSION.txt index 7a2d21ef5ad9..1b20908eee99 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.5.107 \ No newline at end of file +5.5.108 \ No newline at end of file diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index bf8481b1faaf..5c6aa4cc74e9 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -235,7 +235,7 @@ class BaseRule implements RuleInterface return $this; } 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_REDUCED_TAX => $this->taxReduced(), Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(), diff --git a/app/DataMapper/Tax/DE/Rule.php b/app/DataMapper/Tax/DE/Rule.php index 8a84e9933e45..8315a51792eb 100644 --- a/app/DataMapper/Tax/DE/Rule.php +++ b/app/DataMapper/Tax/DE/Rule.php @@ -66,7 +66,7 @@ class Rule extends BaseRule implements RuleInterface return $this->taxExempt(); } - match($product_tax_type){ + match(intval($product_tax_type)){ Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), Product::PRODUCT_TYPE_SERVICE => $this->taxService(), diff --git a/app/DataMapper/Tax/US/Rule.php b/app/DataMapper/Tax/US/Rule.php index 11b08ce41f81..854a689cae2a 100644 --- a/app/DataMapper/Tax/US/Rule.php +++ b/app/DataMapper/Tax/US/Rule.php @@ -57,7 +57,7 @@ class Rule extends BaseRule implements RuleInterface public function taxByType($product_tax_type): self { - match($product_tax_type) { + match(intval($product_tax_type)) { Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), Product::PRODUCT_TYPE_SERVICE => $this->taxService(), @@ -103,7 +103,7 @@ class Rule extends BaseRule implements RuleInterface */ public function taxService(): self { - if($this->tax_data->txbService == 'Y') { + if($this->tax_data?->txbService == 'Y') { $this->default(); } @@ -117,7 +117,7 @@ class Rule extends BaseRule implements RuleInterface */ public function taxShipping(): self { - if($this->tax_data->txbFreight == 'Y') { + if($this->tax_data?->txbFreight == 'Y') { $this->default(); } diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 3e0312462388..e44feb595b2d 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -668,7 +668,7 @@ class BaseController extends Controller * Passes back the miniloaded data response * * @param Builder $query - * @return void + * */ protected function timeConstrainedResponse($query) { diff --git a/app/Http/Controllers/ChartController.php b/app/Http/Controllers/ChartController.php index a2dc38f19ffa..52f6a5079a85 100644 --- a/app/Http/Controllers/ChartController.php +++ b/app/Http/Controllers/ChartController.php @@ -64,7 +64,6 @@ class ChartController extends BaseController * ), * ) * @param Request $request - * @return Response|mixed */ public function totals(ShowChartRequest $request) { diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index bf4b43b76f07..c4fc70f07334 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -574,7 +574,7 @@ class ClientController extends BaseController /** * Update the specified resource in storage. * - * @param UploadClientRequest $request + * @param PurgeClientRequest $request * @param Client $client * @return Response * @@ -644,7 +644,7 @@ class ClientController extends BaseController * * @param PurgeClientRequest $request * @param Client $client - * @param string $mergeable client hashed_id + * @param string $mergeable_client * @return Response * * diff --git a/app/Http/Controllers/ClientGatewayTokenController.php b/app/Http/Controllers/ClientGatewayTokenController.php index 75ea17883dea..33e9a1bd1221 100644 --- a/app/Http/Controllers/ClientGatewayTokenController.php +++ b/app/Http/Controllers/ClientGatewayTokenController.php @@ -46,20 +46,15 @@ class ClientGatewayTokenController extends BaseController protected $entity_transformer = ClientGatewayTokenTransformer::class; - /** - * @var ClientGatewayTokenRepository - */ - protected $client_gateway_token_gateway_token_repo; - /** * ClientGatewayTokenController constructor. - * @param ClientGatewayTokenRepository $client_gateway_token_gateway_token_repo + * @param ClientGatewayTokenRepository $client_gateway_token_repo */ - public function __construct(ClientGatewayTokenRepository $client_gateway_token_gateway_token_repo) + public function __construct(protected ClientGatewayTokenRepository $client_gateway_token_repo) { parent::__construct(); - $this->client_gateway_token_repo = $client_gateway_token_gateway_token_repo; + $this->client_gateway_token_repo = $client_gateway_token_repo; } /** @@ -69,8 +64,7 @@ class ClientGatewayTokenController extends BaseController * tags={"client_gateway_tokens"}, * summary="Gets a list of client_gateway_tokens", * description="Lists client_gateway_tokens, search and filters allow fine grained lists to be generated. - - Query parameters can be added to performed more fine grained filtering of the client_gateway_tokens, these are handled by the ClientGatewayTokenFilters class which defines the methods available", + * Query parameters can be added to performed more fine grained filtering of the client_gateway_tokens, these are handled by the ClientGatewayTokenFilters class which defines the methods available", * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"), * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), * @OA\Parameter(ref="#/components/parameters/include"), @@ -94,7 +88,7 @@ class ClientGatewayTokenController extends BaseController * @OA\JsonContent(ref="#/components/schemas/Error"), * ), * ) - * @param ClientGatewayTokenFilters $filters + * @param ListClientGatewayTokenRequest $request * @return Response|mixed */ public function index(ListClientGatewayTokenRequest $request) diff --git a/app/Http/Controllers/ClientPortal/ContactHashLoginController.php b/app/Http/Controllers/ClientPortal/ContactHashLoginController.php index 1f8579ba6f9d..4fb99e36ceba 100644 --- a/app/Http/Controllers/ClientPortal/ContactHashLoginController.php +++ b/app/Http/Controllers/ClientPortal/ContactHashLoginController.php @@ -11,17 +11,18 @@ namespace App\Http\Controllers\ClientPortal; -use App\Http\Controllers\Controller; -use App\Http\ViewComposers\PortalComposer; -use App\Models\RecurringInvoice; use Auth; +use App\Models\RecurringInvoice; +use App\Http\Controllers\Controller; +use Illuminate\Support\Facades\Redirect; +use App\Http\ViewComposers\PortalComposer; class ContactHashLoginController extends Controller { /** * Logs a user into the client portal using their contact_key * @param string $contact_key The contact key - * @return Auth|Redirect + * @return Redirect */ public function login(string $contact_key) { diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index d13445b8d057..351d23f36fb4 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -187,7 +187,6 @@ class InvoiceController extends Controller * * @param array $ids * - * @return void */ private function downloadInvoicePDF(array $ids) { diff --git a/app/Http/Controllers/ClientPortal/StatementController.php b/app/Http/Controllers/ClientPortal/StatementController.php index 50675869f1a3..0720e8d52a1d 100644 --- a/app/Http/Controllers/ClientPortal/StatementController.php +++ b/app/Http/Controllers/ClientPortal/StatementController.php @@ -39,7 +39,7 @@ class StatementController extends Controller public function raw(ShowStatementRequest $request) { $pdf = $request->client()->service()->statement( - $request->only(['start_date', 'end_date', 'show_payments_table', 'show_aging_table', 'status']) + $request->only(['start_date', 'end_date', 'show_payments_table', 'show_aging_table', 'show_credits_table', 'status']) ); if ($pdf && $request->query('download')) { diff --git a/app/Http/Controllers/ClientPortal/SubscriptionPlanSwitchController.php b/app/Http/Controllers/ClientPortal/SubscriptionPlanSwitchController.php index f06258104ea4..b616ee161746 100644 --- a/app/Http/Controllers/ClientPortal/SubscriptionPlanSwitchController.php +++ b/app/Http/Controllers/ClientPortal/SubscriptionPlanSwitchController.php @@ -24,7 +24,7 @@ class SubscriptionPlanSwitchController extends Controller * * @param ShowPlanSwitchRequest $request * @param RecurringInvoice $recurring_invoice - * @param string $target + * @param Subscription $target * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function index(ShowPlanSwitchRequest $request, RecurringInvoice $recurring_invoice, Subscription $target) diff --git a/app/Http/Controllers/ClientPortal/TempRouteController.php b/app/Http/Controllers/ClientPortal/TempRouteController.php index a9cb67cf3825..b1f71a837063 100644 --- a/app/Http/Controllers/ClientPortal/TempRouteController.php +++ b/app/Http/Controllers/ClientPortal/TempRouteController.php @@ -11,16 +11,17 @@ namespace App\Http\Controllers\ClientPortal; -use App\Http\Controllers\Controller; use Auth; +use App\Http\Controllers\Controller; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Redirect; class TempRouteController extends Controller { /** * Logs a user into the client portal using their contact_key - * @param string $contact_key The contact key - * @return Auth|Redirect + * @param string $hash The hash + * @return Redirect */ public function index(string $hash) { diff --git a/app/Http/Controllers/ClientStatementController.php b/app/Http/Controllers/ClientStatementController.php index 38f1577bb363..13d4a81ff06d 100644 --- a/app/Http/Controllers/ClientStatementController.php +++ b/app/Http/Controllers/ClientStatementController.php @@ -11,9 +11,10 @@ namespace App\Http\Controllers; -use App\Http\Requests\Statements\CreateStatementRequest; use App\Utils\Traits\MakesHash; use App\Utils\Traits\Pdf\PdfMaker; +use Illuminate\Support\Facades\Response; +use App\Http\Requests\Statements\CreateStatementRequest; class ClientStatementController extends BaseController { @@ -32,71 +33,6 @@ class ClientStatementController extends BaseController * * @param CreateStatementRequest $request * @return Response - * - * @OA\Post( - * path="/api/v1/client_statement", - * operationId="clientStatement", - * tags={"clients"}, - * summary="Return a PDF of the client statement", - * description="Return a PDF of the client statement", - * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\RequestBody( - * description="Statment Options", - * required=true, - * @OA\MediaType( - * mediaType="application/json", - * @OA\Schema( - * type="object", - * @OA\Property( - * property="start_date", - * description="The start date of the statement period - format Y-m-d", - * type="string", - * ), - * @OA\Property( - * property="end_date", - * description="The start date of the statement period - format Y-m-d", - * type="string", - * ), - * @OA\Property( - * property="client_id", - * description="The hashed ID of the client", - * type="string", - * ), - * @OA\Property( - * property="show_payments_table", - * description="Flag which determines if the payments table is shown", - * type="boolean", - * ), - * @OA\Property( - * property="show_aging_table", - * description="Flag which determines if the aging table is shown", - * type="boolean", - * ) - * ) - * ) - * ), - * @OA\Response( - * response=200, - * description="Returns the client object", - * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * @OA\JsonContent(ref="#/components/schemas/Client"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) */ public function statement(CreateStatementRequest $request) { diff --git a/app/Http/Controllers/CompanyController.php b/app/Http/Controllers/CompanyController.php index f65e970d7199..6b32a6bee2e4 100644 --- a/app/Http/Controllers/CompanyController.php +++ b/app/Http/Controllers/CompanyController.php @@ -603,7 +603,7 @@ class CompanyController extends BaseController * Update the specified resource in storage. * * @param UploadCompanyRequest $request - * @param Company $client + * @param Company $company * @return Response * * diff --git a/app/Http/Requests/ClientPortal/Statements/ShowStatementRequest.php b/app/Http/Requests/ClientPortal/Statements/ShowStatementRequest.php index c263248e83bb..01ad16230e82 100644 --- a/app/Http/Requests/ClientPortal/Statements/ShowStatementRequest.php +++ b/app/Http/Requests/ClientPortal/Statements/ShowStatementRequest.php @@ -39,6 +39,7 @@ class ShowStatementRequest extends FormRequest $this->merge([ 'show_payments_table' => $this->has('show_payments_table') ? \boolval($this->show_payments_table) : false, 'show_aging_table' => $this->has('show_aging_table') ? \boolval($this->show_aging_table) : false, + 'show_credits_table' => $this->has('show_credits_table') ? \boolval($this->show_credits_table) : false, ]); } diff --git a/app/Http/Requests/Statements/CreateStatementRequest.php b/app/Http/Requests/Statements/CreateStatementRequest.php index 9df2dee4e4d0..a2ac1204dabd 100644 --- a/app/Http/Requests/Statements/CreateStatementRequest.php +++ b/app/Http/Requests/Statements/CreateStatementRequest.php @@ -51,6 +51,7 @@ class CreateStatementRequest extends Request $this->merge([ 'show_payments_table' => $this->has('show_payments_table') ? \boolval($this->show_payments_table) : false, 'show_aging_table' => $this->has('show_aging_table') ? \boolval($this->show_aging_table) : false, + 'show_credits_table' => $this->has('show_credits_table') ? \boolval($this->show_credits_table) : false, ]); } diff --git a/app/Listeners/Account/CreateAccountActivity.php b/app/Listeners/Account/CreateAccountActivity.php index 3b3fcbed3e5e..7dc56fdb7179 100644 --- a/app/Listeners/Account/CreateAccountActivity.php +++ b/app/Listeners/Account/CreateAccountActivity.php @@ -22,7 +22,6 @@ class CreateAccountActivity implements ShouldQueue /** * Create the event listener. * - * @param ActivityRepository $activity_repo */ public function __construct() { diff --git a/app/Listeners/Document/DeleteCompanyDocuments.php b/app/Listeners/Document/DeleteCompanyDocuments.php index 541913a3bae0..8b8d5bcdbd58 100644 --- a/app/Listeners/Document/DeleteCompanyDocuments.php +++ b/app/Listeners/Document/DeleteCompanyDocuments.php @@ -4,7 +4,6 @@ namespace App\Listeners\Document; use App\Libraries\MultiDB; use App\Models\Document; -use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Facades\Storage; class DeleteCompanyDocuments @@ -31,10 +30,6 @@ class DeleteCompanyDocuments $path = sprintf('%s/%s', public_path('storage'), $event->company->company_key); - // Remove all files & folders, under company's path. - // This will delete directory itself, as well. - // In case we want to remove the content of folder, we should use $fs->cleanDirectory(); - //$filesystem = new Filesystem(); Storage::deleteDirectory($event->company->company_key); Document::whereCompanyId($event->company->id)->delete(); diff --git a/app/Models/Client.php b/app/Models/Client.php index 4f032d5d221d..fb130dbf8825 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -310,7 +310,6 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $system_logs * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @property int $has_valid_vat_number - * @property string $leitweg_id * @property-read \Illuminate\Database\Eloquent\Collection $activities * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger * @property-read \Illuminate\Database\Eloquent\Collection $contacts diff --git a/app/Models/Company.php b/app/Models/Company.php index df8d643bf0e2..e4bcfca7aa7b 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -18,7 +18,6 @@ use App\Utils\Ninja; use App\Utils\Traits\AppSetup; use App\Utils\Traits\CompanySettingsSaver; use App\Utils\Traits\MakesHash; -use App\Utils\Traits\ThrottlesEmail; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Notifications\Notification; @@ -785,7 +784,6 @@ class Company extends BaseModel use PresentableTrait; use MakesHash; use CompanySettingsSaver; - use ThrottlesEmail; use AppSetup; use \Awobaz\Compoships\Compoships; diff --git a/app/Services/Scheduler/EmailStatementService.php b/app/Services/Scheduler/EmailStatementService.php index 8080ef39281d..235e142938bb 100644 --- a/app/Services/Scheduler/EmailStatementService.php +++ b/app/Services/Scheduler/EmailStatementService.php @@ -66,8 +66,9 @@ class EmailStatementService return [ 'start_date' =>$start_end[0], 'end_date' =>$start_end[1], - 'show_payments_table' => $this->scheduler->parameters['show_payments_table'], - 'show_aging_table' => $this->scheduler->parameters['show_aging_table'], + 'show_payments_table' => $this->scheduler->parameters['show_payments_table'] ?? true, + 'show_aging_table' => $this->scheduler->parameters['show_aging_table'] ?? true, + 'show_credits_table' => $this->scheduler->parameters['show_credits_table'] ?? true, 'status' => $this->scheduler->parameters['status'] ]; } diff --git a/app/Utils/Traits/ThrottlesEmail.php b/app/Utils/Traits/ThrottlesEmail.php deleted file mode 100644 index 8bb2e7afe04e..000000000000 --- a/app/Utils/Traits/ThrottlesEmail.php +++ /dev/null @@ -1,72 +0,0 @@ -created_at->diffInMonths() * 100; - - return min($limit, 5000); - } - - public function isThrottled(Company $company) - { - $key = $company->company_key; - - // http://stackoverflow.com/questions/1375501/how-do-i-throttle-my-sites-api-users - $day = 60 * 60 * 24; - $day_limit = $this->getDailyEmailLimit($company); - $day_throttle = Cache::get("email_day_throttle:{$key}", null); - $last_api_request = Cache::get("last_email_request:{$key}", 0); - $last_api_diff = time() - $last_api_request; - - if (is_null($day_throttle)) { - $new_day_throttle = 0; - } else { - $new_day_throttle = $day_throttle - $last_api_diff; - $new_day_throttle = $new_day_throttle < 0 ? 0 : $new_day_throttle; - $new_day_throttle += $day / $day_limit; - $day_hits_remaining = floor(($day - $new_day_throttle) * $day_limit / $day); - $day_hits_remaining = $day_hits_remaining >= 0 ? $day_hits_remaining : 0; - } - - Cache::put("email_day_throttle:{$key}", $new_day_throttle, 60); - Cache::put("last_email_request:{$key}", time(), 60); - - if ($new_day_throttle > $day) { - $error_email = config('ninja.error_email'); - if ($error_email && ! Cache::get("throttle_notified:{$key}")) { - Mail::raw('Account Throttle: '.$company->company_key, function ($message) use ($error_email, $company) { - $message->to($error_email) - ->from(config('ninja.contact.email')) - ->subject('Email throttle triggered for company '.$company->id); - }); - } - Cache::put("throttle_notified:{$key}", true, 60 * 24); - - return true; - } - - return false; - } -} diff --git a/composer.json b/composer.json index 5df1d2c211cc..df88a3d8e6e9 100644 --- a/composer.json +++ b/composer.json @@ -108,6 +108,7 @@ "laravel/dusk": "^6.15", "mockery/mockery": "^1.4.4", "nunomaduro/collision": "^6.1", + "nunomaduro/larastan": "^2.0", "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^9.5.10", "spatie/laravel-ignition": "^1.0", diff --git a/composer.lock b/composer.lock index 0ee6552a5c38..442dcfbcd1b6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7482363bb2c3f9f8fb07bbd3d517597b", + "content-hash": "8c21eb3ea2c2baeecb223d5fdbc8423c", "packages": [ { "name": "adrienrn/php-mimetyper", @@ -15445,6 +15445,102 @@ ], "time": "2023-01-03T12:54:54+00:00" }, + { + "name": "nunomaduro/larastan", + "version": "v2.6.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/larastan.git", + "reference": "ccac5b25949576807862cf32ba1fce1769c06c42" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/larastan/zipball/ccac5b25949576807862cf32ba1fce1769c06c42", + "reference": "ccac5b25949576807862cf32ba1fce1769c06c42", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^9.47.0 || ^10.0.0", + "illuminate/container": "^9.47.0 || ^10.0.0", + "illuminate/contracts": "^9.47.0 || ^10.0.0", + "illuminate/database": "^9.47.0 || ^10.0.0", + "illuminate/http": "^9.47.0 || ^10.0.0", + "illuminate/pipeline": "^9.47.0 || ^10.0.0", + "illuminate/support": "^9.47.0 || ^10.0.0", + "php": "^8.0.2", + "phpmyadmin/sql-parser": "^5.6.0", + "phpstan/phpstan": "~1.10.6" + }, + "require-dev": { + "nikic/php-parser": "^4.15.2", + "orchestra/testbench": "^7.19.0 || ^8.0.0", + "phpunit/phpunit": "^9.5.27" + }, + "suggest": { + "orchestra/testbench": "Using Larastan for analysing a package needs Testbench" + }, + "type": "phpstan-extension", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "NunoMaduro\\Larastan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel", + "keywords": [ + "PHPStan", + "code analyse", + "code analysis", + "larastan", + "laravel", + "package", + "php", + "static analysis" + ], + "support": { + "issues": "https://github.com/nunomaduro/larastan/issues", + "source": "https://github.com/nunomaduro/larastan/tree/v2.6.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/canvural", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2023-04-20T12:40:01+00:00" + }, { "name": "openlss/lib-array2xml", "version": "1.0.0", @@ -15843,6 +15939,93 @@ }, "time": "2023-03-27T19:02:04+00:00" }, + { + "name": "phpmyadmin/sql-parser", + "version": "5.7.0", + "source": { + "type": "git", + "url": "https://github.com/phpmyadmin/sql-parser.git", + "reference": "0f5895aab2b6002d00b6831b60983523dea30bff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/0f5895aab2b6002d00b6831b60983523dea30bff", + "reference": "0f5895aab2b6002d00b6831b60983523dea30bff", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "phpmyadmin/motranslator": "<3.0" + }, + "require-dev": { + "phpbench/phpbench": "^1.1", + "phpmyadmin/coding-standard": "^3.0", + "phpmyadmin/motranslator": "^4.0 || ^5.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.9.12", + "phpstan/phpstan-phpunit": "^1.3.3", + "phpunit/php-code-coverage": "*", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "^0.16.1", + "vimeo/psalm": "^4.11", + "zumba/json-serializer": "^3.0" + }, + "suggest": { + "ext-mbstring": "For best performance", + "phpmyadmin/motranslator": "Translate messages to your favorite locale" + }, + "bin": [ + "bin/highlight-query", + "bin/lint-query", + "bin/tokenize-query" + ], + "type": "library", + "autoload": { + "psr-4": { + "PhpMyAdmin\\SqlParser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "The phpMyAdmin Team", + "email": "developers@phpmyadmin.net", + "homepage": "https://www.phpmyadmin.net/team/" + } + ], + "description": "A validating SQL lexer and parser with a focus on MySQL dialect.", + "homepage": "https://github.com/phpmyadmin/sql-parser", + "keywords": [ + "analysis", + "lexer", + "parser", + "query linter", + "sql", + "sql lexer", + "sql linter", + "sql parser", + "sql syntax highlighter", + "sql tokenizer" + ], + "support": { + "issues": "https://github.com/phpmyadmin/sql-parser/issues", + "source": "https://github.com/phpmyadmin/sql-parser" + }, + "funding": [ + { + "url": "https://www.phpmyadmin.net/donate/", + "type": "other" + } + ], + "time": "2023-01-25T10:43:40+00:00" + }, { "name": "phpstan/phpstan", "version": "1.10.13", diff --git a/config/ninja.php b/config/ninja.php index b7823016323c..df1a3dc957d0 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -15,8 +15,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.5.107', - 'app_tag' => '5.5.107', + 'app_version' => '5.5.108', + 'app_tag' => '5.5.108', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), diff --git a/phpstan.neon b/phpstan.neon index 0324dc8d0903..5a42eb355d68 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,4 +1,9 @@ +includes: + - ./vendor/nunomaduro/larastan/extension.neon + parameters: + ignoreErrors: + - '#Call to an undefined method .*badMethod\(\)#' level: 2 paths: - app diff --git a/tests/Unit/Tax/UsTaxTest.php b/tests/Unit/Tax/UsTaxTest.php index 5ed356723cd2..610b1b5218c0 100644 --- a/tests/Unit/Tax/UsTaxTest.php +++ b/tests/Unit/Tax/UsTaxTest.php @@ -147,6 +147,77 @@ class UsTaxTest extends TestCase return $invoice; } + public function testSameSubregionAndExemptProduct() + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all_subregions = true; + $tax_data->regions->EU->has_sales_above_threshold = true; + $tax_data->regions->EU->tax_all_subregions = true; + $tax_data->regions->EU->subregions->DE->tax_rate = 21; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 840, + 'postal_code' => '90210', + 'shipping_country_id' => 840, + 'shipping_postal_code' => '90210', + 'has_valid_vat_number' => false, + 'postal_code' => 'xx', + 'is_tax_exempt' => false, + ]); + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => 1, + 'user_id' => $this->user->id, + 'uses_inclusive_taxes' => false, + 'discount' => 0, + 'line_items' => [ + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '1', + 'tax_id' => Product::PRODUCT_TYPE_EXEMPT, + ], + ], + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(100, $invoice->amount); + + } + public function testForeignTaxesEnabledWithExemptProduct() { $settings = CompanySettings::defaults(); @@ -174,6 +245,7 @@ class UsTaxTest extends TestCase 'shipping_country_id' => 276, 'has_valid_vat_number' => false, 'postal_code' => 'xx', + 'is_tax_exempt' => false, ]); $invoice = Invoice::factory()->create([