mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
validator for ExpenseMailbox property
This commit is contained in:
parent
dd9727a701
commit
36279be694
@ -11,10 +11,17 @@
|
||||
|
||||
namespace App\Helpers\Mail\Webhook;
|
||||
|
||||
use App\Models\Company;
|
||||
|
||||
interface BaseWebhookHandler
|
||||
{
|
||||
public function process()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function matchCompany(string $email)
|
||||
{
|
||||
return Company::where("expense_mailbox", $email)->first();
|
||||
}
|
||||
}
|
||||
|
@ -11,12 +11,21 @@
|
||||
|
||||
namespace App\Helpers\Mail\Webhook\Postmark;
|
||||
|
||||
use App\Factory\ExpenseFactory;
|
||||
use App\Helpers\Mail\Webhook\BaseWebhookHandler;
|
||||
|
||||
interface PostmarkWebhookHandler extends BaseWebhookHandler
|
||||
{
|
||||
public function process()
|
||||
public function process($data)
|
||||
{
|
||||
|
||||
$email = '';
|
||||
|
||||
$company = $this->matchCompany($email);
|
||||
if (!$company)
|
||||
return false;
|
||||
|
||||
$expense = ExpenseFactory::create($company->id, $company->owner()->id);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace App\Http\Requests\Company;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\Company\ValidCompanyQuantity;
|
||||
use App\Http\ValidationRules\Company\ValidExpenseMailbox;
|
||||
use App\Http\ValidationRules\Company\ValidSubdomain;
|
||||
use App\Http\ValidationRules\ValidSettingsRule;
|
||||
use App\Models\Company;
|
||||
@ -28,7 +29,7 @@ class StoreCompanyRequest extends Request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
public function authorize(): bool
|
||||
{
|
||||
/** @var \App\Models\User auth()->user */
|
||||
$user = auth()->user();
|
||||
@ -55,6 +56,8 @@ class StoreCompanyRequest extends Request
|
||||
}
|
||||
}
|
||||
|
||||
$rules['expense_mailbox'] = new ValidExpenseMailbox($this->company->key, $this->company->account->isPaid() && $this->company->account->plan == 'enterprise'); // @turbo124 check if this is right
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ namespace App\Http\Requests\Company;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\Company\ValidExpenseMailbox;
|
||||
use App\Http\ValidationRules\Company\ValidSubdomain;
|
||||
use App\Http\ValidationRules\ValidSettingsRule;
|
||||
use App\Utils\Ninja;
|
||||
@ -35,7 +36,7 @@ class UpdateCompanyRequest extends Request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
public function authorize(): bool
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
@ -67,6 +68,8 @@ class UpdateCompanyRequest extends Request
|
||||
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain()];
|
||||
}
|
||||
|
||||
$rules['expense_mailbox'] = new ValidExpenseMailbox($this->company->key, $this->company->account->isPaid() && $this->company->account->plan == 'enterprise'); // @turbo124 check if this is right
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
@ -80,14 +83,14 @@ class UpdateCompanyRequest extends Request
|
||||
}
|
||||
|
||||
if (array_key_exists('settings', $input)) {
|
||||
$input['settings'] = (array)$this->filterSaveableSettings($input['settings']);
|
||||
$input['settings'] = (array) $this->filterSaveableSettings($input['settings']);
|
||||
}
|
||||
|
||||
if(array_key_exists('subdomain', $input) && $this->company->subdomain == $input['subdomain']) {
|
||||
if (array_key_exists('subdomain', $input) && $this->company->subdomain == $input['subdomain']) {
|
||||
unset($input['subdomain']);
|
||||
}
|
||||
|
||||
if(array_key_exists('e_invoice_certificate_passphrase', $input) && empty($input['e_invoice_certificate_passphrase'])) {
|
||||
if (array_key_exists('e_invoice_certificate_passphrase', $input) && empty($input['e_invoice_certificate_passphrase'])) {
|
||||
unset($input['e_invoice_certificate_passphrase']);
|
||||
}
|
||||
|
||||
@ -115,17 +118,17 @@ class UpdateCompanyRequest extends Request
|
||||
}
|
||||
|
||||
if (isset($settings['email_style_custom'])) {
|
||||
$settings['email_style_custom'] = str_replace(['{{','}}'], ['',''], $settings['email_style_custom']);
|
||||
$settings['email_style_custom'] = str_replace(['{{', '}}'], ['', ''], $settings['email_style_custom']);
|
||||
}
|
||||
|
||||
if (! $account->isFreeHostedClient()) {
|
||||
if (!$account->isFreeHostedClient()) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
$saveable_casts = CompanySettings::$free_plan_casts;
|
||||
|
||||
foreach ($settings as $key => $value) {
|
||||
if (! array_key_exists($key, $saveable_casts)) {
|
||||
if (!array_key_exists($key, $saveable_casts)) {
|
||||
unset($settings->{$key});
|
||||
}
|
||||
}
|
||||
@ -137,7 +140,7 @@ class UpdateCompanyRequest extends Request
|
||||
{
|
||||
if (Ninja::isHosted()) {
|
||||
$url = str_replace('http://', '', $url);
|
||||
$url = parse_url($url, PHP_URL_SCHEME) === null ? $scheme.$url : $url;
|
||||
$url = parse_url($url, PHP_URL_SCHEME) === null ? $scheme . $url : $url;
|
||||
}
|
||||
|
||||
return rtrim($url, '/');
|
||||
|
64
app/Http/ValidationRules/Company/ValidExpenseMailbox.php
Normal file
64
app/Http/ValidationRules/Company/ValidExpenseMailbox.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\ValidationRules\Company;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
/**
|
||||
* Class ValidCompanyQuantity.
|
||||
*/
|
||||
class ValidExpenseMailbox implements Rule
|
||||
{
|
||||
|
||||
private $validated_schema = false;
|
||||
private $company_key = false;
|
||||
private $isEnterprise = false;
|
||||
|
||||
public function __construct(string $company_key, bool $isEnterprise = false)
|
||||
{
|
||||
$this->company_key = $company_key;
|
||||
$this->isEnterprise = $isEnterprise;
|
||||
}
|
||||
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
if (empty($value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// early return, if we dont have any additional validation
|
||||
if (!config('ninja.inbound_expense.webhook.mailbox_schema') && !(Ninja::isHosted() && config('ninja.inbound_expense.webhook.mailbox_schema_enterprise'))) {
|
||||
$this->validated_schema = true;
|
||||
return MultiDB::checkExpenseMailboxAvailable($value);
|
||||
}
|
||||
|
||||
// Validate Schema
|
||||
$validated = !config('ninja.inbound_expense.webhook.mailbox_schema') || (preg_match(config('ninja.inbound_expense.webhook.mailbox_schema'), $value) && (!config('ninja.inbound_expense.webhook.mailbox_schema_hascompanykey') || str_contains($value, $this->company_key))) ? true : false;
|
||||
$validated_enterprise = !config('ninja.inbound_expense.webhook.mailbox_schema_enterprise') || (Ninja::isHosted() && $this->isEnterprise && preg_match(config('ninja.inbound_expense.webhook.mailbox_schema_enterprise'), $value));
|
||||
|
||||
if (!$validated && !$validated_enterprise)
|
||||
return false;
|
||||
|
||||
$this->validated_schema = true;
|
||||
return MultiDB::checkExpenseMailboxAvailable($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return $this->validated_schema ? ctrans('texts.expense_mailbox_taken') : ctrans('texts.expense_mailbox_invalid');
|
||||
}
|
||||
}
|
@ -71,18 +71,20 @@ class MultiDB
|
||||
'socket',
|
||||
];
|
||||
|
||||
private static $protected_expense_mailboxes = [];
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getDbs() : array
|
||||
public static function getDbs(): array
|
||||
{
|
||||
return self::$dbs;
|
||||
}
|
||||
|
||||
public static function checkDomainAvailable($subdomain) : bool
|
||||
public static function checkDomainAvailable($subdomain): bool
|
||||
{
|
||||
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
if (!config('ninja.db.multi_db_enabled')) {
|
||||
return Company::whereSubdomain($subdomain)->count() == 0;
|
||||
}
|
||||
|
||||
@ -105,9 +107,35 @@ class MultiDB
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function checkUserEmailExists($email) : bool
|
||||
public static function checkExpenseMailboxAvailable($expense_mailbox): bool
|
||||
{
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
|
||||
if (!config('ninja.db.multi_db_enabled')) {
|
||||
return Company::where("expense_mailbox", $expense_mailbox)->withTrashed()->exists();
|
||||
}
|
||||
|
||||
if (in_array($expense_mailbox, self::$protected_expense_mailboxes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$current_db = config('database.default');
|
||||
|
||||
foreach (self::$dbs as $db) {
|
||||
if (Company::on($db)->where("expense_mailbox", $expense_mailbox)->withTrashed()->exists()) {
|
||||
self::setDb($current_db);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
self::setDb($current_db);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function checkUserEmailExists($email): bool
|
||||
{
|
||||
if (!config('ninja.db.multi_db_enabled')) {
|
||||
return User::where(['email' => $email])->withTrashed()->exists();
|
||||
} // true >= 1 emails found / false -> == emails found
|
||||
|
||||
@ -139,7 +167,7 @@ class MultiDB
|
||||
* @param string $company_key The company key
|
||||
* @return bool True|False
|
||||
*/
|
||||
public static function checkUserAndCompanyCoExist($email, $company_key) :bool
|
||||
public static function checkUserAndCompanyCoExist($email, $company_key): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -166,9 +194,9 @@ class MultiDB
|
||||
* @param array $data
|
||||
* @return User|null
|
||||
*/
|
||||
public static function hasUser(array $data) : ?User
|
||||
public static function hasUser(array $data): ?User
|
||||
{
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
if (!config('ninja.db.multi_db_enabled')) {
|
||||
return User::where($data)->withTrashed()->first();
|
||||
}
|
||||
|
||||
@ -190,9 +218,9 @@ class MultiDB
|
||||
* @param string $email
|
||||
* @return ClientContact|null
|
||||
*/
|
||||
public static function hasContact(string $email) : ?ClientContact
|
||||
public static function hasContact(string $email): ?ClientContact
|
||||
{
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
if (!config('ninja.db.multi_db_enabled')) {
|
||||
return ClientContact::where('email', $email)->withTrashed()->first();
|
||||
}
|
||||
|
||||
@ -217,9 +245,9 @@ class MultiDB
|
||||
* @param array $search
|
||||
* @return ClientContact|null
|
||||
*/
|
||||
public static function findContact(array $search) : ?ClientContact
|
||||
public static function findContact(array $search): ?ClientContact
|
||||
{
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
if (!config('ninja.db.multi_db_enabled')) {
|
||||
return ClientContact::where($search)->first();
|
||||
}
|
||||
|
||||
@ -240,7 +268,7 @@ class MultiDB
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function contactFindAndSetDb($token) :bool
|
||||
public static function contactFindAndSetDb($token): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -257,7 +285,7 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function userFindAndSetDb($email) : bool
|
||||
public static function userFindAndSetDb($email): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -275,7 +303,7 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function documentFindAndSetDb($hash) : bool
|
||||
public static function documentFindAndSetDb($hash): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -293,7 +321,7 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDb($token) :bool
|
||||
public static function findAndSetDb($token): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -310,7 +338,7 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByCompanyKey($company_key) :bool
|
||||
public static function findAndSetDbByCompanyKey($company_key): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -327,7 +355,7 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByCompanyId($company_id) :?Company
|
||||
public static function findAndSetDbByCompanyId($company_id): ?Company
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -344,7 +372,7 @@ class MultiDB
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByShopifyName($shopify_name) :?Company
|
||||
public static function findAndSetDbByShopifyName($shopify_name): ?Company
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -361,7 +389,7 @@ class MultiDB
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByAccountKey($account_key) :bool
|
||||
public static function findAndSetDbByAccountKey($account_key): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -378,7 +406,7 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByInappTransactionId($transaction_id) :bool
|
||||
public static function findAndSetDbByInappTransactionId($transaction_id): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -396,7 +424,7 @@ class MultiDB
|
||||
}
|
||||
|
||||
|
||||
public static function findAndSetDbByContactKey($contact_key) :bool
|
||||
public static function findAndSetDbByContactKey($contact_key): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -413,7 +441,7 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByVendorContactKey($contact_key) :bool
|
||||
public static function findAndSetDbByVendorContactKey($contact_key): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -430,7 +458,7 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByClientHash($client_hash) :bool
|
||||
public static function findAndSetDbByClientHash($client_hash): bool
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -447,7 +475,7 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByClientId($client_id) :?Client
|
||||
public static function findAndSetDbByClientId($client_id): ?Client
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
@ -466,7 +494,7 @@ class MultiDB
|
||||
|
||||
public static function findAndSetDbByDomain($query_array)
|
||||
{
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
if (!config('ninja.db.multi_db_enabled')) {
|
||||
return Company::where($query_array)->first();
|
||||
}
|
||||
|
||||
@ -487,7 +515,7 @@ class MultiDB
|
||||
|
||||
public static function findAndSetDbByInvitation($entity, $invitation_key)
|
||||
{
|
||||
$class = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
|
||||
$class = 'App\Models\\' . ucfirst(Str::camel($entity)) . 'Invitation';
|
||||
$current_db = config('database.default');
|
||||
|
||||
foreach (self::$dbs as $db) {
|
||||
@ -507,9 +535,9 @@ class MultiDB
|
||||
* @param string $phone
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasPhoneNumber(string $phone) : bool
|
||||
public static function hasPhoneNumber(string $phone): bool
|
||||
{
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
if (!config('ninja.db.multi_db_enabled')) {
|
||||
return Account::where('account_sms_verification_number', $phone)->where('account_sms_verified', true)->exists();
|
||||
}
|
||||
|
||||
@ -548,7 +576,7 @@ class MultiDB
|
||||
$string .= $consonants[rand(0, 19)];
|
||||
$string .= $vowels[rand(0, 4)];
|
||||
}
|
||||
} while (! self::checkDomainAvailable($string));
|
||||
} while (!self::checkDomainAvailable($string));
|
||||
|
||||
self::setDb($current_db);
|
||||
|
||||
@ -559,7 +587,7 @@ class MultiDB
|
||||
* @param $database
|
||||
* @return void
|
||||
*/
|
||||
public static function setDB(string $database) : void
|
||||
public static function setDB(string $database): void
|
||||
{
|
||||
/* This will set the database connection for the request */
|
||||
config(['database.default' => $database]);
|
||||
|
@ -90,7 +90,7 @@ class AccountTransformer extends EntityTransformer
|
||||
'set_react_as_default_ap' => (bool) $account->set_react_as_default_ap,
|
||||
'trial_days_left' => Ninja::isHosted() ? (int) $account->getTrialDays() : 0,
|
||||
'account_sms_verified' => (bool) $account->account_sms_verified,
|
||||
'has_iap_plan' => (bool)$account->inapp_transaction_id,
|
||||
'has_iap_plan' => (bool) $account->inapp_transaction_id,
|
||||
'tax_api_enabled' => (bool) config('services.tax.zip_tax.key') ? true : false
|
||||
|
||||
];
|
||||
|
@ -146,7 +146,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'enabled_modules' => (int) $company->enabled_modules,
|
||||
'updated_at' => (int) $company->updated_at,
|
||||
'archived_at' => (int) $company->deleted_at,
|
||||
'created_at' =>(int) $company->created_at,
|
||||
'created_at' => (int) $company->created_at,
|
||||
'slack_webhook_url' => (string) $company->slack_webhook_url,
|
||||
'google_analytics_url' => (string) $company->google_analytics_key, //@deprecate 1-2-2021
|
||||
'google_analytics_key' => (string) $company->google_analytics_key,
|
||||
@ -158,7 +158,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'is_large' => (bool) $this->isLarge($company),
|
||||
'is_disabled' => (bool) $company->is_disabled,
|
||||
'enable_shop_api' => (bool) $company->enable_shop_api,
|
||||
'mark_expenses_invoiceable'=> (bool) $company->mark_expenses_invoiceable,
|
||||
'mark_expenses_invoiceable' => (bool) $company->mark_expenses_invoiceable,
|
||||
'mark_expenses_paid' => (bool) $company->mark_expenses_paid,
|
||||
'invoice_expense_documents' => (bool) $company->invoice_expense_documents,
|
||||
'invoice_task_timelog' => (bool) $company->invoice_task_timelog,
|
||||
@ -168,10 +168,10 @@ class CompanyTransformer extends EntityTransformer
|
||||
'use_credits_payment' => 'always', // @deprecate 1-2-2021
|
||||
'default_task_is_date_based' => (bool) $company->default_task_is_date_based,
|
||||
'enable_product_discount' => (bool) $company->enable_product_discount,
|
||||
'calculate_expense_tax_by_amount' =>(bool) $company->calculate_expense_tax_by_amount,
|
||||
'calculate_expense_tax_by_amount' => (bool) $company->calculate_expense_tax_by_amount,
|
||||
'hide_empty_columns_on_pdf' => false, // @deprecate 1-2-2021
|
||||
'expense_inclusive_taxes' => (bool) $company->expense_inclusive_taxes,
|
||||
'expense_amount_is_pretax' =>(bool) true, //@deprecate 1-2-2021
|
||||
'expense_amount_is_pretax' => (bool) true, //@deprecate 1-2-2021
|
||||
'oauth_password_required' => (bool) $company->oauth_password_required,
|
||||
'session_timeout' => (int) $company->session_timeout,
|
||||
'default_password_timeout' => (int) $company->default_password_timeout,
|
||||
@ -204,6 +204,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'invoice_task_project_header' => (bool) $company->invoice_task_project_header,
|
||||
'invoice_task_item_description' => (bool) $company->invoice_task_item_description,
|
||||
'origin_tax_data' => $company->origin_tax_data ?: new \stdClass,
|
||||
'expense_mailbox' => $company->expense_mailbox,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -238,7 +238,9 @@ return [
|
||||
],
|
||||
'webhook' => [
|
||||
'mailbox_template' => env('INBOUND_EXPENSE_WEBHOOK_MAILBOXTEMPLATE', null),
|
||||
'mailbox_template_enterprise' => env('INBOUND_EXPENSE_WEBHOOK_MAILBOXTEMPLATE_ENTERPRISE', '{{input}}@expense.invoicing.co'),
|
||||
'mailbox_schema' => env('INBOUND_EXPENSE_WEBHOOK_MAILBOX_SCHEMA', null),
|
||||
'mailbox_schema_hascompanykey' => env('INBOUND_EXPENSE_WEBHOOK_MAILBOX_SCHEMA_HASCOMPANYKEY', false),
|
||||
'mailbox_schema_enterprise' => env('INBOUND_EXPENSE_WEBHOOK_MAILBOX_SCHEMA_ENTERPRISE', '.*@expense\.invoicing\.co$'),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
@ -2534,6 +2534,8 @@ $lang = array(
|
||||
'local_storage_required' => 'Error: local storage is not available.',
|
||||
'your_password_reset_link' => 'Your Password Reset Link',
|
||||
'subdomain_taken' => 'The subdomain is already in use',
|
||||
'expense_mailbox_taken' => 'The mailbox is already in use',
|
||||
'expense_mailbox_invalid' => 'The mailbox does not match the required schema',
|
||||
'client_login' => 'Client Login',
|
||||
'converted_amount' => 'Converted Amount',
|
||||
'default' => 'Default',
|
||||
|
Loading…
x
Reference in New Issue
Block a user