diff --git a/app/Console/Commands/CreateSingleAccount.php b/app/Console/Commands/CreateSingleAccount.php index 626b1d29249e..9d83e6b8f086 100644 --- a/app/Console/Commands/CreateSingleAccount.php +++ b/app/Console/Commands/CreateSingleAccount.php @@ -711,6 +711,29 @@ class CreateSingleAccount extends Command $cg->save(); } + if (config('ninja.testvars.paypal_rest') && ($this->gateway == 'all' || $this->gateway == 'paypal_rest')) { + $cg = new CompanyGateway; + $cg->company_id = $company->id; + $cg->user_id = $user->id; + $cg->gateway_key = '80af24a6a691230bbec33e930ab40665'; + $cg->require_cvv = true; + $cg->require_billing_address = true; + $cg->require_shipping_address = true; + $cg->update_details = true; + $cg->config = encrypt(config('ninja.testvars.paypal_rest')); + $cg->save(); + + $gateway_types = $cg->driver()->gatewayTypes(); + + $fees_and_limits = new stdClass; + $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits; + + $cg->fees_and_limits = $fees_and_limits; + $cg->save(); + } + + + if (config('ninja.testvars.checkout') && ($this->gateway == 'all' || $this->gateway == 'checkout')) { $cg = new CompanyGateway; $cg->company_id = $company->id; diff --git a/app/DataMapper/Analytics/AccountCreated.php b/app/DataMapper/Analytics/AccountCreated.php index 9f3b5c5d6e82..0b35a2ed1455 100644 --- a/app/DataMapper/Analytics/AccountCreated.php +++ b/app/DataMapper/Analytics/AccountCreated.php @@ -37,7 +37,6 @@ class AccountCreated extends GenericCounter * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; diff --git a/app/DataMapper/Analytics/AccountDeleted.php b/app/DataMapper/Analytics/AccountDeleted.php index fba503ed543b..ed6166916ae5 100644 --- a/app/DataMapper/Analytics/AccountDeleted.php +++ b/app/DataMapper/Analytics/AccountDeleted.php @@ -37,7 +37,6 @@ class AccountDeleted extends GenericCounter * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; diff --git a/app/DataMapper/Analytics/AccountPlatform.php b/app/DataMapper/Analytics/AccountPlatform.php index db3e785767d3..7a8ce7a3f1a7 100644 --- a/app/DataMapper/Analytics/AccountPlatform.php +++ b/app/DataMapper/Analytics/AccountPlatform.php @@ -37,7 +37,6 @@ class AccountPlatform extends GenericMixedMetric * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; diff --git a/app/DataMapper/Analytics/AccountSignup.php b/app/DataMapper/Analytics/AccountSignup.php index 49baf78a9bca..9f71612a693a 100644 --- a/app/DataMapper/Analytics/AccountSignup.php +++ b/app/DataMapper/Analytics/AccountSignup.php @@ -37,7 +37,6 @@ class AccountSignup extends GenericMixedMetric * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; @@ -61,7 +60,7 @@ class AccountSignup extends GenericMixedMetric * The counter * set to 1. * - * @var string + * @var int */ public $int_metric1 = 1; diff --git a/app/DataMapper/Analytics/BankAccountsCreated.php b/app/DataMapper/Analytics/BankAccountsCreated.php index 18d05e3a145e..eab024ce6631 100644 --- a/app/DataMapper/Analytics/BankAccountsCreated.php +++ b/app/DataMapper/Analytics/BankAccountsCreated.php @@ -37,7 +37,6 @@ class BankAccountsCreated extends GenericMixedMetric * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; diff --git a/app/DataMapper/Analytics/DbQuery.php b/app/DataMapper/Analytics/DbQuery.php index 6e9ac09f88d5..39c69f91b6cc 100644 --- a/app/DataMapper/Analytics/DbQuery.php +++ b/app/DataMapper/Analytics/DbQuery.php @@ -37,7 +37,6 @@ class DbQuery extends GenericMixedMetric * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; @@ -57,7 +56,7 @@ class DbQuery extends GenericMixedMetric * The counter * set to 1. * - * @var string + * @var int */ public $int_metric1 = 1; diff --git a/app/DataMapper/Analytics/EmailCount.php b/app/DataMapper/Analytics/EmailCount.php index ec41dbb4d5ba..249126c5a6ad 100644 --- a/app/DataMapper/Analytics/EmailCount.php +++ b/app/DataMapper/Analytics/EmailCount.php @@ -37,7 +37,6 @@ class EmailCount extends GenericMixedMetric * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; diff --git a/app/DataMapper/Analytics/EmailFailure.php b/app/DataMapper/Analytics/EmailFailure.php index bd31ac13e43a..a95957a0ec29 100644 --- a/app/DataMapper/Analytics/EmailFailure.php +++ b/app/DataMapper/Analytics/EmailFailure.php @@ -37,7 +37,6 @@ class EmailFailure extends GenericMixedMetric * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; @@ -61,7 +60,6 @@ class EmailFailure extends GenericMixedMetric * The counter * set to 1. * - * @var string */ public $int_metric1 = 1; diff --git a/app/DataMapper/Analytics/EmailInvoiceFailure.php b/app/DataMapper/Analytics/EmailInvoiceFailure.php index 058f8c7cfea5..77f7a4a29fa6 100644 --- a/app/DataMapper/Analytics/EmailInvoiceFailure.php +++ b/app/DataMapper/Analytics/EmailInvoiceFailure.php @@ -37,7 +37,6 @@ class EmailInvoiceFailure extends GenericMixedMetric * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; diff --git a/app/DataMapper/Analytics/EmailSuccess.php b/app/DataMapper/Analytics/EmailSuccess.php index 94ddf34ecdb4..a84514a68503 100644 --- a/app/DataMapper/Analytics/EmailSuccess.php +++ b/app/DataMapper/Analytics/EmailSuccess.php @@ -37,7 +37,6 @@ class EmailSuccess extends GenericMixedMetric * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; @@ -61,7 +60,6 @@ class EmailSuccess extends GenericMixedMetric * The counter * set to 1. * - * @var string */ public $int_metric1 = 1; diff --git a/app/DataMapper/Analytics/LivePreview.php b/app/DataMapper/Analytics/LivePreview.php index 7cf062b75c71..19833fa7eaf6 100644 --- a/app/DataMapper/Analytics/LivePreview.php +++ b/app/DataMapper/Analytics/LivePreview.php @@ -37,7 +37,6 @@ class LivePreview extends GenericCounter * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; diff --git a/app/DataMapper/Analytics/LoginFailure.php b/app/DataMapper/Analytics/LoginFailure.php index 954c1edb1fec..641bdc3e6ea7 100644 --- a/app/DataMapper/Analytics/LoginFailure.php +++ b/app/DataMapper/Analytics/LoginFailure.php @@ -37,7 +37,6 @@ class LoginFailure extends GenericCounter * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; diff --git a/app/DataMapper/Analytics/LoginSuccess.php b/app/DataMapper/Analytics/LoginSuccess.php index 00099a1b458f..9f974d48c0cf 100644 --- a/app/DataMapper/Analytics/LoginSuccess.php +++ b/app/DataMapper/Analytics/LoginSuccess.php @@ -37,7 +37,6 @@ class LoginSuccess extends GenericCounter * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; diff --git a/app/DataMapper/Analytics/Mail/EmailBounce.php b/app/DataMapper/Analytics/Mail/EmailBounce.php index 7605447d346a..ce9372245a74 100644 --- a/app/DataMapper/Analytics/Mail/EmailBounce.php +++ b/app/DataMapper/Analytics/Mail/EmailBounce.php @@ -37,7 +37,6 @@ class EmailBounce extends GenericMixedMetric * * date("Y-m-d H:i:s") * - * @var \DateTime */ public $datetime; @@ -67,7 +66,6 @@ class EmailBounce extends GenericMixedMetric * The counter * set to 1. * - * @var string */ public $int_metric1 = 1; diff --git a/app/DataMapper/BaseSettings.php b/app/DataMapper/BaseSettings.php index 489e77db1078..06c3405b4852 100644 --- a/app/DataMapper/BaseSettings.php +++ b/app/DataMapper/BaseSettings.php @@ -16,13 +16,13 @@ namespace App\DataMapper; */ class BaseSettings { - //@deprecated - public function __construct($obj) - { - // foreach ($obj as $key => $value) { - // $obj->{$key} = $value; - // } - } + // //@deprecated + // public function __construct($obj) + // { + // // foreach ($obj as $key => $value) { + // // $obj->{$key} = $value; + // // } + // } public static function setCasts($obj, $casts) { diff --git a/app/DataMapper/ClientSettings.php b/app/DataMapper/ClientSettings.php index 4e0f02176fbc..0f33352bb052 100644 --- a/app/DataMapper/ClientSettings.php +++ b/app/DataMapper/ClientSettings.php @@ -47,17 +47,17 @@ class ClientSettings extends BaseSettings 'send_reminders' => 'bool', ]; - /** - * Cast object values and return entire class - * prevents missing properties from not being returned - * and always ensure an up to date class is returned. - * - * @param $obj - */ - public function __construct($obj) - { - parent::__construct($obj); - } + // /** + // * Cast object values and return entire class + // * prevents missing properties from not being returned + // * and always ensure an up to date class is returned. + // * + // * @param $obj + // */ + // public function __construct($obj) + // { + // // parent::__construct($obj); + // } /** * Default Client Settings scaffold. @@ -78,8 +78,8 @@ class ClientSettings extends BaseSettings /** * Merges settings from Company to Client. * - * @param stdClass $company_settings - * @param stdClass $client_settings + * @param $company_settings + * @param $client_settings * @return stdClass of merged settings */ public static function buildClientSettings($company_settings, $client_settings) diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 473de7064576..91871e2ae6e3 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -767,18 +767,18 @@ class CompanySettings extends BaseSettings 'purchase_order_design_id', ]; - /** - * Cast object values and return entire class - * prevents missing properties from not being returned - * and always ensure an up to date class is returned. - * - * @param $obj - * @deprecated - */ - public function __construct() - { - // parent::__construct($obj); - } + // /** + // * Cast object values and return entire class + // * prevents missing properties from not being returned + // * and always ensure an up to date class is returned. + // * + // * @param $obj + // * @deprecated + // */ + // public function __construct() + // { + // // parent::__construct($obj); + // } /** * Provides class defaults on init. diff --git a/app/DataMapper/DefaultSettings.php b/app/DataMapper/DefaultSettings.php index 02609da09978..6451370cf620 100644 --- a/app/DataMapper/DefaultSettings.php +++ b/app/DataMapper/DefaultSettings.php @@ -34,13 +34,4 @@ class DefaultSettings extends BaseSettings ]; } - /** - * @return stdClass - */ - private static function userSettingsObject() : stdClass - { - return (object) [ - // 'per_page' => self::$per_page, - ]; - } -} +} \ No newline at end of file diff --git a/app/DataMapper/EmailTemplateDefaults.php b/app/DataMapper/EmailTemplateDefaults.php index c0c1c1d43ce8..b63dba59c705 100644 --- a/app/DataMapper/EmailTemplateDefaults.php +++ b/app/DataMapper/EmailTemplateDefaults.php @@ -24,92 +24,78 @@ class EmailTemplateDefaults case 'email_template_invoice': return self::emailInvoiceTemplate(); - break; case 'email_template_quote': return self::emailQuoteTemplate(); - break; case 'email_template_credit': return self::emailCreditTemplate(); - break; case 'email_template_payment': return self::emailPaymentTemplate(); - break; case 'email_template_payment_partial': return self::emailPaymentPartialTemplate(); - break; case 'email_template_statement': return self::emailStatementTemplate(); - break; case 'email_template_reminder1': return self::emailReminder1Template(); - break; case 'email_template_reminder2': return self::emailReminder2Template(); - break; case 'email_template_reminder3': return self::emailReminder3Template(); - break; case 'email_template_reminder_endless': return self::emailReminderEndlessTemplate(); - break; case 'email_template_custom1': return self::emailInvoiceTemplate(); - break; case 'email_template_custom2': return self::emailInvoiceTemplate(); - break; case 'email_template_custom3': return self::emailInvoiceTemplate(); case 'email_template_purchase_order': return self::emailPurchaseOrderTemplate(); - break; - /* Subject */ case 'email_subject_purchase_order': return self::emailPurchaseOrderSubject(); case 'email_subject_invoice': return self::emailInvoiceSubject(); - break; + case 'email_subject_quote': return self::emailQuoteSubject(); - break; + case 'email_subject_credit': return self::emailCreditSubject(); - break; + case 'email_subject_payment': return self::emailPaymentSubject(); - break; + case 'email_subject_payment_partial': return self::emailPaymentPartialSubject(); - break; + case 'email_subject_statement': return self::emailStatementSubject(); - break; + case 'email_subject_reminder1': return self::emailReminder1Subject(); - break; + case 'email_subject_reminder2': return self::emailReminder2Subject(); - break; + case 'email_subject_reminder3': return self::emailReminder3Subject(); - break; + case 'email_subject_reminder_endless': return self::emailReminderEndlessSubject(); - break; + case 'email_subject_custom1': return self::emailInvoiceSubject(); - break; + case 'email_subject_custom2': return self::emailInvoiceSubject(); - break; + case 'email_subject_custom3': return self::emailInvoiceSubject(); - break; + default: return self::emailInvoiceTemplate(); - break; + } } diff --git a/app/DataMapper/FreeCompanySettings.php b/app/DataMapper/FreeCompanySettings.php index 96d5e6eb4455..9866862a96e9 100644 --- a/app/DataMapper/FreeCompanySettings.php +++ b/app/DataMapper/FreeCompanySettings.php @@ -141,7 +141,6 @@ class FreeCompanySettings extends BaseSettings public static $casts = [ 'portal_design_id' => 'string', - 'currency_id' => 'string', 'task_number_pattern' => 'string', 'task_number_counter' => 'int', 'expense_number_pattern' => 'string', @@ -191,16 +190,16 @@ class FreeCompanySettings extends BaseSettings 'website' => 'string', ]; - /** - * Cast object values and return entire class - * prevents missing properties from not being returned - * and always ensure an up to date class is returned. - * - * @param $obj - */ - public function __construct($obj) - { - } + // /** + // * Cast object values and return entire class + // * prevents missing properties from not being returned + // * and always ensure an up to date class is returned. + // * + // * @param $obj + // */ + // public function __construct($obj) + // { + // } /** * Provides class defaults on init. @@ -223,7 +222,7 @@ class FreeCompanySettings extends BaseSettings $data->date_format_id = (string) config('ninja.i18n.date_format_id'); $data->country_id = (string) config('ninja.i18n.country_id'); $data->translations = (object) []; - $data->pdf_variables = (object) self::getEntityVariableDefaults(); + // $data->pdf_variables = (object) self::getEntityVariableDefaults(); return self::setCasts($data, self::$casts); } diff --git a/app/DataMapper/Tax/AU/Rule.php b/app/DataMapper/Tax/AU/Rule.php index 8150f3e96cf5..aa11658ae194 100644 --- a/app/DataMapper/Tax/AU/Rule.php +++ b/app/DataMapper/Tax/AU/Rule.php @@ -251,7 +251,7 @@ class Rule extends BaseRule implements RuleInterface // } $this->tax_rate = $this->client->company->tax_data->regions->AU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate; - $this->reduced_tax_rate = $this->client->company->tax_data->regions->AU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate; + $this->reduced_tax_rate = $this->client->company->tax_data->regions->AU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate; return $this; diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 4b1368bb3120..efca1de3573a 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -197,7 +197,7 @@ class BaseRule implements RuleInterface } /** 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)) { + if($company->origin_tax_data->originDestination == 'O' && ($company->tax_data?->seller_subregion == $this->client_subregion)) { $tax_data = $company->origin_tax_data; diff --git a/app/DataProviders/USStates.php b/app/DataProviders/USStates.php index 3f07c4ecf427..4809df8439c1 100644 --- a/app/DataProviders/USStates.php +++ b/app/DataProviders/USStates.php @@ -33859,8 +33859,6 @@ class USStates '99926' => 'AK', '99927' => 'AK', '99929' => 'AK', - '13021' => 'NY', - '13024' => 'NY', ]; public static function get(): array diff --git a/app/Events/Design/DesignWasCreated.php b/app/Events/Design/DesignWasCreated.php index c919768ac511..213eb9882461 100644 --- a/app/Events/Design/DesignWasCreated.php +++ b/app/Events/Design/DesignWasCreated.php @@ -30,7 +30,7 @@ class DesignWasCreated /** * Get the channels the event should broadcast on. * - * @return PrivateChannel + * @return PrivateChannel|array */ public function broadcastOn() { diff --git a/app/Events/Design/DesignWasDeleted.php b/app/Events/Design/DesignWasDeleted.php index f2339cd59d1f..d9142724a074 100644 --- a/app/Events/Design/DesignWasDeleted.php +++ b/app/Events/Design/DesignWasDeleted.php @@ -30,7 +30,7 @@ class DesignWasDeleted /** * Get the channels the event should broadcast on. * - * @return PrivateChannel + * @return PrivateChannel|array */ public function broadcastOn() { diff --git a/app/Events/Design/DesignWasRestored.php b/app/Events/Design/DesignWasRestored.php index 789ff15a684b..6daa2c2545ba 100644 --- a/app/Events/Design/DesignWasRestored.php +++ b/app/Events/Design/DesignWasRestored.php @@ -30,7 +30,7 @@ class DesignWasRestored /** * Get the channels the event should broadcast on. * - * @return PrivateChannel + * @return PrivateChannel|array */ public function broadcastOn() { diff --git a/app/Events/Design/DesignWasUpdated.php b/app/Events/Design/DesignWasUpdated.php index 30d49a85c4fb..3b215fdff2bf 100644 --- a/app/Events/Design/DesignWasUpdated.php +++ b/app/Events/Design/DesignWasUpdated.php @@ -30,7 +30,7 @@ class DesignWasUpdated /** * Get the channels the event should broadcast on. * - * @return PrivateChannel + * @return PrivateChannel|array */ public function broadcastOn() { diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 506213d57797..4e8f339a3916 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -17,8 +17,10 @@ use App\Utils\Ninja; use Sentry\State\Scope; use Illuminate\Support\Arr; use Illuminate\Http\Request; +use InvalidArgumentException; use Sentry\Laravel\Integration; use Illuminate\Support\Facades\Schema; +use Aws\Exception\CredentialsException; use GuzzleHttp\Exception\ConnectException; use Illuminate\Auth\AuthenticationException; use League\Flysystem\UnableToCreateDirectory; @@ -34,7 +36,6 @@ use Symfony\Component\Console\Exception\CommandNotFoundException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException; -use InvalidArgumentException; class Handler extends ExceptionHandler { @@ -64,7 +65,7 @@ class Handler extends ExceptionHandler ConnectException::class, RuntimeException::class, InvalidArgumentException::class, - Aws\Exception\CredentialsException::class, + CredentialsException::class, ]; protected $hostedDontReport = [ diff --git a/app/Exceptions/PaymentRefundFailed.php b/app/Exceptions/PaymentRefundFailed.php index 4aa2c4487c66..28e00858e867 100644 --- a/app/Exceptions/PaymentRefundFailed.php +++ b/app/Exceptions/PaymentRefundFailed.php @@ -14,7 +14,7 @@ namespace App\Exceptions; use Exception; use Illuminate\Http\Request; -use Illuminate\Http\Response; +use Illuminate\Http\JsonResponse; class PaymentRefundFailed extends Exception { @@ -32,14 +32,14 @@ class PaymentRefundFailed extends Exception * Render the exception into an HTTP response. * * @param Request $request - * @return Response + * @return JsonResponse */ public function render($request) { // $msg = 'Unable to refund the transaction'; $msg = ctrans('texts.warning_local_refund'); - if ($this->getMessage() && strlen($this->getMessage()) >= 1) { + if ($this->getMessage() && strlen($this->getMessage()) > 1) { $msg = $this->getMessage(); } diff --git a/app/Exceptions/QuoteConversion.php b/app/Exceptions/QuoteConversion.php index a4812f4c1bdb..ceafdf8b499a 100644 --- a/app/Exceptions/QuoteConversion.php +++ b/app/Exceptions/QuoteConversion.php @@ -14,7 +14,7 @@ namespace App\Exceptions; use Exception; use Illuminate\Http\Request; -use Illuminate\Http\Response; +use Illuminate\Http\JsonResponse; class QuoteConversion extends Exception { @@ -32,7 +32,7 @@ class QuoteConversion extends Exception * Render the exception into an HTTP response. * * @param Request $request - * @return Response + * @return JsonResponse */ public function render($request) { diff --git a/app/Exceptions/YodleeApiException.php b/app/Exceptions/YodleeApiException.php index 177be9ae1834..b56eedced3bd 100644 --- a/app/Exceptions/YodleeApiException.php +++ b/app/Exceptions/YodleeApiException.php @@ -14,7 +14,7 @@ namespace App\Exceptions; use Exception; use Illuminate\Http\Request; -use Illuminate\Http\Response; +use Illuminate\Http\JsonResponse; class YodleeApiException extends Exception { @@ -32,14 +32,14 @@ class YodleeApiException extends Exception * Render the exception into an HTTP response. * * @param Request $request - * @return Response + * @return JsonResponse */ public function render($request) { // $msg = 'Unable to refund the transaction'; $msg = ctrans('texts.error'); - if ($this->getMessage() && strlen($this->getMessage()) >= 1) { + if ($this->getMessage() && strlen($this->getMessage()) > 1) { $msg = $this->getMessage(); } diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index d11c9ef72c7e..8576bdfa8337 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -373,6 +373,8 @@ class BaseExport if(!is_array($parts) || count($parts) < 2) return ''; + $value = ''; + match($parts[0]) { 'contact' => $value = $this->resolveClientContactKey($parts[1], $entity, $transformer), 'client' => $value = $this->resolveClientKey($parts[1], $entity, $transformer), @@ -385,7 +387,7 @@ class BaseExport 'purchase_order' => $value = $this->resolvePurchaseOrderKey($parts[1], $entity, $transformer), 'payment' => $value = $this->resolvePaymentKey($parts[1], $entity, $transformer), 'task' => $value = $this->resolveTaskKey($parts[1], $entity, $transformer), - default => $value = '' + default => $value = '', }; return $value; @@ -580,6 +582,7 @@ class BaseExport private function resolveInvoiceKey($column, $entity, $transformer) { nlog("searching for {$column}"); + $transformed_invoice = false; if($transformer instanceof PaymentTransformer) { $transformed_invoices = $transformer->includeInvoices($entity); @@ -615,14 +618,14 @@ class BaseExport } - if(array_key_exists($column, $transformed_invoice)) { + if($transformed_invoice && array_key_exists($column, $transformed_invoice)) { return $transformed_invoice[$column]; - } elseif (array_key_exists(str_replace("invoice.", "", $column), $transformed_invoice)) { + } elseif ($transformed_invoice && array_key_exists(str_replace("invoice.", "", $column), $transformed_invoice)) { return $transformed_invoice[$column]; } - if($column == 'status') - return $entity->stringStatus($entity->status_id); + // if($column == 'status') + // return $entity->stringStatus($entity->status_id); return ''; } diff --git a/app/Filters/BankTransactionFilters.php b/app/Filters/BankTransactionFilters.php index a1aab569020c..41a558931e99 100644 --- a/app/Filters/BankTransactionFilters.php +++ b/app/Filters/BankTransactionFilters.php @@ -37,7 +37,7 @@ class BankTransactionFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -118,7 +118,7 @@ class BankTransactionFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/BankTransactionRuleFilters.php b/app/Filters/BankTransactionRuleFilters.php index ee8496f579b5..d19e49371474 100644 --- a/app/Filters/BankTransactionRuleFilters.php +++ b/app/Filters/BankTransactionRuleFilters.php @@ -36,7 +36,7 @@ class BankTransactionRuleFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -54,7 +54,7 @@ class BankTransactionRuleFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/PaymentTermFilters.php b/app/Filters/PaymentTermFilters.php index 26400394c935..54cfeeea4785 100644 --- a/app/Filters/PaymentTermFilters.php +++ b/app/Filters/PaymentTermFilters.php @@ -21,7 +21,7 @@ class PaymentTermFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -39,7 +39,7 @@ class PaymentTermFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/QuoteFilters.php b/app/Filters/QuoteFilters.php index d906f2bd1c2f..2befad099495 100644 --- a/app/Filters/QuoteFilters.php +++ b/app/Filters/QuoteFilters.php @@ -22,7 +22,7 @@ class QuoteFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -124,7 +124,7 @@ class QuoteFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/RecurringExpenseFilters.php b/app/Filters/RecurringExpenseFilters.php index f8e0c2ba667a..95a293c5274b 100644 --- a/app/Filters/RecurringExpenseFilters.php +++ b/app/Filters/RecurringExpenseFilters.php @@ -21,7 +21,7 @@ class RecurringExpenseFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -52,7 +52,7 @@ class RecurringExpenseFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/SubscriptionFilters.php b/app/Filters/SubscriptionFilters.php index d9722e46ce2a..b32f692acf16 100644 --- a/app/Filters/SubscriptionFilters.php +++ b/app/Filters/SubscriptionFilters.php @@ -21,7 +21,7 @@ class SubscriptionFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -39,7 +39,7 @@ class SubscriptionFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/SystemLogFilters.php b/app/Filters/SystemLogFilters.php index 3fb6c80b082e..62b63f8b76fa 100644 --- a/app/Filters/SystemLogFilters.php +++ b/app/Filters/SystemLogFilters.php @@ -48,7 +48,7 @@ class SystemLogFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -64,7 +64,7 @@ class SystemLogFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/TaskFilters.php b/app/Filters/TaskFilters.php index 9b42549a3afc..f0b3a1afc256 100644 --- a/app/Filters/TaskFilters.php +++ b/app/Filters/TaskFilters.php @@ -24,7 +24,7 @@ class TaskFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -99,7 +99,7 @@ class TaskFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder @@ -131,8 +131,8 @@ class TaskFilters extends QueryFilters $status_parameters = explode(',', $value); - if(count($status_parameters) > 0) - return $this->builder->whereIn('status_id', $this->transformKeys($status_parameters)); + if(count($status_parameters) >= 1) + $this->builder->whereIn('status_id', $this->transformKeys($status_parameters)); return $this->builder; } diff --git a/app/Filters/TaskStatusFilters.php b/app/Filters/TaskStatusFilters.php index 35ef91a0288b..2487a9f5a94a 100644 --- a/app/Filters/TaskStatusFilters.php +++ b/app/Filters/TaskStatusFilters.php @@ -21,7 +21,7 @@ class TaskStatusFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -39,7 +39,7 @@ class TaskStatusFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/TaxRateFilters.php b/app/Filters/TaxRateFilters.php index 8d225d3db268..ada581887fc0 100644 --- a/app/Filters/TaxRateFilters.php +++ b/app/Filters/TaxRateFilters.php @@ -21,7 +21,7 @@ class TaxRateFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -39,7 +39,7 @@ class TaxRateFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/TokenFilters.php b/app/Filters/TokenFilters.php index 2037d768add8..0093ac4f4183 100644 --- a/app/Filters/TokenFilters.php +++ b/app/Filters/TokenFilters.php @@ -21,7 +21,7 @@ class TokenFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -45,7 +45,7 @@ class TokenFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/UserFilters.php b/app/Filters/UserFilters.php index 20d2a7ce777f..12a26d27a6cf 100644 --- a/app/Filters/UserFilters.php +++ b/app/Filters/UserFilters.php @@ -21,7 +21,7 @@ class UserFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -43,7 +43,7 @@ class UserFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder @@ -69,6 +69,19 @@ class UserFilters extends QueryFilters }); } + /** + * Filters users that have been removed from the + * company, but not deleted from the system. + * + * @return void + */ + public function hideRemovedUsers() + { + return $this->builder->whereHas('company_users', function ($q) { + $q->where('company_id', '=', auth()->user()->company()->id)->whereNull('deleted_at'); + }); + } + /** * Overrides the base with() function as no company ID * exists on the user table diff --git a/app/Filters/VendorFilters.php b/app/Filters/VendorFilters.php index 4937a7513326..1f3adf6d4d50 100644 --- a/app/Filters/VendorFilters.php +++ b/app/Filters/VendorFilters.php @@ -21,7 +21,7 @@ class VendorFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -58,7 +58,7 @@ class VendorFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Filters/WebhookFilters.php b/app/Filters/WebhookFilters.php index 6ab092749584..1401b3ad1bf1 100644 --- a/app/Filters/WebhookFilters.php +++ b/app/Filters/WebhookFilters.php @@ -21,7 +21,7 @@ class WebhookFilters extends QueryFilters /** * Filter based on search text. * - * @param string query filter + * @param string $filter * @return Builder * @deprecated */ @@ -39,7 +39,7 @@ class WebhookFilters extends QueryFilters /** * Sorts the list based on $sort. * - * @param string sort formatted as column|asc + * @param string $sort formatted as column|asc * @return Builder */ public function sort(string $sort = ''): Builder diff --git a/app/Helpers/ClientPortal.php b/app/Helpers/ClientPortal.php index 25f7706344f0..f4d1a2fb89fc 100644 --- a/app/Helpers/ClientPortal.php +++ b/app/Helpers/ClientPortal.php @@ -19,7 +19,7 @@ use Illuminate\View\View; * * @param $page * @param bool $boolean - * @return bool + * @return bool | string */ function isActive($page, bool $boolean = false) { @@ -40,8 +40,6 @@ function isActive($page, bool $boolean = false) return true; } - - return false; } diff --git a/app/Helpers/Epc/EpcQrGenerator.php b/app/Helpers/Epc/EpcQrGenerator.php index 2da19d1da679..100e9a2cca38 100644 --- a/app/Helpers/Epc/EpcQrGenerator.php +++ b/app/Helpers/Epc/EpcQrGenerator.php @@ -53,9 +53,8 @@ class EpcQrGenerator $qr = $writer->writeString($this->encodeMessage(), 'utf-8'); } catch(\Throwable $e) { return ''; - } catch(\Exception $e) { - return ''; - } + } + return " {$qr}"; } diff --git a/app/Http/Controllers/BankIntegrationController.php b/app/Http/Controllers/BankIntegrationController.php index e5095bdaff43..e80a9b31a537 100644 --- a/app/Http/Controllers/BankIntegrationController.php +++ b/app/Http/Controllers/BankIntegrationController.php @@ -12,6 +12,7 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; +use Illuminate\Http\Response; use App\Models\BankIntegration; use App\Utils\Traits\MakesHash; use Illuminate\Http\JsonResponse; @@ -50,7 +51,7 @@ class BankIntegrationController extends BaseController /** * @param BankIntegrationFilters $filters - * @return Responsec + * @return Response */ public function index(BankIntegrationFilters $filters) { @@ -250,7 +251,7 @@ class BankIntegrationController extends BaseController * Return the remote list of accounts stored on the third party provider * and update our local cache. * - * @return Response + * @return Response | JsonResponse * */ diff --git a/app/Http/Controllers/BankTransactionController.php b/app/Http/Controllers/BankTransactionController.php index eefad1dd49fe..d8b05a3d5e33 100644 --- a/app/Http/Controllers/BankTransactionController.php +++ b/app/Http/Controllers/BankTransactionController.php @@ -71,7 +71,7 @@ class BankTransactionController extends BaseController public function create(CreateBankTransactionRequest $request) { - $bank_transaction = BankTransactionFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id); + $bank_transaction = BankTransactionFactory::create(auth()->user()->company()->id, auth()->user()->id); return $this->itemResponse($bank_transaction); } @@ -79,7 +79,7 @@ class BankTransactionController extends BaseController public function store(StoreBankTransactionRequest $request) { //stub to store the model - $bank_transaction = $this->bank_transaction_repo->save($request->all(), BankTransactionFactory::create(auth()->user()->company()->id, auth()->user()->id, auth()->user()->account_id)); + $bank_transaction = $this->bank_transaction_repo->save($request->all(), BankTransactionFactory::create(auth()->user()->company()->id, auth()->user()->id)); return $this->itemResponse($bank_transaction); } diff --git a/app/Http/Controllers/ClientPortal/QuoteController.php b/app/Http/Controllers/ClientPortal/QuoteController.php index 36064e8f62d2..dd56ca5edd0e 100644 --- a/app/Http/Controllers/ClientPortal/QuoteController.php +++ b/app/Http/Controllers/ClientPortal/QuoteController.php @@ -185,7 +185,7 @@ class QuoteController extends Controller $quote->service()->approve(auth()->user())->save(); if (request()->has('signature') && ! is_null(request()->signature) && ! empty(request()->signature)) { - InjectSignature::dispatch($quote, request()->signature); + InjectSignature::dispatch($quote, auth()->guard('contact')->user()->id, request()->signature, request()->getClientIp()); } } diff --git a/app/Http/Controllers/VendorPortal/PurchaseOrderController.php b/app/Http/Controllers/VendorPortal/PurchaseOrderController.php index 65ce601a7c6a..108cee7667c4 100644 --- a/app/Http/Controllers/VendorPortal/PurchaseOrderController.php +++ b/app/Http/Controllers/VendorPortal/PurchaseOrderController.php @@ -186,8 +186,7 @@ class PurchaseOrderController extends Controller ->save(); if (request()->has('signature') && ! is_null(request()->signature) && ! empty(request()->signature)) { - (new InjectSignature($purchase_order, request()->signature))->handle(); - // InjectSignature::dispatch($purchase_order, request()->signature); + (new InjectSignature($purchase_order, auth()->guard('vendor')->user()->id, request()->signature, request()->getClientIp()))->handle(); } event(new PurchaseOrderWasAccepted($purchase_order, auth()->guard('vendor')->user(), $purchase_order->company, Ninja::eventVars())); diff --git a/app/Http/Livewire/PdfSlot.php b/app/Http/Livewire/PdfSlot.php index dccaf802c03d..8c8770eb1205 100644 --- a/app/Http/Livewire/PdfSlot.php +++ b/app/Http/Livewire/PdfSlot.php @@ -53,6 +53,8 @@ class PdfSlot extends Component public $route_entity = 'client'; + public $is_quote = false; + public function mount() { MultiDB::setDb($this->db); @@ -111,9 +113,9 @@ class PdfSlot extends Component $this->show_line_total = in_array('$product.line_total', $this->settings->pdf_variables->product_quote_columns); } - $this->html_variables = $this->entity->client ? - (new HtmlEngine($this->invitation))->generateLabelsAndValues() : - (new VendorHtmlEngine($this->invitation))->generateLabelsAndValues(); + $this->html_variables = $this->entity_type == 'purchase_order' ? + (new VendorHtmlEngine($this->invitation))->generateLabelsAndValues() : + (new HtmlEngine($this->invitation))->generateLabelsAndValues(); return render('components.livewire.pdf-slot', [ 'invitation' => $this->invitation, @@ -230,15 +232,16 @@ class PdfSlot extends Component private function getProducts() { - - $product_items = collect($this->entity->line_items)->filter(function ($item) { return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5; })->map(function ($item){ + + $notes = strlen($item->notes) > 4 ? $item->notes : $item->product_key; + return [ 'quantity' => $item->quantity, 'cost' => Number::formatMoney($item->cost, $this->entity->client ?: $this->entity->vendor), - 'notes' => $this->invitation->company->markdown_enabled ? DesignHelpers::parseMarkdownToHtml($item->notes) : $item->notes, + 'notes' => $this->invitation->company->markdown_enabled ? DesignHelpers::parseMarkdownToHtml($notes) : $notes, 'line_total' => Number::formatMoney($item->line_total, $this->entity->client ?: $this->entity->vendor), ]; }); @@ -268,6 +271,7 @@ class PdfSlot extends Component if ($this->invitation instanceof InvoiceInvitation) { return 'invoice'; } elseif ($this->invitation instanceof QuoteInvitation) { + $this->is_quote = true; return 'quote'; } elseif ($this->invitation instanceof CreditInvitation) { return 'credit'; diff --git a/app/Jobs/Bank/MatchBankTransactions.php b/app/Jobs/Bank/MatchBankTransactions.php index f2a44c60c809..ca6dc4a5a2bb 100644 --- a/app/Jobs/Bank/MatchBankTransactions.php +++ b/app/Jobs/Bank/MatchBankTransactions.php @@ -47,11 +47,11 @@ class MatchBankTransactions implements ShouldQueue private array $input; - protected Company $company; + protected ?Company $company; public Invoice $invoice; - private BankTransaction $bt; + private ?BankTransaction $bt; private $categories; @@ -78,8 +78,6 @@ class MatchBankTransactions implements ShouldQueue /** * Execute the job. * - * - * @return void */ public function handle() { diff --git a/app/Jobs/Bank/ProcessBankTransactions.php b/app/Jobs/Bank/ProcessBankTransactions.php index f2c3a519ebdd..aeb309c50e7c 100644 --- a/app/Jobs/Bank/ProcessBankTransactions.php +++ b/app/Jobs/Bank/ProcessBankTransactions.php @@ -108,10 +108,12 @@ class ProcessBankTransactions implements ShouldQueue $at = new AccountTransformer(); $account = $at->transform($account_summary); - $this->bank_integration->balance = $account['current_balance']; - $this->bank_integration->currency = $account['account_currency']; - $this->bank_integration->save(); - + if($account[0]['current_balance']) { + $this->bank_integration->balance = $account['current_balance']; + $this->bank_integration->currency = $account['account_currency']; + $this->bank_integration->save(); + } + } } catch(\Exception $e) { diff --git a/app/Jobs/Company/CompanyTaxRate.php b/app/Jobs/Company/CompanyTaxRate.php index 8fbc6a3e4452..886704fa358e 100644 --- a/app/Jobs/Company/CompanyTaxRate.php +++ b/app/Jobs/Company/CompanyTaxRate.php @@ -52,7 +52,7 @@ class CompanyTaxRate implements ShouldQueue /** State must be calculated else default to the company state for taxes */ if(array_key_exists($this->company->settings->state, USStates::get())) { - $calculated_state = $this->company->setting->state; + $calculated_state = $this->company->settings->state; } else { diff --git a/app/Jobs/Invoice/InjectSignature.php b/app/Jobs/Invoice/InjectSignature.php index fd5792adf15d..db67ea2b223e 100644 --- a/app/Jobs/Invoice/InjectSignature.php +++ b/app/Jobs/Invoice/InjectSignature.php @@ -2,21 +2,19 @@ namespace App\Jobs\Invoice; -use App\Jobs\Entity\CreateEntityPdf; -use App\Jobs\Vendor\CreatePurchaseOrderPdf; use App\Models\PurchaseOrder; use Illuminate\Bus\Queueable; +use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; class InjectSignature implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; /** - * @var App\Models\Invoice|App\Models\Quote + * @var \App\Models\Invoice | \App\Models\Quote | \App\Models\Credit | \App\Models\PurchaseOrder */ public $entity; @@ -25,17 +23,26 @@ class InjectSignature implements ShouldQueue */ public $signature; + public $contact_id; + + public $ip; + /** * Create a new job instance. * * @param $entity * @param string $signature */ - public function __construct($entity, string $signature) + public function __construct($entity, $contact_id, string $signature, ?string $ip) { $this->entity = $entity; + $this->contact_id = $contact_id; + $this->signature = $signature; + + $this->ip = $ip; + } /** @@ -45,20 +52,31 @@ class InjectSignature implements ShouldQueue */ public function handle() { - $invitation = $this->entity->invitations->whereNotNull('signature_base64')->first(); + $invitation = false; + if($this->entity instanceof PurchaseOrder){ + $invitation = $this->entity->invitations()->where('vendor_contact_id', $this->contact_id)->first(); + + if(!$invitation) + $invitation = $this->entity->invitations->first(); + + } + else { + + $invitation = $this->entity->invitations()->where('client_contact_id', $this->contact_id)->first(); + + if(!$invitation) + $invitation = $this->entity->invitations->first(); + } + if (! $invitation) { return; } $invitation->signature_base64 = $this->signature; + $invitation->signature_date = now(); + $invitation->signature_ip = $this->ip; $invitation->save(); - $this->entity->refresh()->service()->touchPdf(true); - - // if($this->entity instanceof PurchaseOrder) - // (new CreatePurchaseOrderPdf($invitation))->handle(); - // else - // (new CreateEntityPdf($invitation))->handle(); } } diff --git a/app/Models/Activity.php b/app/Models/Activity.php index d0edf254e2c9..17cfcb872dc3 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -13,6 +13,7 @@ namespace App\Models; use App\Utils\Number; use App\Utils\Traits\MakesHash; +use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * App\Models\Activity @@ -302,161 +303,120 @@ class Activity extends StaticModel 'backup', ]; - /** - * @return mixed - */ + public function getHashedIdAttribute() { return $this->encodePrimaryKey($this->id); } - /** - * @return mixed - */ + public function getEntityType() { return self::class; } - /** - * @return mixed - */ + public function backup() { return $this->hasOne(Backup::class); } - /** - * @return mixed - */ + public function history() { return $this->hasOne(Backup::class); } - /** - * @return mixed - */ - public function user() + public function user() :BelongsTo { return $this->belongsTo(User::class)->withTrashed(); } - /** - * @return mixed - */ + public function contact() { return $this->belongsTo(ClientContact::class, 'client_contact_id', 'id')->withTrashed(); } - /** - * @return mixed - */ + public function client() { return $this->belongsTo(Client::class)->withTrashed(); } - /** - * @return mixed - */ + public function invoice() { return $this->belongsTo(Invoice::class)->withTrashed(); } - /** - * @return mixed - */ + public function vendor() { return $this->belongsTo(Vendor::class)->withTrashed(); } - /** - * @return mixed - */ + public function recurring_invoice() { return $this->belongsTo(RecurringInvoice::class)->withTrashed(); } - /** - * @return mixed - */ + public function credit() { return $this->belongsTo(Credit::class)->withTrashed(); } - /** - * @return mixed - */ + public function quote() { return $this->belongsTo(Quote::class)->withTrashed(); } - /** - * @return mixed - */ + public function subscription() { return $this->belongsTo(Subscription::class)->withTrashed(); } - /** - * @return mixed - */ + public function payment() { return $this->belongsTo(Payment::class)->withTrashed(); } - /** - * @return mixed - */ + public function expense() { return $this->belongsTo(Expense::class)->withTrashed(); } - /** - * @return mixed - */ + public function recurring_expense() { return $this->belongsTo(RecurringExpense::class)->withTrashed(); } - /** - * @return mixed - */ + public function purchase_order() { return $this->belongsTo(PurchaseOrder::class)->withTrashed(); } - /** - * @return mixed - */ + public function vendor_contact() { return $this->belongsTo(VendorContact::class)->withTrashed(); } - /** - * @return mixed - */ + public function task() { return $this->belongsTo(Task::class)->withTrashed(); } - /** - * @return mixed - */ + public function company() { return $this->belongsTo(Company::class); diff --git a/app/Models/BankAccount.php b/app/Models/BankAccount.php index 5b4abddc9a96..8152c94d5335 100644 --- a/app/Models/BankAccount.php +++ b/app/Models/BankAccount.php @@ -50,12 +50,6 @@ class BankAccount extends BaseModel { use SoftDeletes; - /** - * @var array - */ - /** - * @var array - */ protected $fillable = [ 'bank_id', 'app_version', diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index f5d58460bb37..62bcabefa805 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -31,30 +31,35 @@ use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundExceptio * @property-read mixed $hashed_id * @property string $number * @property int $company_id + * @property int $id * @property \App\Models\Company $company * @method static \Illuminate\Database\Eloquent\Builder|BaseModel|Illuminate\Database\Eloquent\Relations\BelongsTo|\Awobaz\Compoships\Database\Eloquent\Relations\BelongsTo|\App\Models\Company company() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel|Illuminate\Database\Eloquent\Relations\HasMany|BaseModel orderBy() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns) - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel with() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel newModelQuery() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel with($value) + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel newModelQuery($query) + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel newQuery($query) * @method static \Illuminate\Database\Eloquent\Builder|BaseModel query() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude(array $excludeable) * @method static \Illuminate\Database\Eloquent\Builder|BaseModel withTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scopeExclude() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel find() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel whereIn() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel where() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scopeExclude($query) + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel find($value) + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel whereIn($query) + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel where($query) * @method static \Illuminate\Database\Eloquent\Builder|BaseModel count() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel create() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel insert() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel create($query) + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel insert($query) * @method static \Illuminate\Database\Eloquent\Builder|BaseModel service() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel whereHas() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel withTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel orderBy($column, $direction) + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel invitations() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel createInvitations() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel whereHas($query) * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read int|null $invitations_count * * @method \App\Models\Company company() * @method int companyId() + * @method createInvitations() * @method Builder|static exclude($columns) * @method static \Illuminate\Database\Eloquent\Builder exclude(array $columns) * @mixin \Eloquent diff --git a/app/Models/Client.php b/app/Models/Client.php index 5896488cce73..f8687eef6141 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -11,21 +11,26 @@ namespace App\Models; +use App\Models\GatewayType; +use App\Utils\Traits\AppSetup; +use App\Utils\Traits\MakesHash; +use App\Utils\Traits\MakesDates; +use App\DataMapper\FeesAndLimits; +use App\Models\Traits\Excludable; use App\DataMapper\ClientSettings; use App\DataMapper\CompanySettings; -use App\DataMapper\FeesAndLimits; -use App\Models\Presenters\ClientPresenter; -use App\Models\Traits\Excludable; -use App\Services\Client\ClientService; -use App\Utils\Traits\AppSetup; -use App\Utils\Traits\ClientGroupSettingsSaver; -use App\Utils\Traits\GeneratesCounter; -use App\Utils\Traits\MakesDates; -use App\Utils\Traits\MakesHash; -use Illuminate\Contracts\Translation\HasLocalePreference; -use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\Cache; +use App\Services\Client\ClientService; +use App\Utils\Traits\GeneratesCounter; use Laracasts\Presenter\PresentableTrait; +use App\Models\Presenters\ClientPresenter; +use Illuminate\Database\Eloquent\SoftDeletes; +use App\Utils\Traits\ClientGroupSettingsSaver; +use App\Libraries\Currency\Conversion\CurrencyApi; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\MorphMany; +use Illuminate\Contracts\Translation\HasLocalePreference; /** * App\Models\Client @@ -65,6 +70,7 @@ use Laracasts\Presenter\PresentableTrait; * @property string|null $shipping_postal_code * @property int|null $shipping_country_id * @property object|null $settings + * @property object|null $group_settings * @property bool $is_deleted * @property int|null $group_settings_id * @property string|null $vat_number @@ -114,68 +120,12 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices * @property-read int|null $tasks_count * @property-read \App\Models\User $user - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel company() * @method static \Illuminate\Database\Eloquent\Builder|Client exclude($columns) * @method static \Database\Factories\ClientFactory factory($count = null, $state = []) * @method static \Illuminate\Database\Eloquent\Builder|Client filter(\App\Filters\QueryFilters $filters) - * @method static \Illuminate\Database\Eloquent\Builder|Client newModelQuery() - * @method static \Illuminate\Database\Eloquent\Builder|Client newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|Client onlyTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|Client query() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope() - * @method static \Illuminate\Database\Eloquent\Builder|Client whereAddress1($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereAddress2($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereAssignedUserId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereBalance($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereCity($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereClientHash($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereCompanyId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereCountryId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereCreditBalance($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereCustomValue1($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereCustomValue2($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereCustomValue3($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereCustomValue4($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereGroupSettingsId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereIdNumber($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereIndustryId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereIsDeleted($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereLastLogin($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereLogo($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereNumber($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client wherePaidToDate($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client wherePhone($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client wherePostalCode($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client wherePrivateNotes($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client wherePublicNotes($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereSettings($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereShippingAddress1($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereShippingAddress2($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereShippingCity($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereShippingCountryId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereShippingPostalCode($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereShippingState($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereSizeId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereState($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereUserId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereVatNumber($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client whereWebsite($value) - * @method static \Illuminate\Database\Eloquent\Builder|Client withTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|Client withoutTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|Client with() - * @method static \Illuminate\Database\Eloquent\Builder|Client where() * @property string $payment_balance - * @method static \Illuminate\Database\Eloquent\Builder|Client wherePaymentBalance($value) * @property mixed $tax_data - * @method static \Illuminate\Database\Eloquent\Builder|Client whereTaxData($value) * @property int $is_tax_exempt - * @method static \Illuminate\Database\Eloquent\Builder|Client whereIsTaxExempt($value) - * @property int $has_valid_vat_number * @mixin \Eloquent */ @@ -357,62 +307,62 @@ class Client extends BaseModel implements HasLocalePreference return $this->hasMany(Activity::class)->take(50)->orderBy('id', 'desc'); } - public function contacts() + public function contacts() :HasMany { return $this->hasMany(ClientContact::class)->orderBy('is_primary', 'desc'); } - public function primary_contact() + public function primary_contact():HasMany { return $this->hasMany(ClientContact::class)->where('is_primary', true); } - public function company() + public function company() :BelongsTo { return $this->belongsTo(Company::class); } - public function user() + public function user() :BelongsTo { return $this->belongsTo(User::class)->withTrashed(); } - public function assigned_user() + public function assigned_user() :BelongsTo { return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed(); } - public function country() + public function country() :BelongsTo { return $this->belongsTo(Country::class); } - public function invoices() + public function invoices() :HasMany { return $this->hasMany(Invoice::class)->withTrashed(); } - public function quotes() + public function quotes() :HasMany { return $this->hasMany(Quote::class)->withTrashed(); } - public function tasks() + public function tasks() :HasMany { return $this->hasMany(Task::class)->withTrashed(); } - public function payments() + public function payments() :HasMany { return $this->hasMany(Payment::class)->withTrashed(); } - public function recurring_invoices() + public function recurring_invoices() :HasMany { return $this->hasMany(RecurringInvoice::class)->withTrashed(); } - public function recurring_expenses() + public function recurring_expenses() :HasMany { return $this->hasMany(RecurringExpense::class)->withTrashed(); } @@ -422,12 +372,12 @@ class Client extends BaseModel implements HasLocalePreference return $this->belongsTo(Country::class, 'shipping_country_id', 'id'); } - public function system_logs() + public function system_logs() :HasMany { return $this->hasMany(SystemLog::class)->take(50)->orderBy('id', 'desc'); } - public function timezone() + public function timezone() :Timezone { return Timezone::find($this->getSetting('timezone_id')); } @@ -445,17 +395,17 @@ class Client extends BaseModel implements HasLocalePreference })->first(); } - public function industry() + public function industry() :BelongsTo { return $this->belongsTo(Industry::class); } - public function size() + public function size() :BelongsTo { return $this->belongsTo(Size::class); } - public function locale() + public function locale() :string { if (! $this->language()) { return 'en'; @@ -526,7 +476,7 @@ class Client extends BaseModel implements HasLocalePreference * @param string $setting The Setting parameter * @return mixed The setting requested */ - public function getSetting($setting) + public function getSetting($setting) :mixed { /*Client Settings*/ if ($this->settings && property_exists($this->settings, $setting) && isset($this->settings->{$setting})) { @@ -556,7 +506,6 @@ class Client extends BaseModel implements HasLocalePreference return ''; -// throw new \Exception("Settings corrupted", 1); } public function getSettingEntity($setting) @@ -582,12 +531,12 @@ class Client extends BaseModel implements HasLocalePreference throw new \Exception('Could not find a settings object', 1); } - public function documents() + public function documents() :MorphMany { return $this->morphMany(Document::class, 'documentable'); } - public function group_settings() + public function group_settings() :BelongsTo { return $this->belongsTo(GroupSetting::class); } @@ -710,7 +659,7 @@ class Client extends BaseModel implements HasLocalePreference } } - public function getCurrencyCode() + public function getCurrencyCode(): string { if ($this->currency()) { return $this->currency()->code; @@ -751,51 +700,51 @@ class Client extends BaseModel implements HasLocalePreference })->first()->locale; } - public function backup_path() + public function backup_path() :string { return $this->company->company_key.'/'.$this->client_hash.'/backups'; } - public function invoice_filepath($invitation) + public function invoice_filepath($invitation) :string { $contact_key = $invitation->contact->contact_key; return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/invoices/'; } - public function e_invoice_filepath($invitation) + public function e_invoice_filepath($invitation) :string { $contact_key = $invitation->contact->contact_key; return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/e_invoice/'; } - public function quote_filepath($invitation) + public function quote_filepath($invitation) :string { $contact_key = $invitation->contact->contact_key; return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/quotes/'; } - public function credit_filepath($invitation) + public function credit_filepath($invitation) :string { $contact_key = $invitation->contact->contact_key; return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/credits/'; } - public function recurring_invoice_filepath($invitation) + public function recurring_invoice_filepath($invitation) :string { $contact_key = $invitation->contact->contact_key; return $this->company->company_key.'/'.$this->client_hash.'/'.$contact_key.'/recurring_invoices/'; } - public function company_filepath() + public function company_filepath() :string { return $this->company->company_key.'/'; } - public function document_filepath() + public function document_filepath() :string { return $this->company->company_key.'/documents/'; } @@ -820,10 +769,13 @@ class Client extends BaseModel implements HasLocalePreference $defaults['public_notes'] = $this->public_notes; } + $exchange_rate = new CurrencyApi(); + $defaults['exchange_rate'] = 1/$exchange_rate->exchangeRate($this->getSetting('currency_id'), $this->company->settings->currency_id); + return $defaults; } - public function timezone_offset() + public function timezone_offset() :int { $offset = 0; @@ -853,7 +805,7 @@ class Client extends BaseModel implements HasLocalePreference ]; } - public function translate_entity() + public function translate_entity() :string { return ctrans('texts.client'); } diff --git a/app/Models/ClientContact.php b/app/Models/ClientContact.php index 2de9ed0b9b20..4c0c054b8e1e 100644 --- a/app/Models/ClientContact.php +++ b/app/Models/ClientContact.php @@ -130,59 +130,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations - * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications - * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations - * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $client + * @mixin \Eloquent */ class ClientContact extends Authenticatable implements HasLocalePreference diff --git a/app/Models/ClientGatewayToken.php b/app/Models/ClientGatewayToken.php index cf5c6d324759..2b6950048d23 100644 --- a/app/Models/ClientGatewayToken.php +++ b/app/Models/ClientGatewayToken.php @@ -37,29 +37,6 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \App\Models\GatewayType|null $gateway_type * @property-read mixed $hashed_id * @property-read \App\Models\User $user - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel company() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken newModelQuery() - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken onlyTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken query() - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope() - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereClientId($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereCompanyGatewayId($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereCompanyId($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereGatewayCustomerReference($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereGatewayTypeId($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereIsDefault($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereIsDeleted($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereMeta($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereRoutingNumber($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereToken($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken withTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|ClientGatewayToken withoutTrashed() * @mixin \Eloquent */ class ClientGatewayToken extends BaseModel diff --git a/app/Models/Company.php b/app/Models/Company.php index cce115b25c9c..d8c0c9172a7e 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -393,17 +393,17 @@ class Company extends BaseModel return $this->morphMany(Document::class, 'documentable'); } - public function schedulers() + public function schedulers() :HasMany { return $this->hasMany(Scheduler::class); } - public function task_schedulers() //alias for schedulers + public function task_schedulers() :HasMany { return $this->hasMany(Scheduler::class); } - public function all_documents() + public function all_documents() :HasMany { return $this->hasMany(Document::class); } @@ -413,22 +413,22 @@ class Company extends BaseModel return self::class; } - public function ledger() + public function ledger() :HasMany { return $this->hasMany(CompanyLedger::class); } - public function bank_integrations() + public function bank_integrations() :HasMany { return $this->hasMany(BankIntegration::class); } - public function bank_transactions() + public function bank_transactions() :HasMany { return $this->hasMany(BankTransaction::class); } - public function bank_transaction_rules() + public function bank_transaction_rules() :HasMany { return $this->hasMany(BankTransactionRule::class); } @@ -443,7 +443,7 @@ class Company extends BaseModel return $this->belongsTo(Account::class); } - public function client_contacts() + public function client_contacts() :HasMany { return $this->hasMany(ClientContact::class)->withTrashed(); } @@ -453,27 +453,27 @@ class Company extends BaseModel return $this->hasManyThrough(User::class, CompanyUser::class, 'company_id', 'id', 'id', 'user_id')->withTrashed(); } - public function expense_categories() + public function expense_categories() :HasMany { return $this->hasMany(ExpenseCategory::class)->withTrashed(); } - public function subscriptions() + public function subscriptions() :HasMany { return $this->hasMany(Subscription::class)->withTrashed(); } - public function purchase_orders() + public function purchase_orders() :HasMany { return $this->hasMany(PurchaseOrder::class)->withTrashed(); } - public function task_statuses() + public function task_statuses() :HasMany { return $this->hasMany(TaskStatus::class)->withTrashed(); } - public function clients() + public function clients() :HasMany { return $this->hasMany(Client::class)->withTrashed(); } @@ -481,12 +481,12 @@ class Company extends BaseModel /** * @return HasMany */ - public function tasks() + public function tasks() :HasMany { return $this->hasMany(Task::class)->withTrashed(); } - public function webhooks() + public function webhooks() :HasMany { return $this->hasMany(Webhook::class); } @@ -494,7 +494,7 @@ class Company extends BaseModel /** * @return HasMany */ - public function projects() + public function projects() :HasMany { return $this->hasMany(Project::class)->withTrashed(); } @@ -502,17 +502,17 @@ class Company extends BaseModel /** * @return HasMany */ - public function vendors() + public function vendors() :HasMany { return $this->hasMany(Vendor::class)->withTrashed(); } - public function all_activities() + public function all_activities() :HasMany { return $this->hasMany(Activity::class); } - public function activities() + public function activities() :HasMany { return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(50); } diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index b104cbccdada..fa5de69aee3a 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -28,7 +28,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property int|null $update_details * @property int $is_deleted * @property string $config - * @property object $fees_and_limits + * @property mixed $fees_and_limits * @property string|null $custom_value1 * @property string|null $custom_value2 * @property string|null $custom_value3 @@ -232,7 +232,8 @@ class CompanyGateway extends BaseModel /* This is the public entry point into the payment superclass */ public function driver(Client $client = null) { - $class = static::driver_class(); +// $class = static::driver_class(); +$class = self::driver_class(); if (!$class) { return false; @@ -343,10 +344,10 @@ class CompanyGateway extends BaseModel return ! empty($this->getConfigField('enable_pay_pal')); } - public function feesEnabled() - { - return floatval($this->fee_amount) || floatval($this->fee_percent); - } + // public function feesEnabled() + // { + // return floatval($this->fee_amount) || floatval($this->fee_percent); + // } /** * Returns the current test mode of the gateway. diff --git a/app/Models/CompanyToken.php b/app/Models/CompanyToken.php index b3564f1e7b3b..d4b879a9eb23 100644 --- a/app/Models/CompanyToken.php +++ b/app/Models/CompanyToken.php @@ -53,7 +53,6 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @method static \Illuminate\Database\Eloquent\Builder|CompanyToken whereUserId($value) * @method static \Illuminate\Database\Eloquent\Builder|CompanyToken withTrashed() * @method static \Illuminate\Database\Eloquent\Builder|CompanyToken withoutTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|CompanyToken with() * @mixin \Eloquent */ class CompanyToken extends BaseModel diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index 56603bf3f05b..79677bf45b5c 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -180,7 +180,6 @@ class Gateway extends StaticModel GatewayType::SEPA => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']], GatewayType::INSTANT_BANK_PAY => ['refund' => false, 'token_billing' => true, 'webhooks' => [' ']], ]; - break; case 58: return [ GatewayType::HOSTED_PAGE => ['refund' => false, 'token_billing' => false, 'webhooks' => [' ']], // Razorpay @@ -190,6 +189,20 @@ class Gateway extends StaticModel GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true], // Forte GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']], ]; + case 60: + return [ + GatewayType::PAYPAL => ['refund' => false, 'token_billing' => false], + GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => false], + GatewayType::VENMO => ['refund' => false, 'token_billing' => false], + // GatewayType::SEPA => ['refund' => false, 'token_billing' => false], + // GatewayType::BANCONTACT => ['refund' => false, 'token_billing' => false], + // GatewayType::EPS => ['refund' => false, 'token_billing' => false], + // GatewayType::MYBANK => ['refund' => false, 'token_billing' => false], + // GatewayType::PAYLATER => ['refund' => false, 'token_billing' => false], + // GatewayType::PRZELEWY24 => ['refund' => false, 'token_billing' => false], + // GatewayType::SOFORT => ['refund' => false, 'token_billing' => false], + ]; //Paypal + default: return []; } diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 2ca4159cd3e1..bc207c4955ac 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -29,17 +29,6 @@ namespace App\Models; * @method static \Illuminate\Database\Eloquent\Builder|GatewayType whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|GatewayType whereName($value) * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods - * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @mixin \Eloquent */ class GatewayType extends StaticModel @@ -94,6 +83,14 @@ class GatewayType extends StaticModel const BACS = 24; + const VENMO = 25; + + const MERCADOPAGO = 26; + + const MYBANK = 27; + + const PAYLATER = 28; + public function gateway() { return $this->belongsTo(Gateway::class); @@ -153,9 +150,17 @@ class GatewayType extends StaticModel return ctrans('texts.fpx'); case self::KLARNA: return ctrans('texts.klarna'); + case self::VENMO: + return ctrans('texts.payment_type_Venmo'); + case self::MERCADOPAGO: + return ctrans('texts.mercado_pago'); + case self::MYBANK: + return ctrans('texts.mybank'); + case self::PAYLATER: + return ctrans('texts.paypal_paylater'); default: return ' '; - break; } } } + diff --git a/app/Models/PaymentTerm.php b/app/Models/PaymentTerm.php index d7edf3fa4a27..8c107801c5a3 100644 --- a/app/Models/PaymentTerm.php +++ b/app/Models/PaymentTerm.php @@ -57,9 +57,6 @@ class PaymentTerm extends BaseModel */ public $timestamps = true; - /** - * @var array - */ protected $fillable = ['num_days']; public function getNumDays() diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php index 395adfc4a5da..c836091dc421 100644 --- a/app/Models/Presenters/CompanyPresenter.php +++ b/app/Models/Presenters/CompanyPresenter.php @@ -25,8 +25,6 @@ class CompanyPresenter extends EntityPresenter */ public function name() { - $settings = $this->entity->settings; - return $this->settings->name ?: ctrans('texts.untitled_account'); } diff --git a/app/Models/Presenters/InvoicePresenter.php b/app/Models/Presenters/InvoicePresenter.php index eaa34ad9e563..a7ce9222e30f 100644 --- a/app/Models/Presenters/InvoicePresenter.php +++ b/app/Models/Presenters/InvoicePresenter.php @@ -53,7 +53,7 @@ class InvoicePresenter extends EntityPresenter $properties->itemized_receipt[] = $this->itemRbits($item); } - $data = new stdClass(); + $data = new \stdClass(); $data->receive_time = time(); $data->type = 'transaction_details'; $data->source = 'user'; @@ -64,7 +64,7 @@ class InvoicePresenter extends EntityPresenter public function itemRbits($item) { - $data = new stdClass(); + $data = new \stdClass(); $data->description = $item->notes; $data->item_price = floatval($item->cost); $data->quantity = floatval($item->quantity); diff --git a/app/Models/Project.php b/app/Models/Project.php index 7e88c884072e..03f33215041b 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -106,12 +106,6 @@ class Project extends BaseModel use PresentableTrait; use Filterable; - /** - * @var array - */ - /** - * @var array - */ protected $fillable = [ 'name', 'client_id', diff --git a/app/Models/StaticModel.php b/app/Models/StaticModel.php index bc3cf3ea0c61..f5be46c52558 100644 --- a/app/Models/StaticModel.php +++ b/app/Models/StaticModel.php @@ -22,13 +22,13 @@ use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundExceptio * @property-read mixed $id * @method static \Illuminate\Database\Eloquent\Builder|StaticModel company() * @method static \Illuminate\Database\Eloquent\Builder|StaticModel exclude($columns) - * @method static \Illuminate\Database\Eloquent\Builder|StaticModel newModelQuery() - * @method static \Illuminate\Database\Eloquent\Builder|StaticModel newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|StaticModel query() - * @method static \Illuminate\Database\Eloquent\Builder|StaticModel find() - * @method static \Illuminate\Database\Eloquent\Builder|StaticModel with() - * @method static \Illuminate\Database\Eloquent\Builder|StaticModel withTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|StaticModel findOrFail() + * @method static \Illuminate\Database\Eloquent\Builder|StaticModel newModelQuery($value) + * @method static \Illuminate\Database\Eloquent\Builder|StaticModel newQuery($value) + * @method static \Illuminate\Database\Eloquent\Builder|StaticModel query($value) + * @method static \Illuminate\Database\Eloquent\Builder|StaticModel find($value) + * @method static \Illuminate\Database\Eloquent\Builder|StaticModel with($value) + * @method static \Illuminate\Database\Eloquent\Builder|StaticModel withTrashed($value) + * @method static \Illuminate\Database\Eloquent\Builder|StaticModel findOrFail($value) * @mixin \Eloquent */ class StaticModel extends Model diff --git a/app/Models/User.php b/app/Models/User.php index da37482dbb94..9ceb5f9ad2f3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -98,6 +98,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable; * @method static \Illuminate\Database\Eloquent\Builder|User newQuery() * @method static \Illuminate\Database\Eloquent\Builder|User onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|User query() + * @method static \Illuminate\Database\Eloquent\Builder|User where($column, $value) * @method static \Illuminate\Database\Eloquent\Builder|User whereAcceptedTermsVersion($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereAccountId($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereAvatar($value) @@ -336,13 +337,6 @@ class User extends Authenticatable implements MustVerifyEmail return $this->getCompany(); } - // private function setCompanyByGuard() - // { - // if (Auth::guard('contact')->check()) { - // $this->setCompany(auth()->user()->client->company); - // } - // } - public function company_users() { return $this->hasMany(CompanyUser::class)->withTrashed(); diff --git a/app/Observers/ClientObserver.php b/app/Observers/ClientObserver.php index 0b06b2cf3dee..e33316e1833a 100644 --- a/app/Observers/ClientObserver.php +++ b/app/Observers/ClientObserver.php @@ -88,7 +88,7 @@ class ClientObserver { /** Monitor postal code changes for US based clients for tax calculations */ - if($client->getOriginal('postal_code') != $client->postal_code && $client->country_id == 840 && $client->company->calculate_taxes && !$client->company->account->isFreeHostedClient()) { + if(($client->getOriginal('shipping_postal_code') != $client->shipping_postal_code || $client->getOriginal('postal_code') != $client->postal_code) && $client->country_id == 840 && $client->company->calculate_taxes && !$client->company->account->isFreeHostedClient()) { UpdateTaxData::dispatch($client, $client->company); } diff --git a/app/Observers/SubscriptionObserver.php b/app/Observers/SubscriptionObserver.php index 895ea59dfcc9..dece57bf8858 100644 --- a/app/Observers/SubscriptionObserver.php +++ b/app/Observers/SubscriptionObserver.php @@ -16,9 +16,9 @@ use App\Models\Subscription; class SubscriptionObserver { /** - * Handle the billing_subscription "created" event. + * Handle the subscription "created" event. * - * @param Subscription $billing_subscription + * @param Subscription $subscription * @return void */ public function created(Subscription $subscription) @@ -27,9 +27,9 @@ class SubscriptionObserver } /** - * Handle the billing_subscription "updated" event. + * Handle the subscription "updated" event. * - * @param Subscription $billing_subscription + * @param Subscription $subscription * @return void */ public function updated(Subscription $subscription) @@ -38,9 +38,9 @@ class SubscriptionObserver } /** - * Handle the billing_subscription "deleted" event. + * Handle the subscription "deleted" event. * - * @param Subscription $billing_subscription + * @param Subscription $subscription * @return void */ public function deleted(Subscription $subscription) @@ -49,9 +49,9 @@ class SubscriptionObserver } /** - * Handle the billing_subscription "restored" event. + * Handle the subscription "restored" event. * - * @param Subscription $billing_subscription + * @param Subscription $subscription * @return void */ public function restored(Subscription $subscription) @@ -60,9 +60,9 @@ class SubscriptionObserver } /** - * Handle the billing_subscription "force deleted" event. + * Handle the subscription "force deleted" event. * - * @param Subscription $billing_subscription + * @param Subscription $subscription * @return void */ public function forceDeleted(Subscription $subscription) diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php new file mode 100644 index 000000000000..6e257c8e715b --- /dev/null +++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php @@ -0,0 +1,432 @@ + 'paypal', + 1 => 'card', + 25 => 'venmo', + 9 => 'sepa', + 12 => 'bancontact', + 17 => 'eps', + 15 => 'giropay', + 13 => 'ideal', + 26 => 'mercadopago', + 27 => 'mybank', + 28 => 'paylater', + 16 => 'p24', + 7 => 'sofort' + ]; + + + public function gatewayTypes() + { + + $funding_options = []; + + foreach ($this->company_gateway->fees_and_limits as $key => $value) { + if ($value->is_enabled) { + $funding_options[] = $key; + } + } + + return $funding_options; + + } + + public function init() + { + $this->omnipay_gateway = Omnipay::create( + $this->company_gateway->gateway->provider + ); + + $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig()); + + $this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; + + return $this; + } + + public function setPaymentMethod($payment_method_id) + { + if(!$payment_method_id) + return $this; + + $this->paypal_payment_method = $this->funding_options[$payment_method_id]; + + return $this; + } + + public function authorizeView($payment_method) + { + // PayPal doesn't support direct authorization. + + return $this; + } + + public function authorizeResponse($request) + { + // PayPal doesn't support direct authorization. + + return $this; + } + + public function processPaymentView($data) + { + $this->init(); + + $data['gateway'] = $this; + + $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); + $this->payment_hash->save(); + + $data['client_id'] = $this->company_gateway->getConfigField('clientId'); + $data['token'] = $this->getClientToken(); + $data['order_id'] = $this->createOrder($data); + $data['funding_options'] = $this->paypal_payment_method; + + return render('gateways.paypal.pay', $data); + + } + + private function getFundingOptions():string + { + + $enums = [ + 3 => 'paypal', + 1 => 'card', + 25 => 'venmo', + // 9 => 'sepa', + // 12 => 'bancontact', + // 17 => 'eps', + // 15 => 'giropay', + // 13 => 'ideal', + // 26 => 'mercadopago', + // 27 => 'mybank', + // 28 => 'paylater', + // 16 => 'p24', + // 7 => 'sofort' + ]; + + $funding_options = ''; + + foreach($this->company_gateway->fees_and_limits as $key => $value) { + + if($value->is_enabled) { + + $funding_options .=$enums[$key].','; + + } + + } + + return rtrim($funding_options, ','); + + } + + public function processPaymentResponse($request) + { + + $response = json_decode($request['gateway_response'], true); + + if($response['status'] == 'COMPLETED' && isset($response['purchase_units'])){ + + $data = [ + 'payment_type' => PaymentType::PAYPAL, + 'amount' => $response['purchase_units'][0]['amount']['value'], + 'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'], + 'gateway_type_id' => GatewayType::PAYPAL, + ]; + + $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); + + SystemLogger::dispatch( + ['response' => $response, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company, + ); + + return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); + + } + else { + + SystemLogger::dispatch( + ['response' => $response], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company, + ); + + + throw new PaymentFailed('Payment failed. Please try again.', 401); + } + } + + private function getClientToken(): string + { + + $r = $this->gatewayRequest('/v1/identity/generate-token', 'post', ['body' => '']); + + if($r->successful()) + return $r->json()['client_token']; + + throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401); + + } + + private function createOrder(array $data): string + { + + $_invoice = collect($this->payment_hash->data->invoices)->first(); + + $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); + + $order = [ + "intent" => "CAPTURE", + "payer" => [ + "name" => [ + "given_name" => $this->client->present()->first_name(), + "surname" => $this->client->present()->last_name(), + ], + "email_address" => $this->client->present()->email(), + "address" => [ + "address_line_1" => $this->client->address1, + "address_line_2" => $this->client->address2, + "admin_area_1" => $this->client->city, + "admin_area_2" => $this->client->state, + "postal_code" => $this->client->postal_code, + "country_code" => $this->client->country->iso_3166_2, + ] + ], + "purchase_units" => [ + [ + "description" =>ctrans('texts.invoice_number').'# '.$invoice->number, + "invoice_id" => $invoice->number, + "amount" => [ + "value" => (string)$data['amount_with_fee'], + "currency_code"=> $this->client->currency()->code, + "breakdown" => [ + "item_total" => [ + "currency_code" => $this->client->currency()->code, + "value" => (string)$data['amount_with_fee'] + ] + ] + ], + "items"=> [ + [ + "name" => ctrans('texts.invoice_number').'# '.$invoice->number, + "quantity" => "1", + "unit_amount" => [ + "currency_code" => $this->client->currency()->code, + "value" => (string)$data['amount_with_fee'] + ], + ], + ], + ] + ] + ]; + + $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order); + + return $r->json()['id']; + + } + + public function gatewayRequest(string $uri, string $verb, array $data, ?array $headers = []) + { + $r = Http::withToken($this->omnipay_gateway->getToken()) + ->withHeaders($this->getHeaders($headers)) + ->{$verb}("{$this->api_endpoint_url}{$uri}", $data); + + if($r->successful()) + return $r; + + throw new PaymentFailed("Gateway failure - {$r->body()}", 401); + + } + + private function getHeaders(array $headers = []): array + { + return array_merge([ + 'Accept' => 'application/json', + 'Content-type' => 'application/json', + 'Accept-Language' => 'en_US', + ], $headers); + } + + /* + public function processPaymentResponse($request) + { + $this->initializeOmnipayGateway(); + + $response = $this->omnipay_gateway + ->completePurchase(['amount' => $this->payment_hash->data->amount, 'currency' => $this->client->getCurrencyCode()]) + ->send(); + + if ($response->isCancelled() && $this->client->getSetting('enable_client_portal')) { + return redirect()->route('client.invoices.index')->with('warning', ctrans('texts.status_cancelled')); + } elseif ($response->isCancelled() && !$this->client->getSetting('enable_client_portal')) { + redirect()->route('client.invoices.show', ['invoice' => $this->payment_hash->fee_invoice])->with('warning', ctrans('texts.status_cancelled')); + } + + if ($response->isSuccessful()) { + $data = [ + 'payment_method' => $response->getData()['TOKEN'], + 'payment_type' => PaymentType::PAYPAL, + 'amount' => $this->payment_hash->data->amount, + 'transaction_reference' => $response->getTransactionReference(), + 'gateway_type_id' => GatewayType::PAYPAL, + ]; + + $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); + + SystemLogger::dispatch( + ['response' => (array) $response->getData(), 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company, + ); + + return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); + } + + if (! $response->isSuccessful()) { + $data = $response->getData(); + + $this->sendFailureMail($response->getMessage() ?: ''); + + $message = [ + 'server_response' => $data['L_LONGMESSAGE0'], + 'data' => $this->payment_hash->data, + ]; + + SystemLogger::dispatch( + $message, + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company, + ); + + throw new PaymentFailed($response->getMessage(), $response->getCode()); + } + } + + public function generatePaymentDetails(array $data) + { + $_invoice = collect($this->payment_hash->data->invoices)->first(); + $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); + + // $this->fee = $this->feeCalc($invoice, $data['total']['amount_with_fee']); + + return [ + 'currency' => $this->client->getCurrencyCode(), + 'transactionType' => 'Purchase', + 'clientIp' => request()->getClientIp(), + // 'amount' => round(($data['total']['amount_with_fee'] + $this->fee),2), + 'amount' => round($data['total']['amount_with_fee'], 2), + 'returnUrl' => route('client.payments.response', [ + 'company_gateway_id' => $this->company_gateway->id, + 'payment_hash' => $this->payment_hash->hash, + 'payment_method_id' => GatewayType::PAYPAL, + ]), + 'cancelUrl' => $this->client->company->domain()."/client/invoices/{$invoice->hashed_id}", + 'description' => implode(',', collect($this->payment_hash->data->invoices) + ->map(function ($invoice) { + return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->invoice_number); + })->toArray()), + 'transactionId' => $this->payment_hash->hash.'-'.time(), + 'ButtonSource' => 'InvoiceNinja_SP', + 'solutionType' => 'Sole', + ]; + } + + public function generatePaymentItems(array $data) + { + $_invoice = collect($this->payment_hash->data->invoices)->first(); + $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); + + $items = []; + + $items[] = new Item([ + 'name' => ' ', + 'description' => ctrans('texts.invoice_number').'# '.$invoice->number, + 'price' => $data['total']['amount_with_fee'], + 'quantity' => 1, + ]); + + return $items; + } + + */ + + private function feeCalc($invoice, $invoice_total) + { + $invoice->service()->removeUnpaidGatewayFees(); + $invoice = $invoice->fresh(); + + $balance = floatval($invoice->balance); + + $_updated_invoice = $invoice->service()->addGatewayFee($this->company_gateway, GatewayType::PAYPAL, $invoice_total)->save(); + + if (floatval($_updated_invoice->balance) > $balance) { + $fee = floatval($_updated_invoice->balance) - $balance; + + $this->payment_hash->fee_total = $fee; + $this->payment_hash->save(); + + return $fee; + } + + return 0; + } + + +} diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index 15c93b5fe556..13df4b4d8435 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -165,6 +165,7 @@ class BaseRepository if (! $model->id) { $company_defaults = $client->setCompanyDefaults($data, lcfirst($resource)); + $data['exchange_rate'] = $company_defaults['exchange_rate']; $model->uses_inclusive_taxes = $client->getSetting('inclusive_taxes'); $data = array_merge($company_defaults, $data); } diff --git a/app/Services/ClientPortal/InstantPayment.php b/app/Services/ClientPortal/InstantPayment.php index 284e79d93a24..c9366ad83b46 100644 --- a/app/Services/ClientPortal/InstantPayment.php +++ b/app/Services/ClientPortal/InstantPayment.php @@ -175,8 +175,11 @@ class InstantPayment } if ($this->request->has('signature') && ! is_null($this->request->signature) && ! empty($this->request->signature)) { - $invoices->each(function ($invoice) { - InjectSignature::dispatch($invoice, $this->request->signature); + + $contact_id = auth()->guard('contact')->user() ? auth()->guard('contact')->user()->id : null; + + $invoices->each(function ($invoice) use($contact_id) { + InjectSignature::dispatch($invoice, $contact_id, $this->request->signature, request()->getClientIp()); }); } diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index d5a0715ee7d2..fa4fb4036187 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -78,9 +78,9 @@ class InvoiceService * Sets the exchange rate on the invoice if the client currency * is different to the company currency. */ - public function setExchangeRate() + public function setExchangeRate($force = false) { - if ($this->invoice->exchange_rate != 1) { + if ($this->invoice->exchange_rate != 1 || $force) { return $this; } diff --git a/app/Services/Quote/MarkSent.php b/app/Services/Quote/MarkSent.php index 9969cbfc3f41..c6a615efd9a5 100644 --- a/app/Services/Quote/MarkSent.php +++ b/app/Services/Quote/MarkSent.php @@ -11,22 +11,17 @@ namespace App\Services\Quote; -use App\Events\Quote\QuoteWasMarkedSent; -use App\Models\Quote; -use App\Models\Webhook; -use App\Utils\Ninja; use Carbon\Carbon; +use App\Utils\Ninja; +use App\Models\Quote; +use App\Models\Client; +use App\Models\Webhook; +use App\Events\Quote\QuoteWasMarkedSent; class MarkSent { - private $client; - - private $quote; - - public function __construct($client, $quote) + public function __construct(private Client $client, private Quote $quote) { - $this->client = $client; - $this->quote = $quote; } public function run() @@ -38,9 +33,9 @@ class MarkSent $this->quote->markInvitationsSent(); - if ($this->quote->due_date != '' || $this->quote->client->getSetting('valid_until') == '') { + if ($this->quote->due_date != '' || $this->client->getSetting('valid_until') == '') { } else { - $this->quote->due_date = Carbon::parse($this->quote->date)->addDays($this->quote->client->getSetting('valid_until')); + $this->quote->due_date = Carbon::parse($this->quote->date)->addDays($this->client->getSetting('valid_until')); } $this->quote diff --git a/app/Services/Report/ARSummaryReport.php b/app/Services/Report/ARSummaryReport.php index ebbb891cfee7..0fd0d5b83dfb 100644 --- a/app/Services/Report/ARSummaryReport.php +++ b/app/Services/Report/ARSummaryReport.php @@ -19,7 +19,6 @@ use App\Models\Invoice; use App\Utils\Ninja; use App\Utils\Number; use App\Utils\Traits\MakesDates; -use Carbon\Carbon; use Illuminate\Support\Facades\App; use League\Csv\Writer; @@ -33,6 +32,8 @@ class ARSummaryReport extends BaseExport public Client $client; + private float $total = 0; + public array $report_keys = [ 'client_name', 'client_number', @@ -101,7 +102,7 @@ class ARSummaryReport extends BaseExport { $this->client = $client; - return [ + $row = [ $this->client->present()->name(), $this->client->number, $this->client->id_number, @@ -111,7 +112,12 @@ class ARSummaryReport extends BaseExport $this->getAgingAmount('90'), $this->getAgingAmount('120'), $this->getAgingAmount('120+'), + Number::formatMoney($this->total, $this->client), ]; + + $this->total = 0; + + return $row; } private function getCurrent(): string @@ -128,6 +134,8 @@ class ARSummaryReport extends BaseExport }) ->sum('balance'); + $this->total += $amount; + return Number::formatMoney($amount, $this->client); } @@ -153,6 +161,8 @@ class ARSummaryReport extends BaseExport ->whereBetween('due_date', [$to, $from]) ->sum('balance'); + $this->total += $amount; + return Number::formatMoney($amount, $this->client); } diff --git a/app/Services/Report/ProfitLoss.php b/app/Services/Report/ProfitLoss.php index 26d4f188ab7f..d070b7636614 100644 --- a/app/Services/Report/ProfitLoss.php +++ b/app/Services/Report/ProfitLoss.php @@ -366,7 +366,7 @@ class ProfitLoss $csv->insertOne(['--------------------']); - $csv->insertOne([ctrans('texts.total_revenue'), Number::formatMoney($this->income, $this->company)]); + $csv->insertOne([ctrans('texts.total_revenue'), Number::formatMoney($this->income - $this->income_taxes, $this->company)]); //total taxes @@ -386,7 +386,7 @@ class ProfitLoss $csv->insertOne([ctrans('texts.total_taxes'), Number::formatMoney(array_sum(array_column($this->expense_break_down, 'tax')), $this->company)]); $csv->insertOne(['--------------------']); - $csv->insertOne([ctrans('texts.total_profit'), Number::formatMoney($this->income - array_sum(array_column($this->expense_break_down, 'total')), $this->company)]); + $csv->insertOne([ctrans('texts.total_profit'), Number::formatMoney($this->income - $this->income_taxes - array_sum(array_column($this->expense_break_down, 'total'))- array_sum(array_column($this->expense_break_down, 'tax')), $this->company)]); //net profit diff --git a/app/Utils/Traits/Inviteable.php b/app/Utils/Traits/Inviteable.php index ee1cffcca78b..ed7c7056845c 100644 --- a/app/Utils/Traits/Inviteable.php +++ b/app/Utils/Traits/Inviteable.php @@ -68,6 +68,8 @@ trait Inviteable $qr = $writer->writeString($this->getPaymentLink(), 'utf-8'); + return "
$qr
"; + return " {$qr}"; } diff --git a/app/Utils/Traits/MakesHash.php b/app/Utils/Traits/MakesHash.php index 72b138795f87..63c151b93530 100644 --- a/app/Utils/Traits/MakesHash.php +++ b/app/Utils/Traits/MakesHash.php @@ -62,7 +62,7 @@ trait MakesHash return $hashids->encode($value); } - public function decodePrimaryKey($value) : string + public function decodePrimaryKey($value) { try { $hashids = new Hashids(config('ninja.hash_salt'), 10); @@ -71,7 +71,6 @@ trait MakesHash if (! is_array($decoded_array)) { throw new \Exception('Invalid Primary Key'); - //response()->json(['error'=>'Invalid primary key'], 400); } return $decoded_array[0]; diff --git a/config/ninja.php b/config/ninja.php index d092d5e8571b..086fae76a203 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -84,6 +84,7 @@ return [ 'password' => 'password', 'stripe' => env('STRIPE_KEYS', ''), 'paypal' => env('PAYPAL_KEYS', ''), + 'paypal_rest' => env('PAYPAL_REST_KEYS', ''), 'authorize' => env('AUTHORIZE_KEYS', ''), 'checkout' => env('CHECKOUT_KEYS', ''), 'travis' => env('TRAVIS', false), diff --git a/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php b/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php new file mode 100644 index 000000000000..df40da51c578 --- /dev/null +++ b/database/migrations/2023_06_20_123355_add_paypal_rest_payment_driver.php @@ -0,0 +1,48 @@ +clientId = ""; + $fields->secret = ""; + $fields->testMode = false; + + $paypal = new Gateway; + $paypal->id = 60; + $paypal->name = 'PayPal REST'; + $paypal->key = '80af24a6a691230bbec33e930ab40665'; + $paypal->provider = 'PayPal_Rest'; + $paypal->is_offsite = false; + $paypal->fields = \json_encode($fields); + $paypal->visible = 1; + $paypal->site_url = 'https://www.paypal.com/'; + $paypal->save(); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +}; diff --git a/database/seeders/PaymentLibrariesSeeder.php b/database/seeders/PaymentLibrariesSeeder.php index b7420fb8c4ee..4a096febef58 100644 --- a/database/seeders/PaymentLibrariesSeeder.php +++ b/database/seeders/PaymentLibrariesSeeder.php @@ -83,6 +83,7 @@ class PaymentLibrariesSeeder extends Seeder ['id' => 57, 'name' => 'Square', 'provider' => 'Square', 'is_offsite' => false, 'sort_order' => 21, 'key' => '65faab2ab6e3223dbe848b1686490baz', 'fields' => '{"accessToken":"","applicationId":"","locationId":"","testMode":false}'], ['id' => 58, 'name' => 'Razorpay', 'provider' => 'Razorpay', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9', 'fields' => '{"apiKey":"","apiSecret":""}'], ['id' => 59, 'name' => 'Forte', 'provider' => 'Forte', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'kivcvjexxvdiyqtj3mju5d6yhpeht2xs', 'fields' => '{"testMode":false,"apiLoginId":"","apiAccessId":"","secureKey":"","authOrganizationId":"","organizationId":"","locationId":""}'], + ['id' => 60, 'name' => 'PayPal REST', 'provider' => 'PayPal_Rest', 'key' => '80af24a6a691230bbec33e930ab40665', 'fields' => '{"clientId":"","secret":"","signature":"","testMode":false}'], ]; foreach ($gateways as $gateway) { @@ -99,7 +100,7 @@ class PaymentLibrariesSeeder extends Seeder Gateway::query()->update(['visible' => 0]); - Gateway::whereIn('id', [1,3,7,11,15,20,39,46,55,50,57,52,58,59])->update(['visible' => 1]); + Gateway::whereIn('id', [1,3,7,11,15,20,39,46,55,50,57,52,58,59,60])->update(['visible' => 1]); if (Ninja::isHosted()) { Gateway::whereIn('id', [20])->update(['visible' => 0]); diff --git a/lang/da/texts.php b/lang/da/texts.php index 1407fc804bc0..14151b4137dd 100644 --- a/lang/da/texts.php +++ b/lang/da/texts.php @@ -882,7 +882,7 @@ $LANG = array( 'custom_invoice_charges_helps' => 'Add a text input to the invoice create/edit page and include the charge in the invoice subtotals.', 'token_expired' => 'Validation token was expired. Please try again.', 'invoice_link' => 'Invoice Link', - 'button_confirmation_message' => 'Confirm your email.', + 'button_confirmation_message' => 'Klik for at bekræfte din e-mail.', 'confirm' => 'Confirm', 'email_preferences' => 'Email Preferences', 'created_invoices' => 'Successfully created :count invoice(s)', @@ -1179,7 +1179,7 @@ $LANG = array( 'plan_started' => 'Plan Started', 'plan_expires' => 'Plan Expires', - 'white_label_button' => 'Purchase White Label', + 'white_label_button' => 'Køb whitelabel licens', 'pro_plan_year_description' => 'One year enrollment in the Invoice Ninja Pro Plan.', 'pro_plan_month_description' => 'One month enrollment in the Invoice Ninja Pro Plan.', @@ -1853,7 +1853,7 @@ $LANG = array( 'task' => 'Opgave', 'contact_name' => 'Kontakt navn', 'city_state_postal' => 'By/Postnummer', - 'postal_city' => 'Postal/City', + 'postal_city' => 'Postnr og by', 'custom_field' => 'Brugerdefineret felt', 'account_fields' => 'Virksomheds felter', 'facebook_and_twitter' => 'Facebook og Twitter', @@ -2060,8 +2060,8 @@ $LANG = array( 'group_when_sorted' => 'Group Sort', 'group_dates_by' => 'Gruppér datoer efter', 'year' => 'År', - 'view_statement' => 'View Statement', - 'statement' => 'Statement', + 'view_statement' => 'Se kontoudskrift', + 'statement' => 'Kontoudtog', 'statement_date' => 'Statement Date', 'mark_active' => 'Markér som aktiv', 'send_automatically' => 'Send automatisk', @@ -4972,7 +4972,7 @@ $LANG = array( 'xinvoice_payable' => 'Payable within :payeddue days net until :paydate', 'xinvoice_no_buyers_reference' => "No buyer's reference given", 'xinvoice_online_payment' => 'The invoice needs to be paid online via the provided link', - 'pre_payment' => 'Pre Payment', + 'pre_payment' => 'Forudbetaling', 'number_of_payments' => 'Number of payments', 'number_of_payments_helper' => 'The number of times this payment will be made', 'pre_payment_indefinitely' => 'Continue until cancelled', @@ -5113,6 +5113,16 @@ $LANG = array( 'item_tax_rate2' => 'Item Tax Rate 2', 'item_tax_rate3' => 'Item Tax Rate 3', 'buy_price' => 'Buy Price', + 'country_Macedonia' => 'Macedonia', + 'admin_initiated_payments' => 'Admin Initiated Payments', + 'admin_initiated_payments_help' => 'Support entering a payment in the admin portal without an invoice', + 'paid_date' => 'Paid Date', + 'downloaded_entities' => 'An email will be sent with the PDFs', + 'lang_French - Swiss' => 'French - Swiss', + 'currency_swazi_lilangeni' => 'Swazi Lilangeni', + 'income' => 'Income', + 'amount_received_help' => 'Enter a value here if the total amount received was MORE than the invoice amount, or when recording a payment with no invoices. Otherwise this field should be left blank.', + 'vendor_phone' => 'Vendor Phone', ); diff --git a/lang/en/texts.php b/lang/en/texts.php index de8f17ea971e..a1f4316e3dd1 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5124,6 +5124,17 @@ $LANG = array( 'income' => 'Income', 'amount_received_help' => 'Enter a value here if the total amount received was MORE than the invoice amount, or when recording a payment with no invoices. Otherwise this field should be left blank.', 'vendor_phone' => 'Vendor Phone', + 'mercado_pago' => 'Mercado Pago', + 'mybank' => 'MyBank', + 'paypal_paylater' => 'Pay in 4', + 'paid_date' => 'Paid Date', + 'district' => 'District', + 'region' => 'Region', + 'county' => 'County', + 'tax_details' => 'Tax Details', + 'activity_10_online' => ':contact entered payment :payment for invoice :invoice for :client', + 'activity_10_manual' => ':user entered payment :payment for invoice :invoice for :client', + 'default_payment_type' => 'Default Payment Type', ); diff --git a/lang/fr_CA/texts.php b/lang/fr_CA/texts.php index 94189ac73af5..609c4acafd9a 100644 --- a/lang/fr_CA/texts.php +++ b/lang/fr_CA/texts.php @@ -4947,8 +4947,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'click_to_variables' => 'Cliquez ici pour voir toutes les variables', 'ship_to' => 'Livrer à', 'stripe_direct_debit_details' => 'Veuillez transférer dans le compte bancaire indiqué ci-dessus.', - 'branch_name' => 'Nom de branche', - 'branch_code' => 'Code de branche', + 'branch_name' => 'Nom de succursale', + 'branch_code' => 'Code de succursale', 'bank_name' => 'Nom de l\'institution bancaire', 'bank_code' => 'Code de l\'institution bancaire', 'bic' => 'BIC', @@ -5114,7 +5114,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'lang_French - Swiss' => 'Français - Suisse', 'currency_swazi_lilangeni' => 'Lilangeni Eswatinien', 'income' => 'Revenu', - + 'amount_received_help' => 'Saisissez une valeur si le montant total reçu été PLUS élevé que le montant de la facture, ou lors de l\'enregistrement d\'un paiement sans factures. Sinon, ce champ devrait rester vide.', + 'vendor_phone' => 'Téléphone du fournisseur', ); diff --git a/lang/pt_BR/texts.php b/lang/pt_BR/texts.php index 670a7033b49d..4585f228c220 100644 --- a/lang/pt_BR/texts.php +++ b/lang/pt_BR/texts.php @@ -4569,7 +4569,7 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'activity_134' => ':user restored purchase order :purchase_order', 'activity_135' => ':user emailed purchase order :purchase_order', 'activity_136' => ':contact viewed purchase order :purchase_order', - 'purchase_order_subject' => 'New Purchase Order :number from :account', + 'purchase_order_subject' => 'Novo pedido de compra', 'purchase_order_message' => 'To view your purchase order for :amount, click the link below.', 'view_purchase_order' => 'View Purchase Order', 'purchase_orders_backup_subject' => 'Your purchase orders are ready for download', @@ -4639,7 +4639,7 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'purchase_order_footer' => 'Purchase Order Footer', 'require_purchase_order_signature' => 'Purchase Order Signature', 'require_purchase_order_signature_help' => 'Require vendor to provide their signature.', - 'new_purchase_order' => 'New Purchase Order', + 'new_purchase_order' => 'Novo pedido de compra', 'edit_purchase_order' => 'Edit Purchase Order', 'created_purchase_order' => 'Successfully created purchase order', 'updated_purchase_order' => 'Successfully updated purchase order', @@ -5108,6 +5108,16 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'item_tax_rate2' => 'Item Tax Rate 2', 'item_tax_rate3' => 'Item Tax Rate 3', 'buy_price' => 'Buy Price', + 'country_Macedonia' => 'Macedonia', + 'admin_initiated_payments' => 'Admin Initiated Payments', + 'admin_initiated_payments_help' => 'Support entering a payment in the admin portal without an invoice', + 'paid_date' => 'Paid Date', + 'downloaded_entities' => 'An email will be sent with the PDFs', + 'lang_French - Swiss' => 'French - Swiss', + 'currency_swazi_lilangeni' => 'Swazi Lilangeni', + 'income' => 'Income', + 'amount_received_help' => 'Enter a value here if the total amount received was MORE than the invoice amount, or when recording a payment with no invoices. Otherwise this field should be left blank.', + 'vendor_phone' => 'Vendor Phone', ); diff --git a/phpstan.neon b/phpstan.neon index 2372c3d259ae..cdfa3b25d911 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,19 +1,13 @@ includes: - ./vendor/nunomaduro/larastan/extension.neon - - phpstan-baseline.neon parameters: - treatPhpDocTypesAsCertain: false - parallel: - jobSize: 10 - maximumNumberOfProcesses: 1 - processTimeout: 60.0 - ignoreErrors: - - '#Call to an undefined method .*badMethod\(\)#' - - '#Call to an undefined method Illuminate\Database\Eloquent\Builder::exclude#' level: 4 paths: - 'app/' excludePaths: - 'vendor/' + - 'app/Jobs/Ninja/*' + - 'app/Console/Commands/*' + - 'app/DataMapper/Analytics/*' universalObjectCratesClasses: - App\DataMapper\Tax\RuleInterface \ No newline at end of file diff --git a/resources/views/portal/ninja2020/components/html-viewer.blade.php b/resources/views/portal/ninja2020/components/html-viewer.blade.php index 0b1ccdafed00..2841d3a17487 100644 --- a/resources/views/portal/ninja2020/components/html-viewer.blade.php +++ b/resources/views/portal/ninja2020/components/html-viewer.blade.php @@ -136,10 +136,12 @@ span { {{ ctrans('texts.total') }} {{ $amount }} + @if(!$is_quote) {{ ctrans('texts.balance') }} {{ $balance }} + @endif diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php new file mode 100644 index 000000000000..c94550d3f331 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -0,0 +1,68 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => 'PayPal']) + +@section('gateway_head') + + +@endsection + +@section('gateway_content') +
+ @csrf + + + + +
+ + + +
+ +@endsection + +@section('gateway_footer') +@endsection + +@push('footer') + + + +@endpush \ No newline at end of file diff --git a/tests/Feature/Export/ProfitAndLossReportTest.php b/tests/Feature/Export/ProfitAndLossReportTest.php index 52ba1b37342d..56d634e1beb4 100644 --- a/tests/Feature/Export/ProfitAndLossReportTest.php +++ b/tests/Feature/Export/ProfitAndLossReportTest.php @@ -120,6 +120,108 @@ class ProfitAndLossReportTest extends TestCase $this->account->delete(); } + public function testExpenseResolution() + { + $this->buildData(); + + Expense::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'amount' => 121, + 'date' => now()->format('Y-m-d'), + 'uses_inclusive_taxes' => true, + 'tax_rate1' => 21, + 'tax_name1' => 'VAT', + 'calculate_tax_by_amount' => false, + 'exchange_rate' => 1, + ]); + + $pl = new ProfitLoss($this->company, $this->payload); + $pl->build(); + + $expense_breakdown = $pl->getExpenseBreakDown(); + + $this->assertEquals(100, array_sum(array_column($expense_breakdown, 'total'))); + $this->assertEquals(21, array_sum(array_column($expense_breakdown, 'tax'))); + + $this->account->delete(); + + } + + public function testMultiCurrencyInvoiceIncome() + { + $this->buildData(); + + $settings = ClientSettings::defaults(); + $settings->currency_id = 2; + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'is_deleted' => 0, + 'settings' => $settings + ]); + + + $client2 = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'is_deleted' => 0, + ]); + + Invoice::factory()->create([ + 'client_id' => $client->id, + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'amount' => 10, + 'balance' => 10, + 'status_id' => 2, + 'total_taxes' => 1, + 'date' => now()->format('Y-m-d'), + 'terms' => 'nada', + 'discount' => 0, + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'uses_inclusive_taxes' => false, + 'exchange_rate' => 2 + ]); + + Invoice::factory()->create([ + 'client_id' => $client2->id, + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'amount' => 10, + 'balance' => 10, + 'status_id' => 2, + 'total_taxes' => 1, + 'date' => now()->format('Y-m-d'), + 'terms' => 'nada', + 'discount' => 0, + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'uses_inclusive_taxes' => false, + 'exchange_rate' => 1 + ]); + + + $pl = new ProfitLoss($this->company, $this->payload); + $pl->build(); + + $this->assertEquals(13.5, $pl->getIncome()); + $this->assertEquals(1.5, $pl->getIncomeTaxes()); + + $this->account->delete(); + + } + public function testSimpleInvoiceIncome() { $this->buildData(); diff --git a/tests/Feature/Export/ReportCsvGenerationTest.php b/tests/Feature/Export/ReportCsvGenerationTest.php index e11adfb447f4..53a9c3924255 100644 --- a/tests/Feature/Export/ReportCsvGenerationTest.php +++ b/tests/Feature/Export/ReportCsvGenerationTest.php @@ -70,6 +70,60 @@ class ReportCsvGenerationTest extends TestCase public $cu; + private $all_client_report_keys = ["client.name","client.user","client.assigned_user","client.balance","client.paid_to_date","client.currency_id","client.website","client.private_notes","client.industry_id","client.size_id","client.address1","client.address2","client.city","client.state","client.postal_code","client.country_id","contact.custom_value4","client.shipping_address1","client.shipping_address2","client.shipping_city","client.shipping_state","client.shipping_postal_code","client.shipping_country_id","client.payment_terms","client.vat_number","client.id_number","client.public_notes","client.phone","contact.first_name","contact.last_name","contact.email","contact.phone"]; + + private $all_payment_report_keys = [ + 'payment.date', + 'payment.amount', + 'payment.refunded', + 'payment.applied', + 'payment.transaction_reference', + 'payment.currency', + 'payment.exchange_rate', + 'payment.number', + 'payment.method', + 'payment.status', + 'payment.private_notes', + 'payment.custom_value1', + 'payment.custom_value2', + 'payment.custom_value3', + 'payment.custom_value4', + 'payment.user_id', + 'payment.assigned_user_id' + ]; + + private $all_invoice_report_keys = [ + 'invoice.number', + 'invoice.amount', + 'invoice.balance', + 'invoice.paid_to_date', + 'invoice.discount', + 'invoice.po_number', + 'invoice.date', + 'invoice.due_date', + 'invoice.terms', + 'invoice.footer', + 'invoice.status', + 'invoice.public_notes', + 'invoice.private_notes', + 'invoice.uses_inclusive_taxes', + 'invoice.is_amount_discount', + 'invoice.partial', + 'invoice.partial_due_date', + 'invoice.custom_value1', + 'invoice.custom_value2', + 'invoice.custom_value3', + 'invoice.custom_value4', + 'invoice.custom_surcharge1', + 'invoice.custom_surcharge2', + 'invoice.custom_surcharge3', + 'invoice.custom_surcharge4', + 'invoice.exchange_rate', + 'invoice.total_taxes', + 'invoice.assigned_user_id', + 'invoice.user_id', + ]; + /** * start_date - Y-m-d end_date - Y-m-d @@ -329,6 +383,32 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('123456', $this->getFirstValueByColumn($csv, 'Invoice Invoice Number')); $this->assertEquals(1000, $this->getFirstValueByColumn($csv, 'Invoice Amount')); + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_client_report_keys, + 'send_email' => false, + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/tasks', $data)->assertStatus(200); + + + $data = [ + 'date_range' => 'all', + 'report_keys' => array_merge(["task.date","task.number"], $this->all_invoice_report_keys), + 'send_email' => false, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/tasks', $data); + + } @@ -474,6 +554,33 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals(0, $this->getFirstValueByColumn($csv, 'Client Balance')); $this->assertEquals(100, $this->getFirstValueByColumn($csv, 'Client Paid to Date')); + + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_client_report_keys, + 'send_email' => false, + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/payments', $data)->assertStatus(200); + + + + $data = [ + 'date_range' => 'all', + 'report_keys' => array_merge(["payment.amount","payment.date"],$this->all_invoice_report_keys), + 'send_email' => false, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/payments', $data); + } @@ -603,6 +710,17 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('Unpaid', $this->getFirstValueByColumn($csv, 'Payment Amount')); $this->assertEquals('', $this->getFirstValueByColumn($csv, 'Payment Date')); + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_payment_report_keys, + 'send_email' => false, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/credits', $data)->assertStatus(200); + } public function testInvoiceCustomColumnsCsvGeneration() @@ -625,7 +743,7 @@ class ReportCsvGenerationTest extends TestCase $data = [ 'date_range' => 'all', - 'report_keys' => ["client.name","invoice.number","invoice.amount","payment.date", "payment.amount"], + 'report_keys' => ["client.name","invoice.number","invoice.amount","payment.date", "payment.amount","invoice.user"], 'send_email' => false, ]; @@ -641,6 +759,33 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('Unpaid', $this->getFirstValueByColumn($csv, 'Payment Amount')); $this->assertEquals('', $this->getFirstValueByColumn($csv, 'Payment Date')); + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_client_report_keys, + 'send_email' => false, + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/invoices', $data)->assertStatus(200); + + + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_payment_report_keys, + 'send_email' => false, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/invoices', $data)->assertStatus(200); + + } public function testRecurringInvoiceCustomColumnsCsvGeneration() @@ -679,6 +824,20 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Recurring Invoice Invoice Number')); $this->assertEquals('Daily', $this->getFirstValueByColumn($csv, 'Recurring Invoice How Often')); + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_client_report_keys, + 'send_email' => false, + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/recurring_invoices', $data)->assertStatus(200); + + } @@ -745,6 +904,33 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('GST', $this->getFirstValueByColumn($csv, 'Tax Name 1')); $this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Tax Rate 1')); + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_client_report_keys, + 'send_email' => false, + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/invoice_items', $data)->assertStatus(200); + + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_payment_report_keys, + 'send_email' => false, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/invoice_items', $data)->assertStatus(200); + + + } @@ -810,6 +996,20 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('GST', $this->getFirstValueByColumn($csv, 'Tax Name 1')); $this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Tax Rate 1')); + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_client_report_keys, + 'send_email' => false, + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/quote_items', $data)->assertStatus(200); + + } @@ -973,6 +1173,20 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Quote Number')); $this->assertEquals('100', $this->getFirstValueByColumn($csv, 'Quote Amount')); + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_client_report_keys, + 'send_email' => false, + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/quotes', $data)->assertStatus(200); + + } @@ -1104,6 +1318,33 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('Public', $this->getFirstValueByColumn($csv, 'Public Notes')); $this->assertEquals('Private', $this->getFirstValueByColumn($csv, 'Private Notes')); $this->assertEquals('Terms', $this->getFirstValueByColumn($csv, 'Terms')); + + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_client_report_keys, + 'send_email' => false, + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/credits', $data)->assertStatus(200); + + + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_payment_report_keys, + 'send_email' => false, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/credits', $data)->assertStatus(200); + } public function testInvoiceCsvGeneration() @@ -1364,6 +1605,21 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('Private', $this->getFirstValueByColumn($csv, 'Private Notes')); $this->assertEquals($this->user->present()->name(), $this->getFirstValueByColumn($csv, 'User')); + + $data = [ + 'date_range' => 'all', + 'report_keys' => $this->all_client_report_keys, + 'send_email' => false, + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/expenses', $data)->assertStatus(200); + + + } public function testExpenseCustomColumnsCsvGeneration()