From 34067e490fb3ca7a30555f55678c58c2016bf85b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 10:19:48 +1100 Subject: [PATCH 01/13] Static Analysis --- app/Http/Controllers/Bank/NordigenController.php | 4 +++- app/Http/Controllers/BankIntegrationController.php | 2 +- app/Jobs/Bank/ProcessBankTransactionsNordigen.php | 2 +- app/Transformers/BankIntegrationTransformer.php | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Bank/NordigenController.php b/app/Http/Controllers/Bank/NordigenController.php index 803947c9388c..4644e32deb4d 100644 --- a/app/Http/Controllers/Bank/NordigenController.php +++ b/app/Http/Controllers/Bank/NordigenController.php @@ -128,11 +128,13 @@ class NordigenController extends BaseController /** * VIEW: Confirm Nordigen Bank Integration (redirect after nordigen flow) - * @param ConnectNordigenBankIntegrationRequest $request + * @param ConfirmNordigenBankIntegrationRequest $request */ public function confirm(ConfirmNordigenBankIntegrationRequest $request) { $data = $request->all(); + + /** @var array $context */ $context = $request->getTokenContent(); if (!array_key_exists('lang', $data) && $context['lang'] != 'en') return redirect()->route('nordigen.confirm', array_merge(["lang" => $context['lang']], $request->query())); // redirect is required in order for the bank-ui to display everything properly diff --git a/app/Http/Controllers/BankIntegrationController.php b/app/Http/Controllers/BankIntegrationController.php index bf61c52bc7a4..891e7c0ca962 100644 --- a/app/Http/Controllers/BankIntegrationController.php +++ b/app/Http/Controllers/BankIntegrationController.php @@ -212,7 +212,7 @@ class BankIntegrationController extends BaseController ProcessBankTransactionsYodlee::dispatch($user_account->id, $bank_integration); }); - if (config('ninja.nordigen.secret_id') && config('ninja.nordigen.secret_key') && (Ninja::isSelfHost() || (Ninja::isHosted() && $user_account->isPaid() && $user_account->plan == 'enterprise'))) + if (config('ninja.nordigen.secret_id') && config('ninja.nordigen.secret_key') && (Ninja::isSelfHost() || (Ninja::isHosted() && $user_account->isEnterprisePaidClient()))) $user_account->bank_integrations->where("integration_type", BankIntegration::INTEGRATION_TYPE_NORDIGEN)->each(function ($bank_integration) { ProcessBankTransactionsNordigen::dispatch($bank_integration); }); diff --git a/app/Jobs/Bank/ProcessBankTransactionsNordigen.php b/app/Jobs/Bank/ProcessBankTransactionsNordigen.php index da9ac7f98571..fc77d49e5774 100644 --- a/app/Jobs/Bank/ProcessBankTransactionsNordigen.php +++ b/app/Jobs/Bank/ProcessBankTransactionsNordigen.php @@ -35,7 +35,7 @@ class ProcessBankTransactionsNordigen implements ShouldQueue public Company $company; public Nordigen $nordigen; public $nordigen_account; - + private bool $stop_loop = false; /** * Create a new job instance. */ diff --git a/app/Transformers/BankIntegrationTransformer.php b/app/Transformers/BankIntegrationTransformer.php index 8496b23e11ac..4b2288baf0e3 100644 --- a/app/Transformers/BankIntegrationTransformer.php +++ b/app/Transformers/BankIntegrationTransformer.php @@ -68,6 +68,7 @@ class BankIntegrationTransformer extends EntityTransformer 'created_at' => (int) $bank_integration->created_at, 'updated_at' => (int) $bank_integration->updated_at, 'archived_at' => (int) $bank_integration->deleted_at, + 'integration_type' => (string) $bank_integration->integration_type ?: '', ]; } From 6447b887f068e1b5db568e94e5f14b5c819a8dd3 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 10:24:42 +1100 Subject: [PATCH 02/13] Static Analysis --- app/Helpers/Bank/Nordigen/Nordigen.php | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/Helpers/Bank/Nordigen/Nordigen.php b/app/Helpers/Bank/Nordigen/Nordigen.php index 24bc0dc58aa9..9ed080407c75 100644 --- a/app/Helpers/Bank/Nordigen/Nordigen.php +++ b/app/Helpers/Bank/Nordigen/Nordigen.php @@ -85,6 +85,7 @@ class Nordigen $it = new AccountTransformer(); return $it->transform($out); + } catch (\Exception $e) { if (strpos($e->getMessage(), "Invalid Account ID") !== false) return false; @@ -92,8 +93,14 @@ class Nordigen throw $e; } } - - public function isAccountActive(string $account_id) + + /** + * isAccountActive + * + * @param string $account_id + * @return bool + */ + public function isAccountActive(string $account_id): bool { try { $account = $this->client->account($account_id)->getAccountMetaData(); @@ -112,11 +119,15 @@ class Nordigen } } + /** - * this method returns booked transactions from the bank_account, pending transactions are not part of the result - * @todo @turbo124 should we include pending transactions within the integration-process and mark them with a specific category?! + * getTransactions + * + * @param string $accountId + * @param string $dateFrom + * @return array */ - public function getTransactions(string $accountId, string $dateFrom = null) + public function getTransactions(string $accountId, string $dateFrom = null): array { $transactionResponse = $this->client->account($accountId)->getAccountTransactions($dateFrom); From 429bcf51b901b8e1c6900228da3a6ebaf99f6343 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 13:27:16 +1100 Subject: [PATCH 03/13] Update blacklist --- app/Http/ValidationRules/Account/BlackListRule.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Http/ValidationRules/Account/BlackListRule.php b/app/Http/ValidationRules/Account/BlackListRule.php index 12d4bde4ecc3..3845f7f9ce05 100644 --- a/app/Http/ValidationRules/Account/BlackListRule.php +++ b/app/Http/ValidationRules/Account/BlackListRule.php @@ -19,6 +19,9 @@ use Illuminate\Contracts\Validation\Rule; class BlackListRule implements Rule { private array $blacklist = [ + 'secure-coinspot.com', + 'casasotombo.com', + 'otpku.com', 'ckptr.com', 'pretreer.com', 'candassociates.com', From 3e4cf6f2616e05300af42be450e3a7a3a7d874b1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 13:51:37 +1100 Subject: [PATCH 04/13] Convest rule to validationrule --- .../ValidationRules/Account/BlackListRule.php | 25 ++++------------- .../BlacklistValidationTest.php | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/app/Http/ValidationRules/Account/BlackListRule.php b/app/Http/ValidationRules/Account/BlackListRule.php index 3845f7f9ce05..e2e13aa8a931 100644 --- a/app/Http/ValidationRules/Account/BlackListRule.php +++ b/app/Http/ValidationRules/Account/BlackListRule.php @@ -11,12 +11,13 @@ namespace App\Http\ValidationRules\Account; -use Illuminate\Contracts\Validation\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class BlackListRule. */ -class BlackListRule implements Rule +class BlackListRule implements ValidationRule { private array $blacklist = [ 'secure-coinspot.com', @@ -3497,27 +3498,13 @@ class BlackListRule implements Rule 'zzz.com', ]; - /** - * @param string $attribute - * @param mixed $value - * @return bool - */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { $parts = explode("@", $value); - if (is_array($parts)) { - return ! in_array($parts[1], $this->blacklist); - } else { - return true; + if (is_array($parts) && in_array($parts[1], $this->blacklist)) { + $fail('This domain is blacklisted, if you think this is in error, please email contact@invoiceninja.com'); } } - /** - * @return string - */ - public function message(): string - { - return 'This domain is blacklisted, if you think this is in error, please email contact@invoiceninja.com'; - } } diff --git a/tests/Unit/ValidationRules/BlacklistValidationTest.php b/tests/Unit/ValidationRules/BlacklistValidationTest.php index 1a6b54a41435..806f16232fc3 100644 --- a/tests/Unit/ValidationRules/BlacklistValidationTest.php +++ b/tests/Unit/ValidationRules/BlacklistValidationTest.php @@ -52,4 +52,32 @@ class BlacklistValidationTest extends TestCase $v = $this->app['validator']->make($data, $rules); $this->assertFalse($v->passes()); } + + public function testInValidEmailRule2() + { + $rules = [ + 'email' => [new BlackListRule], + ]; + + $data = [ + 'email' => 'jimmy@zzz.com', + ]; + + $v = $this->app['validator']->make($data, $rules); + $this->assertFalse($v->passes()); + } + + public function testInValidEmailRule3() + { + $rules = [ + 'email' => [new BlackListRule], + ]; + + $data = [ + 'email' => 'jimmy@gmail.com', + ]; + + $v = $this->app['validator']->make($data, $rules); + $this->assertTrue($v->passes()); + } } From a88b4d5d8d31685fcf4efc4520032015dc196e12 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 13:54:27 +1100 Subject: [PATCH 05/13] Convest rule to validationrule --- .../Account/EmailBlackListRule.php | 28 +++++++--------- .../BlacklistValidationTest.php | 32 ++++++++++++++++++- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/app/Http/ValidationRules/Account/EmailBlackListRule.php b/app/Http/ValidationRules/Account/EmailBlackListRule.php index 43e3451d1b6c..9054eef63150 100644 --- a/app/Http/ValidationRules/Account/EmailBlackListRule.php +++ b/app/Http/ValidationRules/Account/EmailBlackListRule.php @@ -11,32 +11,26 @@ namespace App\Http\ValidationRules\Account; -use Illuminate\Contracts\Validation\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; /** * Class EmailBlackListRule. */ -class EmailBlackListRule implements Rule +class EmailBlackListRule implements ValidationRule { public array $blacklist = [ - + 'noddy@invoiceninja.com', ]; - /** - * @param string $attribute - * @param mixed $value - * @return bool - */ - public function passes($attribute, $value) + + public function validate(string $attribute, mixed $value, Closure $fail): void { - return ! in_array($value, $this->blacklist); + + if (in_array($value, $this->blacklist)) { + $fail('This email address is blacklisted, if you think this is in error, please email contact@invoiceninja.com'); + } + } - /** - * @return string - */ - public function message() - { - return 'This email address is blacklisted, if you think this is in error, please email contact@invoiceninja.com'; - } } diff --git a/tests/Unit/ValidationRules/BlacklistValidationTest.php b/tests/Unit/ValidationRules/BlacklistValidationTest.php index 806f16232fc3..cc2f76cffea6 100644 --- a/tests/Unit/ValidationRules/BlacklistValidationTest.php +++ b/tests/Unit/ValidationRules/BlacklistValidationTest.php @@ -11,8 +11,9 @@ namespace Tests\Unit\ValidationRules; -use App\Http\ValidationRules\Account\BlackListRule; use Tests\TestCase; +use App\Http\ValidationRules\Account\BlackListRule; +use App\Http\ValidationRules\Account\EmailBlackListRule; /** * @test @@ -25,6 +26,35 @@ class BlacklistValidationTest extends TestCase parent::setUp(); } + public function testValidEmailRule3() + { + $rules = [ + 'email' => [new EmailBlackListRule], + ]; + + $data = [ + 'email' => 'contact@invoiceninja.com', + ]; + + $v = $this->app['validator']->make($data, $rules); + $this->assertTrue($v->passes()); + } + + + public function testValidEmailRule2() + { + $rules = [ + 'email' => [new EmailBlackListRule], + ]; + + $data = [ + 'email' => 'noddy@invoiceninja.com', + ]; + + $v = $this->app['validator']->make($data, $rules); + $this->assertFalse($v->passes()); + } + public function testValidEmailRule() { $rules = [ From e2e9a3567284b3bc7e7319f45d19a56020ab46f3 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 14:08:01 +1100 Subject: [PATCH 06/13] Update domains --- .../ValidationRules/Account/BlackListRule.php | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/app/Http/ValidationRules/Account/BlackListRule.php b/app/Http/ValidationRules/Account/BlackListRule.php index e2e13aa8a931..0d74f646b31a 100644 --- a/app/Http/ValidationRules/Account/BlackListRule.php +++ b/app/Http/ValidationRules/Account/BlackListRule.php @@ -19,6 +19,7 @@ use Illuminate\Contracts\Validation\ValidationRule; */ class BlackListRule implements ValidationRule { + /** Bad domains +/- dispoable email domains */ private array $blacklist = [ 'secure-coinspot.com', 'casasotombo.com', @@ -61,6 +62,8 @@ class BlackListRule implements ValidationRule '10dk.email', '10mail.com', '10mail.org', + '10mail.tk', + '10minmail.de', '10minut.com.pl', '10minut.xyz', '10minutemail.be', @@ -79,6 +82,7 @@ class BlackListRule implements ValidationRule '10minutemailbox.com', '10minutemails.in', '10minutenemail.de', + '10minutenmail.xyz', '10minutesmail.com', '10minutesmail.fr', '10minutmail.pl', @@ -272,6 +276,7 @@ class BlackListRule implements ValidationRule 'affinitywe.us', 'affluentwe.us', 'affordablewe.us', + 'afia.pro', 'afrobacon.com', 'afterhourswe.us', 'agedmail.com', @@ -291,6 +296,8 @@ class BlackListRule implements ValidationRule 'akapost.com', 'akerd.com', 'akgq701.com', + 'akmail.in', + 'akugu.com', 'al-qaeda.us', 'albionwe.us', 'alchemywe.us', @@ -298,6 +305,7 @@ class BlackListRule implements ValidationRule 'aliaswe.us', 'alienware13.com', 'aligamel.com', + 'alina-schiesser.ch', 'alisongamel.com', 'alivance.com', 'alivewe.us', @@ -380,6 +388,7 @@ class BlackListRule implements ValidationRule 'antispam.de', 'antispam24.de', 'antispammail.de', + 'any.pink', 'anyalias.com', 'aoeuhtns.com', 'apfelkorps.de', @@ -444,10 +453,12 @@ class BlackListRule implements ValidationRule 'badoop.com', 'badpotato.tk', 'balaket.com', + 'bangban.uk', 'banit.club', 'banit.me', 'bank-opros1.ru', 'bareed.ws', + 'barooko.com', 'barryogorman.com', 'bartdevos.be', 'basscode.org', @@ -455,11 +466,15 @@ class BlackListRule implements ValidationRule 'bazaaboom.com', 'bbbbyyzz.info', 'bbhost.us', + 'bbitf.com', + 'bbitj.com', + 'bbitq.com', 'bcaoo.com', 'bcast.ws', 'bcb.ro', 'bccto.me', 'bdmuzic.pw', + 'beaconmessenger.com', 'bearsarefuzzy.com', 'beddly.com', 'beefmilk.com', @@ -481,6 +496,7 @@ class BlackListRule implements ValidationRule 'betr.co', 'bgtmail.com', 'bgx.ro', + 'bheps.com', 'bidourlnks.com', 'big1.us', 'bigprofessor.so', @@ -524,9 +540,11 @@ class BlackListRule implements ValidationRule 'bouncr.com', 'boxformail.in', 'boximail.com', + 'boxmail.lol', 'boxomail.live', 'boxtemp.com.br', 'bptfp.net', + 'brand-app.biz', 'brandallday.net', 'brasx.org', 'breakthru.com', @@ -547,8 +565,10 @@ class BlackListRule implements ValidationRule 'budaya-tionghoa.com', 'budayationghoa.com', 'buffemail.com', + 'bugfoo.com', 'bugmenever.com', 'bugmenot.com', + 'bukhariansiddur.com', 'bulrushpress.com', 'bum.net', 'bumpymail.com', @@ -586,6 +606,7 @@ class BlackListRule implements ValidationRule 'caseedu.tk', 'cashflow35.com', 'casualdx.com', + 'catgroup.uk', 'cavi.mx', 'cbair.com', 'cbes.net', @@ -609,6 +630,7 @@ class BlackListRule implements ValidationRule 'cheaphub.net', 'cheatmail.de', 'chenbot.email', + 'chewydonut.com', 'chibakenma.ml', 'chickenkiller.com', 'chielo.com', @@ -625,6 +647,7 @@ class BlackListRule implements ValidationRule 'chong-mail.org', 'chumpstakingdumps.com', 'cigar-auctions.com', + 'civikli.com', 'civx.org', 'ckaazaza.tk', 'ckiso.com', @@ -641,6 +664,7 @@ class BlackListRule implements ValidationRule 'clonemoi.tk', 'cloud-mail.top', 'cloudns.cx', + 'clout.wiki', 'clrmail.com', 'cmail.club', 'cmail.com', @@ -682,6 +706,7 @@ class BlackListRule implements ValidationRule 'crazymailing.com', 'cream.pink', 'crepeau12.com', + 'cringemonster.com', 'cross-law.ga', 'cross-law.gq', 'crossmailjet.com', @@ -737,6 +762,7 @@ class BlackListRule implements ValidationRule 'daymailonline.com', 'dayrep.com', 'dbunker.com', + 'dcctb.com', 'dcemail.com', 'ddcrew.com', 'de-a.org', @@ -773,6 +799,7 @@ class BlackListRule implements ValidationRule 'dev-null.ga', 'dev-null.gq', 'dev-null.ml', + 'developermail.com', 'devnullmail.com', 'deyom.com', 'dharmatel.net', @@ -804,6 +831,7 @@ class BlackListRule implements ValidationRule 'discardmail.com', 'discardmail.de', 'discos4.com', + 'dishcatfish.com', 'disign-concept.eu', 'disign-revelation.com', 'dispo.in', @@ -855,10 +883,12 @@ class BlackListRule implements ValidationRule 'domforfb8.tk', 'domforfb9.tk', 'domozmail.com', + 'donebyngle.com', 'donemail.ru', 'dongqing365.com', 'dontreg.com', 'dontsendmespam.de', + 'doojazz.com', 'doquier.tk', 'dotman.de', 'dotmsg.com', @@ -873,13 +903,17 @@ class BlackListRule implements ValidationRule 'dred.ru', 'drevo.si', 'drivetagdev.com', + 'drmail.in', 'droolingfanboy.de', 'dropcake.de', 'dropjar.com', 'droplar.com', 'dropmail.me', 'dropsin.net', + 'drowblock.com', + 'dsgvo.party', 'dsgvo.ru', + 'dshfjdafd.cloud', 'dsiay.com', 'dspwebservices.com', 'duam.net', @@ -948,6 +982,7 @@ class BlackListRule implements ValidationRule 'emailage.ml', 'emailage.tk', 'emailate.com', + 'emailbin.net', 'emailcu.icu', 'emaildienst.de', 'emaildrop.io', @@ -1021,8 +1056,11 @@ class BlackListRule implements ValidationRule 'eposta.buzz', 'eposta.work', 'eqiluxspam.ga', + 'ereplyzy.com', 'ericjohnson.ml', + 'eripo.net', 'ero-tube.org', + 'esadverse.com', 'esbano-ru.ru', 'esc.la', 'escapehatchapp.com', @@ -1047,16 +1085,19 @@ class BlackListRule implements ValidationRule 'evopo.com', 'evyush.com', 'exdonuts.com', + 'exelica.com', 'existiert.net', 'exitstageleft.net', 'explodemail.com', 'express.net.ua', + 'extracurricularsociety.com', 'extremail.ru', 'eyepaste.com', 'ez.lv', 'ezehe.com', 'ezfill.com', 'ezstest.com', + 'ezztt.com', 'f4k.es', 'f5.si', 'facebook-email.cf', @@ -1112,12 +1153,14 @@ class BlackListRule implements ValidationRule 'fbma.tk', 'fddns.ml', 'fdfdsfds.com', + 'femailtor.com', 'fer-gabon.org', 'fermaxxi.ru', 'fettometern.com', 'fexbox.org', 'fexbox.ru', 'fexpost.com', + 'fextemp.com', 'ficken.de', 'fictionsite.com', 'fightallspam.com', @@ -1131,6 +1174,7 @@ class BlackListRule implements ValidationRule 'filzmail.com', 'findemail.info', 'findu.pl', + 'finews.biz', 'fir.hk', 'firemailbox.club', 'fitnesrezink.ru', @@ -1139,12 +1183,14 @@ class BlackListRule implements ValidationRule 'fizmail.com', 'fleckens.hu', 'flemail.ru', + 'fliegender.fish', 'flowu.com', 'flu.cc', 'fluidsoft.us', 'flurred.com', 'fly-ts.de', 'flyinggeek.net', + 'flymail.tk', 'flyspam.com', 'foobarbot.net', 'footard.com', @@ -1162,6 +1208,7 @@ class BlackListRule implements ValidationRule 'fosil.pro', 'foxja.com', 'foxtrotter.info', + 'fr.cr', 'fr.nf', 'fr33mail.info', 'fragolina2.tk', @@ -1208,11 +1255,15 @@ class BlackListRule implements ValidationRule 'fuirio.com', 'fukaru.com', 'fukurou.ch', + 'fullangle.org', 'fulvie.com', 'fun64.com', 'funnycodesnippets.com', 'funnymail.de', 'furzauflunge.de', + 'futuramind.com', + 'fuwa.be', + 'fuwa.li', 'fuwamofu.com', 'fuwari.be', 'fux0ringduh.com', @@ -1295,6 +1346,7 @@ class BlackListRule implements ValidationRule 'giveh2o.info', 'givememail.club', 'givmail.com', + 'gixenmixen.com', 'glitch.sx', 'globaltouron.com', 'glubex.com', @@ -1308,6 +1360,7 @@ class BlackListRule implements ValidationRule 'gnctr-calgary.com', 'go2usa.info', 'go2vpn.net', + 'goatmail.uk', 'goemailgo.com', 'golemico.com', 'gomail.in', @@ -1367,6 +1420,7 @@ class BlackListRule implements ValidationRule 'guerrillamail.net', 'guerrillamail.org', 'guerrillamailblock.com', + 'gufum.com', 'gustr.com', 'gxemail.men', 'gynzi.co.uk', @@ -1393,6 +1447,7 @@ class BlackListRule implements ValidationRule 'haltospam.com', 'hamham.uk', 'hangxomcuatoilatotoro.ml', + 'happy2023year.com', 'happydomik.ru', 'harakirimail.com', 'haribu.com', @@ -1471,6 +1526,7 @@ class BlackListRule implements ValidationRule 'huskion.net', 'hvastudiesucces.nl', 'hwsye.net', + 'hypenated-domain.com', 'i2pmail.org', 'i6.cloudns.cc', 'iaoss.com', @@ -1480,6 +1536,7 @@ class BlackListRule implements ValidationRule 'ichigo.me', 'icx.in', 'icx.ro', + 'icznn.com', 'idx4.com', 'idxue.com', 'ieatspam.eu', @@ -1502,9 +1559,12 @@ class BlackListRule implements ValidationRule 'imgof.com', 'imgv.de', 'immo-gerance.info', + 'imperialcnk.com', 'imstations.com', 'imul.info', 'in-ulm.de', + 'in2reach.com', + 'inactivemachine.com', 'inbax.tk', 'inbound.plus', 'inbox.si', @@ -1534,6 +1594,7 @@ class BlackListRule implements ValidationRule 'ineec.net', 'infocom.zp.ua', 'inggo.org', + 'inkiny.com', 'inkomail.com', 'inmynetwork.tk', 'inoutmail.de', @@ -1544,12 +1605,16 @@ class BlackListRule implements ValidationRule 'insanumingeniumhomebrew.com', 'insorg-mail.info', 'instaddr.ch', + 'instaddr.uk', + 'instaddr.win', 'instance-email.com', 'instant-mail.de', 'instantblingmail.info', 'instantemailaddress.com', 'instantmail.fr', + 'instmail.uk', 'internet-v-stavropole.ru', + 'internetkeno.com', 'internetoftags.com', 'interstats.org', 'intersteller.com', @@ -1581,6 +1646,7 @@ class BlackListRule implements ValidationRule 'italy-mail.com', 'itcompu.com', 'itfast.net', + 'itsjiff.com', 'itunesgiftcodegenerator.com', 'iubridge.com', 'iuemail.men', @@ -1589,6 +1655,7 @@ class BlackListRule implements ValidationRule 'ixx.io', 'j-p.us', 'jafps.com', + 'jaga.email', 'jajxz.com', 'janproz.com', 'jaqis.com', @@ -1603,11 +1670,15 @@ class BlackListRule implements ValidationRule 'jetable.net', 'jetable.org', 'jetable.pp.ua', + 'ji5.de', + 'ji6.de', + 'ji7.de', 'jiooq.com', 'jmail.ovh', 'jmail.ro', 'jnxjn.com', 'jobbikszimpatizans.hu', + 'jobbrett.com', 'jobposts.net', 'jobs-to-be-done.net', 'joelpet.com', @@ -1664,6 +1735,8 @@ class BlackListRule implements ValidationRule 'killmail.com', 'killmail.net', 'kimsdisk.com', + 'kinda.email', + 'kindamail.com', 'kingsq.ga', 'kino-100.ru', 'kiois.com', @@ -1672,16 +1745,20 @@ class BlackListRule implements ValidationRule 'kitnastar.com', 'kjkszpjcompany.com', 'kkmail.be', + 'kkoup.com', 'kksm.be', 'klassmaster.com', 'klassmaster.net', 'klick-tipp.us', 'klipschx12.com', 'kloap.com', + 'klovenode.com', 'kludgemush.com', 'klzlk.com', 'kmail.li', + 'kmail.live', 'kmhow.com', + 'knickerbockerban.de', 'knol-power.nl', 'kobrandly.com', 'kommunity.biz', @@ -1715,6 +1792,7 @@ class BlackListRule implements ValidationRule 'kwilco.net', 'kyal.pl', 'kyois.com', + 'kzccv.com', 'l-c-a.us', 'l33r.eu', 'l6factors.com', @@ -1729,9 +1807,12 @@ class BlackListRule implements ValidationRule 'lak.pp.ua', 'lakelivingstonrealestate.com', 'lakqs.com', + 'lamasticots.com', + 'lambsauce.de', 'landmail.co', 'laoeq.com', 'larisia.com', + 'larland.com', 'last-chance.pro', 'lastmail.co', 'lastmail.com', @@ -1761,6 +1842,7 @@ class BlackListRule implements ValidationRule 'ligsb.com', 'lillemap.net', 'lilo.me', + 'lilspam.com', 'lindenbaumjapan.com', 'link2mail.net', 'linkedintuts2016.pw', @@ -1806,6 +1888,8 @@ class BlackListRule implements ValidationRule 'lukop.dk', 'luv2.us', 'lyfestylecreditsolutions.com', + 'lyft.live', + 'lyricspad.net', 'lzoaq.com', 'm21.cc', 'm4ilweb.info', @@ -1851,6 +1935,7 @@ class BlackListRule implements ValidationRule 'mailapp.top', 'mailback.com', 'mailbidon.com', + 'mailbiscuit.com', 'mailbiz.biz', 'mailblocks.com', 'mailbox.in.ua', @@ -1880,6 +1965,7 @@ class BlackListRule implements ValidationRule 'mailed.ro', 'maileimer.de', 'maileme101.com', + 'mailers.edu.pl', 'mailexpire.com', 'mailf5.com', 'mailfa.tk', @@ -1947,6 +2033,7 @@ class BlackListRule implements ValidationRule 'mailonaut.com', 'mailorc.com', 'mailorg.org', + 'mailosaur.net', 'mailox.fun', 'mailpick.biz', 'mailpluss.com', @@ -2010,18 +2097,22 @@ class BlackListRule implements ValidationRule 'mcache.net', 'mciek.com', 'mdhc.tk', + 'mdz.email', 'meantinc.com', 'mebelnu.info', 'mechanicalresumes.com', 'medkabinet-uzi.ru', 'meepsheep.eu', + 'meidecn.com', 'meinspamschutz.de', 'meltedbrownies.com', 'meltmail.com', 'memsg.site', 'mentonit.net', 'mepost.pw', + 'merepost.com', 'merry.pink', + 'meruado.uk', 'messagebeamer.de', 'messwiththebestdielikethe.rest', 'metadownload.org', @@ -2047,6 +2138,7 @@ class BlackListRule implements ValidationRule 'migumail.com', 'mihep.com', 'mijnhva.nl', + 'minimail.gq', 'ministry-of-silly-walks.de', 'minsmail.com', 'mintemail.com', @@ -2058,6 +2150,7 @@ class BlackListRule implements ValidationRule 'mjukglass.nu', 'mkpfilm.com', 'ml8.ca', + 'mliok.com', 'mm.my', 'mm5.se', 'mnode.me', @@ -2113,6 +2206,7 @@ class BlackListRule implements ValidationRule 'mucincanon.com', 'muehlacker.tk', 'muell.icu', + 'muell.io', 'muell.monster', 'muell.xyz', 'muellemail.com', @@ -2132,16 +2226,19 @@ class BlackListRule implements ValidationRule 'mycleaninbox.net', 'mycorneroftheinter.net', 'myde.ml', + 'mydefipet.live', 'mydemo.equipment', 'myecho.es', 'myemailboxy.com', 'mygeoweb.info', 'myindohome.services', + 'myinfoinc.com', 'myinterserver.ml', 'mykickassideas.com', 'mymail-in.net', 'mymail90.com', 'mymailoasis.com', + 'mymaily.lol', 'mynetstore.de', 'myopang.com', 'mypacks.net', @@ -2175,10 +2272,12 @@ class BlackListRule implements ValidationRule 'naslazhdai.ru', 'nationalgardeningclub.com', 'nawmin.info', + 'naymedia.com', 'nbzmr.com', 'negated.com', 'neko2.net', 'nekochan.fr', + 'nekosan.uk', 'neomailbox.com', 'neotlozhniy-zaim.ru', 'nepwk.com', @@ -2267,6 +2366,7 @@ class BlackListRule implements ValidationRule 'nwytg.com', 'nwytg.net', 'ny7.me', + 'nyasan.com', 'nypato.com', 'nyrmusic.com', 'o2stk.org', @@ -2284,6 +2384,7 @@ class BlackListRule implements ValidationRule 'oepia.com', 'oerpub.org', 'offshore-proxies.net', + 'ofisher.net', 'ohaaa.de', 'ohi.tw', 'oida.icu', @@ -2309,6 +2410,7 @@ class BlackListRule implements ValidationRule 'onlatedotcom.info', 'online.ms', 'onlineidea.info', + 'onlyapp.net', 'onqin.com', 'ontyne.biz', 'oohioo.com', @@ -2334,6 +2436,7 @@ class BlackListRule implements ValidationRule 'ourklips.com', 'ourpreviewdomain.com', 'outlawspam.com', + 'outlook.edu.pl', 'outmail.win', 'ovomail.co', 'ovpn.to', @@ -2341,6 +2444,7 @@ class BlackListRule implements ValidationRule 'owlpic.com', 'ownsyou.de', 'oxopoha.com', + 'ozatvn.com', 'ozyl.de', 'p-banlis.ru', 'p33.org', @@ -2351,6 +2455,7 @@ class BlackListRule implements ValidationRule 'pagamenti.tk', 'paharpurmim.ga', 'pakadebu.ga', + 'pamaweb.com', 'pancakemail.com', 'papierkorb.me', 'paplease.com', @@ -2388,6 +2493,8 @@ class BlackListRule implements ValidationRule 'pisls.com', 'pitaniezdorovie.ru', 'pivo-bar.ru', + 'pixiil.com', + 'pizzajunk.com', 'pjjkp.com', 'placebomail10.com', 'pleasenoham.org', @@ -2439,6 +2546,7 @@ class BlackListRule implements ValidationRule 'prin.be', 'privacy.net', 'privatdemail.net', + 'privmail.edu.pl', 'privy-mail.com', 'privy-mail.de', 'privymail.de', @@ -2458,6 +2566,7 @@ class BlackListRule implements ValidationRule 'prtz.eu', 'psh.me', 'psles.com', + 'psnator.com', 'psoxs.com', 'puglieisi.com', 'puji.pro', @@ -2468,11 +2577,13 @@ class BlackListRule implements ValidationRule 'put2.net', 'puttanamaiala.tk', 'putthisinyourspamdatabase.com', + 'pwpwa.com', 'pwrby.com', 'qasti.com', 'qbfree.us', 'qc.to', 'qibl.at', + 'qiott.com', 'qipmail.net', 'qiq.us', 'qisdo.com', @@ -2489,6 +2600,7 @@ class BlackListRule implements ValidationRule 'quickinbox.com', 'quickmail.nl', 'quicksend.ch', + 'quipas.com', 'ququb.com', 'qvy.me', 'qwickmail.com', @@ -2500,6 +2612,7 @@ class BlackListRule implements ValidationRule 'raetp9.com', 'rainbowly.ml', 'raketenmann.de', + 'ramenmail.de', 'rancidhome.net', 'randomail.io', 'randomail.net', @@ -2516,6 +2629,7 @@ class BlackListRule implements ValidationRule 're-gister.com', 'reality-concept.club', 'reallymymail.com', + 'realquickemail.com', 'realtyalerts.ca', 'rebates.stream', 'receiveee.com', @@ -2546,6 +2660,7 @@ class BlackListRule implements ValidationRule 'rippb.com', 'risingsuntouch.com', 'riski.cf', + 'risu.be', 'rklips.com', 'rkomo.com', 'rm2rf.com', @@ -2582,6 +2697,7 @@ class BlackListRule implements ValidationRule 's33db0x.com', 'sabrestlouis.com', 'sackboii.com', + 'saeoil.com', 'safaat.cf', 'safermail.info', 'safersignup.de', @@ -2634,10 +2750,13 @@ class BlackListRule implements ValidationRule 'sexforswingers.com', 'sexical.com', 'sexyalwasmi.top', + 'sfolkar.com', 'shadap.org', 'shalar.net', 'sharedmailbox.org', + 'sharkfaces.com', 'sharklasers.com', + 'shchiba.uk', 'sheryli.com', 'shhmail.com', 'shhuut.org', @@ -2666,6 +2785,7 @@ class BlackListRule implements ValidationRule 'sify.com', 'sika3.com', 'sikux.com', + 'silenceofthespam.com', 'siliwangi.ga', 'silvercoin.life', 'sim-simka.ru', @@ -2685,6 +2805,7 @@ class BlackListRule implements ValidationRule 'skrx.tk', 'sky-inbox.com', 'sky-ts.de', + 'skygazerhub.com', 'skyrt.de', 'slapsfromlastnight.com', 'slaskpost.se', @@ -2702,6 +2823,7 @@ class BlackListRule implements ValidationRule 'smapfree24.eu', 'smapfree24.info', 'smapfree24.org', + 'smartnator.com', 'smarttalent.pw', 'smashmail.de', 'smellfear.com', @@ -2709,12 +2831,14 @@ class BlackListRule implements ValidationRule 'smellypotato.tk', 'smtp99.com', 'smwg.info', + 'snakebutt.com', 'snakemail.com', 'snapwet.com', 'sneakmail.de', 'snece.com', 'social-mailer.tk', 'socialfurry.org', + 'sociallymediocre.com', 'sofia.re', 'sofimail.com', 'sofort-mail.de', @@ -2735,6 +2859,7 @@ class BlackListRule implements ValidationRule 'soodmail.com', 'soodomail.com', 'soodonims.com', + 'soombo.com', 'soon.it', 'spacebazzar.ru', 'spam-be-gone.com', @@ -2767,6 +2892,7 @@ class BlackListRule implements ValidationRule 'spamday.com', 'spamdecoy.net', 'spamex.com', + 'spamfellas.com', 'spamfighter.cf', 'spamfighter.ga', 'spamfighter.gq', @@ -2795,6 +2921,7 @@ class BlackListRule implements ValidationRule 'spamobox.com', 'spamoff.de', 'spamsalad.in', + 'spamsandwich.com', 'spamslicer.com', 'spamsphere.com', 'spamspot.com', @@ -2814,11 +2941,13 @@ class BlackListRule implements ValidationRule 'spikio.com', 'spindl-e.com', 'spoofmail.de', + 'sportrid.com', 'spr.io', 'spritzzone.de', 'spruzme.com', 'spybox.de', 'spymail.com', + 'spymail.one', 'squizzy.de', 'squizzy.net', 'sroff.com', @@ -2856,10 +2985,12 @@ class BlackListRule implements ValidationRule 'submic.com', 'suburbanthug.com', 'suckmyd.com', + 'sudern.de', 'sueshaw.com', 'suexamplesb.com', 'suioe.com', 'super-auswahl.de', + 'superblohey.com', 'supergreatmail.com', 'supermailer.jp', 'superplatyna.com', @@ -2876,6 +3007,7 @@ class BlackListRule implements ValidationRule 'sweetxxx.de', 'swift-mail.net', 'swift10minutemail.com', + 'syinxun.com', 'sylvannet.com', 'symphonyresume.com', 'syosetu.gq', @@ -2895,6 +3027,8 @@ class BlackListRule implements ValidationRule 'tastrg.com', 'taukah.com', 'tb-on-line.net', + 'tcwlm.com', + 'tcwlx.com', 'tdtda.com', 'tech69.com', 'techblast.ch', @@ -2905,6 +3039,7 @@ class BlackListRule implements ValidationRule 'teewars.org', 'tefl.ro', 'telecomix.pl', + 'teleg.eu', 'teleworm.com', 'teleworm.us', 'tellos.xyz', @@ -2969,6 +3104,7 @@ class BlackListRule implements ValidationRule 'thecloudindex.com', 'thediamants.org', 'thedirhq.info', + 'theeyeoftruth.com', 'thejoker5.com', 'thelightningmail.net', 'thelimestones.com', @@ -2978,6 +3114,7 @@ class BlackListRule implements ValidationRule 'thereddoors.online', 'theroyalweb.club', 'thescrappermovie.com', + 'thespamfather.com', 'theteastory.info', 'thex.ro', 'thichanthit.com', @@ -3014,9 +3151,13 @@ class BlackListRule implements ValidationRule 'tkitc.de', 'tlpn.org', 'tmail.com', + 'tmail.io', 'tmail.ws', + 'tmail3.com', + 'tmail9.com', 'tmailinator.com', 'tmails.net', + 'tmmbt.net', 'tmpbox.net', 'tmpemails.com', 'tmpeml.com', @@ -3024,6 +3165,7 @@ class BlackListRule implements ValidationRule 'tmpjr.me', 'tmpmail.net', 'tmpmail.org', + 'tmpx.sa.com', 'toddsbighug.com', 'tofeat.com', 'toiea.com', @@ -3053,6 +3195,7 @@ class BlackListRule implements ValidationRule 'totoan.info', 'tourcc.com', 'tp-qa-mail.com', + 'tpwlb.com', 'tqoai.com', 'tqosi.com', 'tradermail.info', @@ -3101,6 +3244,7 @@ class BlackListRule implements ValidationRule 'trialmail.de', 'trickmail.net', 'trillianpro.com', + 'triots.com', 'trixtrux1.ru', 'trollproject.com', 'tropicalbass.info', @@ -3116,6 +3260,7 @@ class BlackListRule implements ValidationRule 'turoid.com', 'turual.com', 'turuma.com', + 'tutuapp.bid', 'tvchd.com', 'tverya.com', 'twinmail.de', @@ -3154,6 +3299,7 @@ class BlackListRule implements ValidationRule 'unit7lahaina.com', 'unmail.ru', 'uooos.com', + 'uorak.com', 'upliftnow.com', 'uplipht.com', 'uploadnolimit.com', @@ -3272,6 +3418,7 @@ class BlackListRule implements ValidationRule 'watchever.biz', 'watchfull.net', 'watchironman3onlinefreefullmovie.com', + 'waterisgone.com', 'wazabi.club', 'wbdev.tech', 'wbml.net', @@ -3311,6 +3458,7 @@ class BlackListRule implements ValidationRule 'wegwrfmail.de', 'wegwrfmail.net', 'wegwrfmail.org', + 'weizixu.com', 'wekawa.com', 'welikecookies.com', 'wellsfargocomcardholders.com', @@ -3320,6 +3468,7 @@ class BlackListRule implements ValidationRule 'wfgdfhj.tk', 'wg0.com', 'wh4f.org', + 'whaaaaaaaaaat.com', 'whatiaas.com', 'whatifanalytics.com', 'whatpaas.com', @@ -3331,6 +3480,7 @@ class BlackListRule implements ValidationRule 'wickmail.net', 'widaryanto.info', 'widget.gg', + 'wiemei.com', 'wierie.tk', 'wifimaple.com', 'wifioak.com', @@ -3359,6 +3509,7 @@ class BlackListRule implements ValidationRule 'wudet.men', 'wuespdj.xyz', 'wupics.com', + 'wuuvo.com', 'wuzup.net', 'wuzupmail.net', 'wwjmp.com', @@ -3391,6 +3542,9 @@ class BlackListRule implements ValidationRule 'xrap.de', 'xrho.com', 'xvx.us', + 'xwaretech.com', + 'xwaretech.info', + 'xwaretech.net', 'xww.ro', 'xxhamsterxx.ga', 'xxi2.com', @@ -3467,6 +3621,7 @@ class BlackListRule implements ValidationRule 'zebins.eu', 'zehnminuten.de', 'zehnminutenmail.de', + 'zemzar.net', 'zepp.dk', 'zetmail.com', 'zfymail.com', @@ -3477,6 +3632,7 @@ class BlackListRule implements ValidationRule 'zhorachu.com', 'zik.dj', 'zipcad.com', + 'zipcatfish.com', 'zipo1.gq', 'zippymail.info', 'zipsendtest.com', From c169d2af99697ab4ed1a1d10547c5b630528e325 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 14:08:39 +1100 Subject: [PATCH 07/13] Updated translations --- lang/fr_CA/texts.php | 634 ++++++++++++++++++++++--------------------- 1 file changed, 331 insertions(+), 303 deletions(-) diff --git a/lang/fr_CA/texts.php b/lang/fr_CA/texts.php index 80062a5fae9f..cc51585fbf01 100644 --- a/lang/fr_CA/texts.php +++ b/lang/fr_CA/texts.php @@ -3364,7 +3364,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'credit_number_counter' => 'Compteur du numéro de crédit', 'reset_counter_date' => 'Réinitialiser le compteur de date', 'counter_padding' => 'Espacement du compteur', - 'shared_invoice_quote_counter' => 'Share Invoice/Quote Counter', + 'shared_invoice_quote_counter' => 'Partager le compteur des factures/soumissions', 'default_tax_name_1' => 'Nom de taxe par défaut 1', 'default_tax_rate_1' => 'Taux de taxe par défaut 1', 'default_tax_name_2' => 'Nom de taxe par défaut 2', @@ -3849,308 +3849,308 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'registration_url' => 'URL d\'inscription', 'show_product_cost' => 'Afficher le montant du produit', 'complete' => 'Terminé', - 'next' => 'Suivant', - 'next_step' => 'Étape suivante', - 'notification_credit_sent_subject' => 'le crédit :invoice a été envoyé à :client', - 'notification_credit_viewed_subject' => 'Le crédit :invoice a été consulté par :client', - 'notification_credit_sent' => 'Un crédit de :amount a été envoyé par courriel au client :client.', - 'notification_credit_viewed' => 'Un crédit de :amount a été consulté par le client :client.', - 'reset_password_text' => 'Saisissez votre adresse courriel pour réinitialiser votre mot de passe.', - 'password_reset' => 'Réinitialisation du mot de passe', - 'account_login_text' => 'Bienvenue ! Heureux de vous voir.', - 'request_cancellation' => 'Annuler la demande', - 'delete_payment_method' => 'Supprimer le mode de paiement', - 'about_to_delete_payment_method' => 'Le mode de paiement sera supprimé', - 'action_cant_be_reversed' => 'Cette action ne peut être annulée', - 'profile_updated_successfully' => 'Le profil a été mis à jour', - 'currency_ethiopian_birr' => 'birr éthiopien', - 'client_information_text' => 'Adresse permanente où vous recevez le courriel', - 'status_id' => 'État de facture', - 'email_already_register' => 'Cette adresse courriel est déjà liée à un compte', - 'locations' => 'Emplacements', - 'freq_indefinitely' => 'Indéfiniment', - 'cycles_remaining' => 'Cycles restants', - 'i_understand_delete' => 'Je comprends. Supprimer.', - 'download_files' => 'Télécharger les fichiers', - 'download_timeframe' => 'Utilisez ce lien pour télécharger vos fichiers. Le lien expirera dans 1 heure.', - 'new_signup' => 'Nouvelle inscription', - 'new_signup_text' => 'Un nouveau compte a été créé par :user - :email - de l\'adresse IP :ip', - 'notification_payment_paid_subject' => 'Le paiement a été fait par :client', - 'notification_partial_payment_paid_subject' => 'Le paiement partiel a été fait par :client', - 'notification_payment_paid' => 'Un paiement de :amount a été fait par le client : pour la facture :invoice', - 'notification_partial_payment_paid' => 'Un paiement partiel de :amount a été fait par le client : pour la facture :invoice', - 'notification_bot' => 'Bot de notifications', - 'invoice_number_placeholder' => 'Facture N° :invoice', - 'entity_number_placeholder' => ':entity N° :entity_number', - 'email_link_not_working' => 'Si le bouton ci-dessus ne fonctionne pas correctement, cliquez sur le lien', - 'display_log' => 'Afficher le registre', - 'send_fail_logs_to_our_server' => 'Rapporter les erreurs en temps réel', - 'setup' => 'Configuration', - 'quick_overview_statistics' => 'Aperçu et statistiques', - 'update_your_personal_info' => 'Mettre à jour vos infos personnelles', - 'name_website_logo' => 'Nom, site web et logo', - 'make_sure_use_full_link' => 'Utilisez le lien complet vers votre site', - 'personal_address' => 'Adresse personnelle', - 'enter_your_personal_address' => 'Saisissez votre adresse personnelle', - 'enter_your_shipping_address' => 'Saisissez votre adresse de livraison', - 'list_of_invoices' => 'Liste des factures', - 'with_selected' => 'Avec la sélection de', - 'invoice_still_unpaid' => 'Cette facture est toujours impayée. Cliquez sur le bouton pour compléter le paiement', - 'list_of_recurring_invoices' => 'Liste des factures récurrentes', - 'details_of_recurring_invoice' => 'Détails à propos des factures récurrentes', - 'cancellation' => 'Annulation', - 'about_cancellation' => 'Pour cesser la facturation récurrente, cliquez sur la requête d\'annulation.', - 'cancellation_warning' => 'Avertissement! Vous avez demandé une annulation de ce service. Votre service pourrait être annulé sans autre notification.', - 'cancellation_pending' => 'Annulation en attente. Nous vous tiendrons au courant.', - 'list_of_payments' => 'Liste des paiements', - 'payment_details' => 'Détails du paiement', - 'list_of_payment_invoices' => 'Liste des factures affectées par le paiement', - 'list_of_payment_methods' => 'Liste des modes de paiement', - 'payment_method_details' => 'Détails du mode de paiement', - 'permanently_remove_payment_method' => 'Supprimer de façon définitive ce mode de paiement', - 'warning_action_cannot_be_reversed' => 'Avertissement! Cette action ne peut être annulée!', - 'confirmation' => 'Confirmation', - 'list_of_quotes' => 'Soumissions', - 'waiting_for_approval' => 'En attente d\'approbation', - 'quote_still_not_approved' => 'Cette soumission n\'a pas encore été approuvée', - 'list_of_credits' => 'Crédits', - 'required_extensions' => 'Extensions requises', - 'php_version' => 'Version PHP', - 'writable_env_file' => 'Fichier .env inscriptible', - 'env_not_writable' => 'Le fichier .env n\'est pas inscriptible par l\'utilisateur en cours', - 'minumum_php_version' => 'Version PHP minimale', - 'satisfy_requirements' => 'Assurez-vous que toutes les exigences sont satisfaites', - 'oops_issues' => 'Oups, quelque chose cloche !', - 'open_in_new_tab' => 'Ouvrir dans un nouvel onglet', - 'complete_your_payment' => 'Paiement complet', - 'authorize_for_future_use' => 'Autoriser ce mode de paiement pour usage ultérieur', - 'page' => 'Page', - 'per_page' => 'Par page', - 'of' => 'De', - 'view_credit' => 'Voir le crédit', - 'to_view_entity_password' => 'Pour voir :entity, vous devez saisir votre mot de passe.', - 'showing_x_of' => 'Affiche :first de :last de :total résultats', - 'no_results' => 'Aucun résultat', - 'payment_failed_subject' => 'Le paiement a échoué pour le client :client', - 'payment_failed_body' => 'Un paiement fait par le client :client a échoué avec le message :message', - 'register' => 'S\'inscrire', - 'register_label' => 'Créer votre compte en quelques secondes', - 'password_confirmation' => 'Confirmer votre mot de passe', - 'verification' => 'Vérification', - 'complete_your_bank_account_verification' => 'Avant d\'utiliser un compte bancaire, il doit être vérifié.', - 'checkout_com' => 'Checkout.com', - 'footer_label' => 'Tous droits réservés © :year :company.', - 'credit_card_invalid' => 'Le numéro de carte de crédit fourni n\'est pas valide', - 'month_invalid' => 'Le mois indiqué n\'est pas valide', - 'year_invalid' => 'L\'année indiquée n\'est pas valide', - 'https_required' => 'HTTPS est requis, l\'envoi du formulaire va échouer', - 'if_you_need_help' => 'Si vous avez besoin d\'aide, vous pouvez écrire à notre', - 'update_password_on_confirm' => 'Après la mise à jour du mot de passe, votre compte sera confirmé.', - 'bank_account_not_linked' => 'Pour payer avec un compte bancaire, vous devez d\'abord l\'ajouter comme un mode de paiement.', - 'application_settings_label' => 'Enregistrons les informations de base sur votre Ninja de la facture!', - 'recommended_in_production' => 'Fortement recommandé en mode production', - 'enable_only_for_development' => 'Activer seulement en mode développement', - 'test_pdf' => 'Tester le PDF', - 'checkout_authorize_label' => 'Checkout.com peut être enregistrer comme un mode de paiement pour usage ultérieur, lors de la première transaction. Cochez "Enregistrer les infos de carte de crédit" lors du processus de paiement.', - 'sofort_authorize_label' => 'Le compte bancaire (SOFORT) peut être enregistré comme un mode de paiement pour usage ultérieur, lors de la première transaction. Cochez "Enregistrer les infos de paiement" lors du processus de paiement.', - 'node_status' => 'État du noeud', - 'npm_status' => 'État du NPM', - 'node_status_not_found' => 'Nœud introuvable. Est-il installé ?', - 'npm_status_not_found' => 'NPM introuvable. Est-il installé ?', - 'locked_invoice' => 'La facture est verrouillée et ne peut être modifiée', - 'downloads' => 'Téléchargements', - 'resource' => 'Ressource', - 'document_details' => 'Détails du document', - 'hash' => 'Hash', - 'resources' => 'Ressources', - 'allowed_file_types' => 'Types de fichiers autorisés:', - 'common_codes' => 'Codes communs et leurs significations', - 'payment_error_code_20087' => '20087 : Données de suivi invalides (CVV et/ou date d\'expiration non valables)', - 'download_selected' => 'Télécharger la sélection', - 'to_pay_invoices' => 'Pour payer les factures, vous devez', - 'add_payment_method_first' => 'ajouter un mode de paiement', - 'no_items_selected' => 'Aucun article sélectionné', - 'payment_due' => 'Paiement dû', - 'account_balance' => 'Solde de compte', - 'thanks' => 'Merci', - 'minimum_required_payment' => 'Le paiement minimum requis est :amount', - 'under_payments_disabled' => 'L\'entreprise d\'autorise pas les surpaiements.', - 'over_payments_disabled' => 'L\'entreprise d\'autorise pas les souspaiements.', - 'saved_at' => 'Enregistré à :time', - 'credit_payment' => 'Le crédit a été appliqué à la facture :invoice_number', - 'credit_subject' => 'Nouveau crédit :credit de :account', - 'credit_message' => 'Pour voir le crédit de :amount, cliquez sur le lien ci-dessous.', - 'payment_type_Crypto' => 'Cryptodevise', - 'payment_type_Credit' => 'Crédit', - 'store_for_future_use' => 'Enregistrer pour un usage ultérieur', - 'pay_with_credit' => 'Payer avec un crédit', - 'payment_method_saving_failed' => 'Ce mode de paiement ne peut pas être enregistré pour usage ultérieur.', - 'pay_with' => 'Payer avec', - 'n/a' => 'N/D', - 'by_clicking_next_you_accept_terms' => 'En cliquant sur "Prochaine étape", vous acceptez les conditions.', - 'not_specified' => 'Non spécifié', - 'before_proceeding_with_payment_warning' => 'Avant de procéder au paiement, vous devez remplir les champs suivants', - 'after_completing_go_back_to_previous_page' => 'Retournez à la page précédente après avoir complété', - 'pay' => 'Payer', - 'instructions' => 'Instructions', - 'notification_invoice_reminder1_sent_subject' => 'Le rappel 1 pour la facture :invoice a été envoyé à :client', - 'notification_invoice_reminder2_sent_subject' => 'Le rappel 2 pour la facture :invoice a été envoyé à :client', - 'notification_invoice_reminder3_sent_subject' => 'Le rappel 3 pour la facture :invoice a été envoyé à :client', - 'notification_invoice_custom_sent_subject' => 'Le rappel personnalisé pour la facture :invoice a été envoyé à :client', - 'notification_invoice_reminder_endless_sent_subject' => 'Un rappel perpétuel pour la facture :invoice a été envoyé à :client', - 'assigned_user' => 'Utilisateur affecté', - 'setup_steps_notice' => 'Pour accéder à la prochaine étape, vous devez tester chaque section.', - 'setup_phantomjs_note' => 'Notes à propos de Phantom JS. En savoir plus.', - 'minimum_payment' => 'Paiement minimum', - 'no_action_provided' => 'Aucune action reçue. Si c’est une erreur, veuillez contacter le soutien technique.', - 'no_payable_invoices_selected' => 'Aucune des factures sélectionnées porte un solde dû. Assurez-vous que vous n\'essayez pas de payer une facture provisoire ou une facture dont le solde est nul.', - 'required_payment_information' => 'Détails de paiement requis', - 'required_payment_information_more' => 'Pour terminer le paiement, nous avons besoin de plus d\'informations à propos de vous.', - 'required_client_info_save_label' => 'Ces informations seront sauvegardées. Vous n\'aurez pas à les saisir la prochaine fois.', - 'notification_credit_bounced' => 'Nous n\'avons pas pu émettre de Credit :invoice to :contact. \n :error', - 'notification_credit_bounced_subject' => 'Impossible d\'émettre Crédit :invoice', - 'save_payment_method_details' => 'Enregistrer les infos de mode de paiement', - 'new_card' => 'Nouvelle carte', - 'new_bank_account' => 'Nouveau compte bancaire', - 'company_limit_reached' => 'Limite de :limit entreprises par compte.', - 'credits_applied_validation' => 'Le total des crédits octroyés ne peut être supérieur au total des factures', - 'credit_number_taken' => 'Ce numéro de crédit est déjà utilisé', - 'credit_not_found' => 'Crédit introuvable', - 'invoices_dont_match_client' => 'Les factures sélectionnées proviennent de plus d\'un client', - 'duplicate_credits_submitted' => 'Crédits soumis en double.', - 'duplicate_invoices_submitted' => 'Factures soumises en double.', - 'credit_with_no_invoice' => 'Vous devez avoir une facture en règle avant de pouvoir utiliser un crédit lors d\'un paiement', - 'client_id_required' => 'Le numéro d\'identification du client est requis', - 'expense_number_taken' => 'Numéro de remboursement déjà utilisé', - 'invoice_number_taken' => 'Numéro de facture déjà utilisé', - 'payment_id_required' => 'Numéro de paiement requis.', - 'unable_to_retrieve_payment' => 'Impossible de récupérer le numéro de paiement spécifié', - 'invoice_not_related_to_payment' => 'La facture #:invoice n\'est pas liée à ce paiement', - 'credit_not_related_to_payment' => 'Le crédit #:credit n\'est pas lié à ce paiement', - 'max_refundable_invoice' => 'Essai de remboursement plus élevé que permis pour la facture #:invoice. Le maximum remboursable est :amount.', - 'refund_without_invoices' => 'Essai de remboursement d\'un paiement avec factures jointes. Veuillez spécifier une ou des factures valides à rembourser.', - 'refund_without_credits' => 'Essai de remboursement d\'un paiement avec crédits jointes. Veuillez spécifier un ou des crédits valides à rembourser.', - 'max_refundable_credit' => 'Essai de remboursement plus élevé que permis pour le crédit :credit. Le maximum remboursable est :amount.', - 'project_client_do_not_match' => 'Partiellement non-appliquée', - 'quote_number_taken' => 'Ce numéro de soumission est déjà pris', - 'recurring_invoice_number_taken' => 'Le numéro de facture récurrente :number est déjà pris', - 'user_not_associated_with_account' => 'L\'utilisateur n\'est pas associé à ce compte', - 'amounts_do_not_balance' => 'Les montants ne balancent pas correctement.', - 'insufficient_applied_amount_remaining' => 'Montant restant affecté insuffisant pour couvrir ce paiement.', - 'insufficient_credit_balance' => 'Solde insuffisant sur le crédit', - 'one_or_more_invoices_paid' => 'Une ou plusieurs factures ont été payées', - 'invoice_cannot_be_refunded' => 'La facture #:number ne peut être remboursée', - 'attempted_refund_failed' => 'Essai de remboursement du montant :amount seulement :refundable_amount  disponible pour remboursement.', - 'user_not_associated_with_this_account' => 'Cet utilisateur ne peut être rattaché à cette entreprise. Peut-être a-t-il déjà enregistré un utilisateur sur un autre compte?', - 'migration_completed' => 'Migration complétée', - 'migration_completed_description' => 'La migration est complétée. Veuillez vérifier vos données après la connexion.', - 'api_404' => '404 | Rien à voir ici!', - 'large_account_update_parameter' => 'Le chargement d\'un gros compte est impossible sans un paramètre updated_at', - 'no_backup_exists' => 'Aucune sauvegarde n\'existe pour cette activité', - 'company_user_not_found' => 'L\'enregistrement de l\'entreprise de l\'utilisateur introuvable.', - 'no_credits_found' => 'Aucun crédit trouvé.', - 'action_unavailable' => 'L\'action :action demandée n\'est pas disponible', - 'no_documents_found' => 'Aucun document trouvé', - 'no_group_settings_found' => 'Aucun paramètre de groupe trouvé', - 'access_denied' => 'Permissions insuffisantes pour accéder/modifier cette ressource', - 'invoice_cannot_be_marked_paid' => 'La facture ne peut pas être marquée comme payée', - 'invoice_license_or_environment' => 'License invalide, ou environnement :environment invalide', - 'route_not_available' => 'La route n\'est pas disponible', - 'invalid_design_object' => 'Objet de modèle personnalisé invalide', - 'quote_not_found' => 'Soumission(s) introuvable(s)', - 'quote_unapprovable' => 'L\'approbation de cette soumission ne peut se faire puisqu\'elle est expirée.', - 'scheduler_has_run' => 'Le planificateur a démarré', - 'scheduler_has_never_run' => 'Le planificateur n\'a jamais démarré', - 'self_update_not_available' => 'La mise à jour manuelle n\'est pas disponible sur ce système', - 'user_detached' => 'L\'utilisateur a été détaché de l\'entreprise', - 'create_webhook_failure' => 'Création Webhook impossible', - 'payment_message_extended' => 'Merci pour votre paiement d\'un montant de :amount', - 'online_payments_minimum_note' => 'Note: Les paiements en ligne sont acceptés seulement si le montant est plus élevé que 1$ ou en devise équivalente.', - 'payment_token_not_found' => 'Le jeton de paiement est introuvable. Veuillez essayer de nouveau. Si le problème persiste, essayez avec un autre mode de paiement', - 'vendor_address1' => 'Rue du fournisseur', - 'vendor_address2' => 'App du fournisseur', - 'partially_unapplied' => 'Partiellement non-appliquée', - 'select_a_gmail_user' => 'Veuillez sélectionner un utilisateur authentifié avec Gmail', - 'list_long_press' => 'Longue pression pour liste', - 'show_actions' => 'Afficher les actions', - 'start_multiselect' => 'Démarrer la multisélection', - 'email_sent_to_confirm_email' => 'Un courriel a été envoyé pour confirmer l\'adresse courriel', - 'converted_paid_to_date' => 'Payé à ce jour converti', - 'converted_credit_balance' => 'Solde de crédit converti', - 'converted_total' => 'Total converti', - 'reply_to_name' => 'Répondre à', - 'payment_status_-2' => 'Partiellement non-appliquée', - 'color_theme' => 'Couleur de thème', - 'start_migration' => 'Démarrer la migration', - 'recurring_cancellation_request' => 'Demande d\'annulation de facture récurrente de :contact', - 'recurring_cancellation_request_body' => ':contact du client :client a demandé l\'annulation de la facture récurrente : invoice', - 'hello' => 'Bonjour', - 'group_documents' => 'Grouper les documents', - 'quote_approval_confirmation_label' => 'Êtes-vous certain de vouloir approuver cette soumission ?', - 'migration_select_company_label' => 'Sélectionnez les entreprises pour la migration', - 'force_migration' => 'Forcer la migration', - 'require_password_with_social_login' => 'Requiert un mot de passe avec une connexion de réseau social', - 'stay_logged_in' => 'Restez connecté', - 'session_about_to_expire' => 'Avertissement: Votre session va expirer bientôt', - 'count_hours' => ':count heures', - 'count_day' => '1 jour', - 'count_days' => ':count jours', - 'web_session_timeout' => 'Expiration de la session web', - 'security_settings' => 'Paramètres de sécurité', - 'resend_email' => 'Renvoyer le courriel', - 'confirm_your_email_address' => 'Veuillez confirmer votre adresse courriel', - 'freshbooks' => 'FreshBooks', - 'invoice2go' => 'Invoice2go', - 'invoicely' => 'Invoicely', - 'waveaccounting' => 'Wave Accounting', - 'zoho' => 'Zoho', - 'accounting' => 'Comptabilité', - 'required_files_missing' => 'Veuillez fournir tous les CSV.', - 'migration_auth_label' => 'Continuez en vous authentifiant.', - 'api_secret' => 'API secret', - 'migration_api_secret_notice' => 'Vous pouvez trouver API_SECRET dans le fichier .env ou Invoice Ninja v5. Si la propriété est manquante, laissez le champ vide.', - 'billing_coupon_notice' => 'Votre rabais sera appliqué au moment de régler votre facture.', - 'use_last_email' => 'Utiliser le dernier e-mail', - 'activate_company' => 'Activer l\'entreprise', - 'activate_company_help' => 'Activez les courriels, les factures récurrentes et les notifications', - 'an_error_occurred_try_again' => 'Une erreur s\'est produite, veuillez réessayer', - 'please_first_set_a_password' => 'Veuillez d\'abord définir un mot de passe', - 'changing_phone_disables_two_factor' => 'Attention: modifier votre numéro de téléphone désactivera l\'authentification à deux facteurs 2FA', - 'help_translate' => 'Aide à la traduction', - 'please_select_a_country' => 'Veuillez sélectionner un pays', - 'disabled_two_factor' => 'L\'authentification à deux facteurs 2FA a été désactivée', - 'connected_google' => 'Le compte a été connecté', - 'disconnected_google' => 'Le compte a été déconnecté', - 'delivered' => 'Livré', - 'spam' => 'Pourriel', - 'view_docs' => 'Afficher la documentation', - 'enter_phone_to_enable_two_factor' => 'Veuillez fournir un numéro de téléphone mobile pour activer l\'authentification à deux facteurs', - 'send_sms' => 'Envoyer un SMS', - 'sms_code' => 'Code SMS', - 'connect_google' => 'Connecter Google', - 'disconnect_google' => 'Déconnecter Google', - 'disable_two_factor' => 'Désactiver l\'authentification à deux facteurs', - 'invoice_task_datelog' => 'Facturer le journal des dates des tâches', - 'invoice_task_datelog_help' => 'Ajouter les détails de date aux lignes d\'articles des factures', - 'promo_code' => 'Code promo', - 'recurring_invoice_issued_to' => 'Facture récurrente émise à', - 'subscription' => 'Abonnement', - 'new_subscription' => 'Nouvel abonnement', - 'deleted_subscription' => 'L\'abonnement a été supprimé', - 'removed_subscription' => 'L\'abonnement a été retiré', - 'restored_subscription' => 'L\'abonnement a été restauré', - 'search_subscription' => 'Recherche de 1 abonnement', - 'search_subscriptions' => 'Recherche :count abonnements', - 'subdomain_is_not_available' => 'Le sous-domaine n\'est pas disponible', - 'connect_gmail' => 'Connectez Gmail', - 'disconnect_gmail' => 'Déconnecter Gmail', - 'connected_gmail' => 'Gmail a été connecté', - 'disconnected_gmail' => 'Gmail a été déconnecté', - 'update_fail_help' => 'Les modifications apportées au code de base peuvent bloquer la mise à jour, vous pouvez exécuter cette commande pour annuler les modifications:', - 'client_id_number' => 'Numéro d\'identification du client', - 'count_minutes' => ':count minutes', - 'password_timeout' => 'Délai d\'expiration du mot de passe', - 'shared_invoice_credit_counter' => 'Partager le compteur pour les factures et les crédits', + 'next' => 'Suivant', + 'next_step' => 'Étape suivante', + 'notification_credit_sent_subject' => 'le crédit :invoice a été envoyé à :client', + 'notification_credit_viewed_subject' => 'Le crédit :invoice a été consulté par :client', + 'notification_credit_sent' => 'Un crédit de :amount a été envoyé par courriel au client :client.', + 'notification_credit_viewed' => 'Un crédit de :amount a été consulté par le client :client.', + 'reset_password_text' => 'Saisissez votre adresse courriel pour réinitialiser votre mot de passe.', + 'password_reset' => 'Réinitialisation du mot de passe', + 'account_login_text' => 'Bienvenue ! Heureux de vous voir.', + 'request_cancellation' => 'Annuler la demande', + 'delete_payment_method' => 'Supprimer le mode de paiement', + 'about_to_delete_payment_method' => 'Le mode de paiement sera supprimé', + 'action_cant_be_reversed' => 'Cette action ne peut être annulée', + 'profile_updated_successfully' => 'Le profil a été mis à jour', + 'currency_ethiopian_birr' => 'birr éthiopien', + 'client_information_text' => 'Adresse permanente où vous recevez le courriel', + 'status_id' => 'État de facture', + 'email_already_register' => 'Cette adresse courriel est déjà liée à un compte', + 'locations' => 'Emplacements', + 'freq_indefinitely' => 'Indéfiniment', + 'cycles_remaining' => 'Cycles restants', + 'i_understand_delete' => 'Je comprends. Supprimer.', + 'download_files' => 'Télécharger les fichiers', + 'download_timeframe' => 'Utilisez ce lien pour télécharger vos fichiers. Le lien expirera dans 1 heure.', + 'new_signup' => 'Nouvelle inscription', + 'new_signup_text' => 'Un nouveau compte a été créé par :user - :email - de l\'adresse IP :ip', + 'notification_payment_paid_subject' => 'Le paiement a été fait par :client', + 'notification_partial_payment_paid_subject' => 'Le paiement partiel a été fait par :client', + 'notification_payment_paid' => 'Un paiement de :amount a été fait par le client : pour la facture :invoice', + 'notification_partial_payment_paid' => 'Un paiement partiel de :amount a été fait par le client : pour la facture :invoice', + 'notification_bot' => 'Bot de notifications', + 'invoice_number_placeholder' => 'Facture N° :invoice', + 'entity_number_placeholder' => ':entity N° :entity_number', + 'email_link_not_working' => 'Si le bouton ci-dessus ne fonctionne pas correctement, cliquez sur le lien', + 'display_log' => 'Afficher le registre', + 'send_fail_logs_to_our_server' => 'Rapporter les erreurs en temps réel', + 'setup' => 'Configuration', + 'quick_overview_statistics' => 'Aperçu et statistiques', + 'update_your_personal_info' => 'Mettre à jour vos infos personnelles', + 'name_website_logo' => 'Nom, site web et logo', + 'make_sure_use_full_link' => 'Utilisez le lien complet vers votre site', + 'personal_address' => 'Adresse personnelle', + 'enter_your_personal_address' => 'Saisissez votre adresse personnelle', + 'enter_your_shipping_address' => 'Saisissez votre adresse de livraison', + 'list_of_invoices' => 'Liste des factures', + 'with_selected' => 'Avec la sélection de', + 'invoice_still_unpaid' => 'Cette facture est toujours impayée. Cliquez sur le bouton pour compléter le paiement', + 'list_of_recurring_invoices' => 'Liste des factures récurrentes', + 'details_of_recurring_invoice' => 'Détails à propos des factures récurrentes', + 'cancellation' => 'Annulation', + 'about_cancellation' => 'Pour cesser la facturation récurrente, cliquez sur la requête d\'annulation.', + 'cancellation_warning' => 'Avertissement! Vous avez demandé une annulation de ce service. Votre service pourrait être annulé sans autre notification.', + 'cancellation_pending' => 'Annulation en attente. Nous vous tiendrons au courant.', + 'list_of_payments' => 'Liste des paiements', + 'payment_details' => 'Détails du paiement', + 'list_of_payment_invoices' => 'Liste des factures affectées par le paiement', + 'list_of_payment_methods' => 'Liste des modes de paiement', + 'payment_method_details' => 'Détails du mode de paiement', + 'permanently_remove_payment_method' => 'Supprimer de façon définitive ce mode de paiement', + 'warning_action_cannot_be_reversed' => 'Avertissement! Cette action ne peut être annulée!', + 'confirmation' => 'Confirmation', + 'list_of_quotes' => 'Soumissions', + 'waiting_for_approval' => 'En attente d\'approbation', + 'quote_still_not_approved' => 'Cette soumission n\'a pas encore été approuvée', + 'list_of_credits' => 'Crédits', + 'required_extensions' => 'Extensions requises', + 'php_version' => 'Version PHP', + 'writable_env_file' => 'Fichier .env inscriptible', + 'env_not_writable' => 'Le fichier .env n\'est pas inscriptible par l\'utilisateur en cours', + 'minumum_php_version' => 'Version PHP minimale', + 'satisfy_requirements' => 'Assurez-vous que toutes les exigences sont satisfaites', + 'oops_issues' => 'Oups, quelque chose cloche !', + 'open_in_new_tab' => 'Ouvrir dans un nouvel onglet', + 'complete_your_payment' => 'Paiement complet', + 'authorize_for_future_use' => 'Autoriser ce mode de paiement pour usage ultérieur', + 'page' => 'Page', + 'per_page' => 'Par page', + 'of' => 'De', + 'view_credit' => 'Voir le crédit', + 'to_view_entity_password' => 'Pour voir :entity, vous devez saisir votre mot de passe.', + 'showing_x_of' => 'Affiche :first de :last de :total résultats', + 'no_results' => 'Aucun résultat', + 'payment_failed_subject' => 'Le paiement a échoué pour le client :client', + 'payment_failed_body' => 'Un paiement fait par le client :client a échoué avec le message :message', + 'register' => 'S\'inscrire', + 'register_label' => 'Créer votre compte en quelques secondes', + 'password_confirmation' => 'Confirmer votre mot de passe', + 'verification' => 'Vérification', + 'complete_your_bank_account_verification' => 'Avant d\'utiliser un compte bancaire, il doit être vérifié.', + 'checkout_com' => 'Checkout.com', + 'footer_label' => 'Tous droits réservés © :year :company.', + 'credit_card_invalid' => 'Le numéro de carte de crédit fourni n\'est pas valide', + 'month_invalid' => 'Le mois indiqué n\'est pas valide', + 'year_invalid' => 'L\'année indiquée n\'est pas valide', + 'https_required' => 'HTTPS est requis, l\'envoi du formulaire va échouer', + 'if_you_need_help' => 'Si vous avez besoin d\'aide, vous pouvez écrire à notre', + 'update_password_on_confirm' => 'Après la mise à jour du mot de passe, votre compte sera confirmé.', + 'bank_account_not_linked' => 'Pour payer avec un compte bancaire, vous devez d\'abord l\'ajouter comme un mode de paiement.', + 'application_settings_label' => 'Enregistrons les informations de base sur votre Ninja de la facture!', + 'recommended_in_production' => 'Fortement recommandé en mode production', + 'enable_only_for_development' => 'Activer seulement en mode développement', + 'test_pdf' => 'Tester le PDF', + 'checkout_authorize_label' => 'Checkout.com peut être enregistrer comme un mode de paiement pour usage ultérieur, lors de la première transaction. Cochez "Enregistrer les infos de carte de crédit" lors du processus de paiement.', + 'sofort_authorize_label' => 'Le compte bancaire (SOFORT) peut être enregistré comme un mode de paiement pour usage ultérieur, lors de la première transaction. Cochez "Enregistrer les infos de paiement" lors du processus de paiement.', + 'node_status' => 'État du noeud', + 'npm_status' => 'État du NPM', + 'node_status_not_found' => 'Nœud introuvable. Est-il installé ?', + 'npm_status_not_found' => 'NPM introuvable. Est-il installé ?', + 'locked_invoice' => 'La facture est verrouillée et ne peut être modifiée', + 'downloads' => 'Téléchargements', + 'resource' => 'Ressource', + 'document_details' => 'Détails du document', + 'hash' => 'Hash', + 'resources' => 'Ressources', + 'allowed_file_types' => 'Types de fichiers autorisés:', + 'common_codes' => 'Codes communs et leurs significations', + 'payment_error_code_20087' => '20087 : Données de suivi invalides (CVV et/ou date d\'expiration non valables)', + 'download_selected' => 'Télécharger la sélection', + 'to_pay_invoices' => 'Pour payer les factures, vous devez', + 'add_payment_method_first' => 'ajouter un mode de paiement', + 'no_items_selected' => 'Aucun article sélectionné', + 'payment_due' => 'Paiement dû', + 'account_balance' => 'Solde de compte', + 'thanks' => 'Merci', + 'minimum_required_payment' => 'Le paiement minimum requis est :amount', + 'under_payments_disabled' => 'L\'entreprise d\'autorise pas les surpaiements.', + 'over_payments_disabled' => 'L\'entreprise d\'autorise pas les souspaiements.', + 'saved_at' => 'Enregistré à :time', + 'credit_payment' => 'Le crédit a été appliqué à la facture :invoice_number', + 'credit_subject' => 'Nouveau crédit :credit de :account', + 'credit_message' => 'Pour voir le crédit de :amount, cliquez sur le lien ci-dessous.', + 'payment_type_Crypto' => 'Cryptodevise', + 'payment_type_Credit' => 'Crédit', + 'store_for_future_use' => 'Enregistrer pour un usage ultérieur', + 'pay_with_credit' => 'Payer avec un crédit', + 'payment_method_saving_failed' => 'Ce mode de paiement ne peut pas être enregistré pour usage ultérieur.', + 'pay_with' => 'Payer avec', + 'n/a' => 'N/D', + 'by_clicking_next_you_accept_terms' => 'En cliquant sur "Prochaine étape", vous acceptez les conditions.', + 'not_specified' => 'Non spécifié', + 'before_proceeding_with_payment_warning' => 'Avant de procéder au paiement, vous devez remplir les champs suivants', + 'after_completing_go_back_to_previous_page' => 'Retournez à la page précédente après avoir complété', + 'pay' => 'Payer', + 'instructions' => 'Instructions', + 'notification_invoice_reminder1_sent_subject' => 'Le rappel 1 pour la facture :invoice a été envoyé à :client', + 'notification_invoice_reminder2_sent_subject' => 'Le rappel 2 pour la facture :invoice a été envoyé à :client', + 'notification_invoice_reminder3_sent_subject' => 'Le rappel 3 pour la facture :invoice a été envoyé à :client', + 'notification_invoice_custom_sent_subject' => 'Le rappel personnalisé pour la facture :invoice a été envoyé à :client', + 'notification_invoice_reminder_endless_sent_subject' => 'Un rappel perpétuel pour la facture :invoice a été envoyé à :client', + 'assigned_user' => 'Utilisateur affecté', + 'setup_steps_notice' => 'Pour accéder à la prochaine étape, vous devez tester chaque section.', + 'setup_phantomjs_note' => 'Notes à propos de Phantom JS. En savoir plus.', + 'minimum_payment' => 'Paiement minimum', + 'no_action_provided' => 'Aucune action reçue. Si c’est une erreur, veuillez contacter le soutien technique.', + 'no_payable_invoices_selected' => 'Aucune des factures sélectionnées porte un solde dû. Assurez-vous que vous n\'essayez pas de payer une facture provisoire ou une facture dont le solde est nul.', + 'required_payment_information' => 'Détails de paiement requis', + 'required_payment_information_more' => 'Pour terminer le paiement, nous avons besoin de plus d\'informations à propos de vous.', + 'required_client_info_save_label' => 'Ces informations seront sauvegardées. Vous n\'aurez pas à les saisir la prochaine fois.', + 'notification_credit_bounced' => 'Nous n\'avons pas pu émettre de Credit :invoice to :contact. \n :error', + 'notification_credit_bounced_subject' => 'Impossible d\'émettre Crédit :invoice', + 'save_payment_method_details' => 'Enregistrer les infos de mode de paiement', + 'new_card' => 'Nouvelle carte', + 'new_bank_account' => 'Nouveau compte bancaire', + 'company_limit_reached' => 'Limite de :limit entreprises par compte.', + 'credits_applied_validation' => 'Le total des crédits octroyés ne peut être supérieur au total des factures', + 'credit_number_taken' => 'Ce numéro de crédit est déjà utilisé', + 'credit_not_found' => 'Crédit introuvable', + 'invoices_dont_match_client' => 'Les factures sélectionnées proviennent de plus d\'un client', + 'duplicate_credits_submitted' => 'Crédits soumis en double.', + 'duplicate_invoices_submitted' => 'Factures soumises en double.', + 'credit_with_no_invoice' => 'Vous devez avoir une facture en règle avant de pouvoir utiliser un crédit lors d\'un paiement', + 'client_id_required' => 'Le numéro d\'identification du client est requis', + 'expense_number_taken' => 'Numéro de remboursement déjà utilisé', + 'invoice_number_taken' => 'Numéro de facture déjà utilisé', + 'payment_id_required' => 'Numéro de paiement requis.', + 'unable_to_retrieve_payment' => 'Impossible de récupérer le numéro de paiement spécifié', + 'invoice_not_related_to_payment' => 'La facture #:invoice n\'est pas liée à ce paiement', + 'credit_not_related_to_payment' => 'Le crédit #:credit n\'est pas lié à ce paiement', + 'max_refundable_invoice' => 'Essai de remboursement plus élevé que permis pour la facture #:invoice. Le maximum remboursable est :amount.', + 'refund_without_invoices' => 'Essai de remboursement d\'un paiement avec factures jointes. Veuillez spécifier une ou des factures valides à rembourser.', + 'refund_without_credits' => 'Essai de remboursement d\'un paiement avec crédits jointes. Veuillez spécifier un ou des crédits valides à rembourser.', + 'max_refundable_credit' => 'Essai de remboursement plus élevé que permis pour le crédit :credit. Le maximum remboursable est :amount.', + 'project_client_do_not_match' => 'Partiellement non-appliquée', + 'quote_number_taken' => 'Ce numéro de soumission est déjà pris', + 'recurring_invoice_number_taken' => 'Le numéro de facture récurrente :number est déjà pris', + 'user_not_associated_with_account' => 'L\'utilisateur n\'est pas associé à ce compte', + 'amounts_do_not_balance' => 'Les montants ne balancent pas correctement.', + 'insufficient_applied_amount_remaining' => 'Montant restant affecté insuffisant pour couvrir ce paiement.', + 'insufficient_credit_balance' => 'Solde insuffisant sur le crédit', + 'one_or_more_invoices_paid' => 'Une ou plusieurs factures ont été payées', + 'invoice_cannot_be_refunded' => 'La facture #:number ne peut être remboursée', + 'attempted_refund_failed' => 'Essai de remboursement du montant :amount seulement :refundable_amount  disponible pour remboursement.', + 'user_not_associated_with_this_account' => 'Cet utilisateur ne peut être rattaché à cette entreprise. Peut-être a-t-il déjà enregistré un utilisateur sur un autre compte?', + 'migration_completed' => 'Migration complétée', + 'migration_completed_description' => 'La migration est complétée. Veuillez vérifier vos données après la connexion.', + 'api_404' => '404 | Rien à voir ici!', + 'large_account_update_parameter' => 'Le chargement d\'un gros compte est impossible sans un paramètre updated_at', + 'no_backup_exists' => 'Aucune sauvegarde n\'existe pour cette activité', + 'company_user_not_found' => 'L\'enregistrement de l\'entreprise de l\'utilisateur introuvable.', + 'no_credits_found' => 'Aucun crédit trouvé.', + 'action_unavailable' => 'L\'action :action demandée n\'est pas disponible', + 'no_documents_found' => 'Aucun document trouvé', + 'no_group_settings_found' => 'Aucun paramètre de groupe trouvé', + 'access_denied' => 'Permissions insuffisantes pour accéder/modifier cette ressource', + 'invoice_cannot_be_marked_paid' => 'La facture ne peut pas être marquée comme payée', + 'invoice_license_or_environment' => 'License invalide, ou environnement :environment invalide', + 'route_not_available' => 'La route n\'est pas disponible', + 'invalid_design_object' => 'Objet de modèle personnalisé invalide', + 'quote_not_found' => 'Soumission(s) introuvable(s)', + 'quote_unapprovable' => 'L\'approbation de cette soumission ne peut se faire puisqu\'elle est expirée.', + 'scheduler_has_run' => 'Le planificateur a démarré', + 'scheduler_has_never_run' => 'Le planificateur n\'a jamais démarré', + 'self_update_not_available' => 'La mise à jour manuelle n\'est pas disponible sur ce système', + 'user_detached' => 'L\'utilisateur a été détaché de l\'entreprise', + 'create_webhook_failure' => 'Création Webhook impossible', + 'payment_message_extended' => 'Merci pour votre paiement d\'un montant de :amount', + 'online_payments_minimum_note' => 'Note: Les paiements en ligne sont acceptés seulement si le montant est plus élevé que 1$ ou en devise équivalente.', + 'payment_token_not_found' => 'Le jeton de paiement est introuvable. Veuillez essayer de nouveau. Si le problème persiste, essayez avec un autre mode de paiement', + 'vendor_address1' => 'Rue du fournisseur', + 'vendor_address2' => 'App du fournisseur', + 'partially_unapplied' => 'Partiellement non-appliquée', + 'select_a_gmail_user' => 'Veuillez sélectionner un utilisateur authentifié avec Gmail', + 'list_long_press' => 'Longue pression pour liste', + 'show_actions' => 'Afficher les actions', + 'start_multiselect' => 'Démarrer la multisélection', + 'email_sent_to_confirm_email' => 'Un courriel a été envoyé pour confirmer l\'adresse courriel', + 'converted_paid_to_date' => 'Payé à ce jour converti', + 'converted_credit_balance' => 'Solde de crédit converti', + 'converted_total' => 'Total converti', + 'reply_to_name' => 'Répondre à', + 'payment_status_-2' => 'Partiellement non-appliquée', + 'color_theme' => 'Couleur de thème', + 'start_migration' => 'Démarrer la migration', + 'recurring_cancellation_request' => 'Demande d\'annulation de facture récurrente de :contact', + 'recurring_cancellation_request_body' => ':contact du client :client a demandé l\'annulation de la facture récurrente : invoice', + 'hello' => 'Bonjour', + 'group_documents' => 'Grouper les documents', + 'quote_approval_confirmation_label' => 'Êtes-vous certain de vouloir approuver cette soumission ?', + 'migration_select_company_label' => 'Sélectionnez les entreprises pour la migration', + 'force_migration' => 'Forcer la migration', + 'require_password_with_social_login' => 'Requiert un mot de passe avec une connexion de réseau social', + 'stay_logged_in' => 'Restez connecté', + 'session_about_to_expire' => 'Avertissement: Votre session va expirer bientôt', + 'count_hours' => ':count heures', + 'count_day' => '1 jour', + 'count_days' => ':count jours', + 'web_session_timeout' => 'Expiration de la session web', + 'security_settings' => 'Paramètres de sécurité', + 'resend_email' => 'Renvoyer le courriel', + 'confirm_your_email_address' => 'Veuillez confirmer votre adresse courriel', + 'freshbooks' => 'FreshBooks', + 'invoice2go' => 'Invoice2go', + 'invoicely' => 'Invoicely', + 'waveaccounting' => 'Wave Accounting', + 'zoho' => 'Zoho', + 'accounting' => 'Comptabilité', + 'required_files_missing' => 'Veuillez fournir tous les CSV.', + 'migration_auth_label' => 'Continuez en vous authentifiant.', + 'api_secret' => 'API secret', + 'migration_api_secret_notice' => 'Vous pouvez trouver API_SECRET dans le fichier .env ou Invoice Ninja v5. Si la propriété est manquante, laissez le champ vide.', + 'billing_coupon_notice' => 'Votre rabais sera appliqué au moment de régler votre facture.', + 'use_last_email' => 'Utiliser le dernier e-mail', + 'activate_company' => 'Activer l\'entreprise', + 'activate_company_help' => 'Activez les courriels, les factures récurrentes et les notifications', + 'an_error_occurred_try_again' => 'Une erreur s\'est produite, veuillez réessayer', + 'please_first_set_a_password' => 'Veuillez d\'abord définir un mot de passe', + 'changing_phone_disables_two_factor' => 'Attention: modifier votre numéro de téléphone désactivera l\'authentification à deux facteurs 2FA', + 'help_translate' => 'Aide à la traduction', + 'please_select_a_country' => 'Veuillez sélectionner un pays', + 'disabled_two_factor' => 'L\'authentification à deux facteurs 2FA a été désactivée', + 'connected_google' => 'Le compte a été connecté', + 'disconnected_google' => 'Le compte a été déconnecté', + 'delivered' => 'Livré', + 'spam' => 'Pourriel', + 'view_docs' => 'Afficher la documentation', + 'enter_phone_to_enable_two_factor' => 'Veuillez fournir un numéro de téléphone mobile pour activer l\'authentification à deux facteurs', + 'send_sms' => 'Envoyer un SMS', + 'sms_code' => 'Code SMS', + 'connect_google' => 'Connecter Google', + 'disconnect_google' => 'Déconnecter Google', + 'disable_two_factor' => 'Désactiver l\'authentification à deux facteurs', + 'invoice_task_datelog' => 'Facturer le journal des dates des tâches', + 'invoice_task_datelog_help' => 'Ajouter les détails de date aux lignes d\'articles des factures', + 'promo_code' => 'Code promo', + 'recurring_invoice_issued_to' => 'Facture récurrente émise à', + 'subscription' => 'Abonnement', + 'new_subscription' => 'Nouvel abonnement', + 'deleted_subscription' => 'L\'abonnement a été supprimé', + 'removed_subscription' => 'L\'abonnement a été retiré', + 'restored_subscription' => 'L\'abonnement a été restauré', + 'search_subscription' => 'Recherche de 1 abonnement', + 'search_subscriptions' => 'Recherche :count abonnements', + 'subdomain_is_not_available' => 'Le sous-domaine n\'est pas disponible', + 'connect_gmail' => 'Connectez Gmail', + 'disconnect_gmail' => 'Déconnecter Gmail', + 'connected_gmail' => 'Gmail a été connecté', + 'disconnected_gmail' => 'Gmail a été déconnecté', + 'update_fail_help' => 'Les modifications apportées au code de base peuvent bloquer la mise à jour, vous pouvez exécuter cette commande pour annuler les modifications:', + 'client_id_number' => 'Numéro d\'identification du client', + 'count_minutes' => ':count minutes', + 'password_timeout' => 'Délai d\'expiration du mot de passe', + 'shared_invoice_credit_counter' => 'Partager le compteur pour les factures et les crédits', 'activity_80' => ':user a créé l\'abonnement :subscription', 'activity_81' => ':user a mis à jour l\'abonnement :subscription', 'activity_82' => ':user a archivé l\'abonnement :subscription', @@ -5212,6 +5212,34 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'receipt' => 'Reçu', 'charges' => 'Charges', 'email_report' => 'Envoyer le rapport', + 'payment_type_Pay Later' => 'Payer plus tard', + 'payment_type_credit' => 'Type de paiement crédit', + 'payment_type_debit' => 'Type de paiement débit', + 'send_emails_to' => 'Envoyer les courriels à', + 'primary_contact' => 'Contact principal', + 'all_contacts' => 'Tous les contacts', + 'insert_below' => 'Insérer ci-dessous', + 'nordigen_handler_subtitle' => 'will gain access for your selected bank account. After selecting your institution you are redirected to theire front-page to complete the request with your account credentials.', + 'nordigen_handler_error_heading_unknown' => 'An Error has occured', + 'nordigen_handler_error_contents_unknown' => 'An unknown Error has occured! Reason:', + 'nordigen_handler_error_heading_token_invalid' => 'Invalid Token', + 'nordigen_handler_error_contents_token_invalid' => 'The provided token was invalid. Please restart the flow, with a valid one_time_token. Contact support for help, if this issue persists.', + 'nordigen_handler_error_heading_account_config_invalid' => 'Missing Credentials', + 'nordigen_handler_error_contents_account_config_invalid' => 'The provided credentials for nordigen are eighter missing or invalid. Contact support for help, if this issue persists.', + 'nordigen_handler_error_heading_not_available' => 'Not Available', + 'nordigen_handler_error_contents_not_available' => 'This flow is not available for your account. Considder upgrading to enterprise version. Contact support for help, if this issue persists.', + 'nordigen_handler_error_heading_institution_invalid' => 'Invalid Institution', + 'nordigen_handler_error_contents_institution_invalid' => 'The provided institution-id is invalid or no longer valid. You can go to the bank selection page by clicking the button below or cancel the flow by clicking on the \'X\' above.', + 'nordigen_handler_error_heading_ref_invalid' => 'Invalid Reference', + 'nordigen_handler_error_contents_ref_invalid' => 'Nordigen did not provide a valid reference. Please run flow again and contact support, if this issue persists.', + 'nordigen_handler_error_heading_not_found' => 'Invalid Requisition', + 'nordigen_handler_error_contents_not_found' => 'Nordigen did not provide a valid reference. Please run flow again and contact support, if this issue persists.', + 'nordigen_handler_error_heading_requisition_invalid_status' => 'Not Ready', + 'nordigen_handler_error_contents_requisition_invalid_status' => 'You may called this site to early. Please finish authorization and refresh this page. Contact support for help, if this issue persists.', + 'nordigen_handler_error_heading_requisition_no_accounts' => 'No Accounts selected', + 'nordigen_handler_error_contents_requisition_no_accounts' => 'The service has not returned any valid accounts. Considder restarting the flow.', + 'nordigen_handler_restart' => 'Restart flow.', + 'nordigen_handler_return' => 'Return to application.', ); return $lang; From d0358086fcdbc5033dbee453ecd5822f8ce3206c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 14:31:03 +1100 Subject: [PATCH 08/13] bank integration cleanup --- app/Helpers/Bank/Nordigen/Nordigen.php | 2 +- app/Http/Controllers/Bank/NordigenController.php | 4 +++- app/Http/Controllers/BankIntegrationController.php | 7 +++++-- .../ConnectNordigenBankIntegrationRequest.php | 13 ++++--------- app/Jobs/Bank/ProcessBankTransactionsYodlee.php | 2 +- app/Jobs/Ninja/BankTransactionSync.php | 8 ++++---- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/Helpers/Bank/Nordigen/Nordigen.php b/app/Helpers/Bank/Nordigen/Nordigen.php index 9ed080407c75..2cef264335c6 100644 --- a/app/Helpers/Bank/Nordigen/Nordigen.php +++ b/app/Helpers/Bank/Nordigen/Nordigen.php @@ -39,7 +39,7 @@ class Nordigen $this->client = new \Nordigen\NordigenPHP\API\NordigenClient(config('ninja.nordigen.secret_id'), config('ninja.nordigen.secret_key')); - $this->client->createAccessToken(); // access_token is valid 24h -> so we dont have to implement a refresh-cycle + $this->client->createAccessToken(); } // metadata-section for frontend diff --git a/app/Http/Controllers/Bank/NordigenController.php b/app/Http/Controllers/Bank/NordigenController.php index 4644e32deb4d..72292b5ff561 100644 --- a/app/Http/Controllers/Bank/NordigenController.php +++ b/app/Http/Controllers/Bank/NordigenController.php @@ -31,6 +31,8 @@ class NordigenController extends BaseController public function connect(ConnectNordigenBankIntegrationRequest $request) { $data = $request->all(); + + /** @var array $context */ $context = $request->getTokenContent(); $lang = $data['lang'] ?? 'en'; $context["lang"] = $lang; @@ -62,7 +64,7 @@ class NordigenController extends BaseController "redirectUrl" => $context["redirect"] . "?action=nordigen_connect&status=failed&reason=account-config-invalid", ]); - if (!(Ninja::isSelfHost() || (Ninja::isHosted() && $account->isPaid() && $account->plan == 'enterprise'))) + if (!(Ninja::isSelfHost() || (Ninja::isHosted() && $account->isEnterprisePaidClient()))) return view('bank.nordigen.handler', [ 'lang' => $lang, 'company' => $company, diff --git a/app/Http/Controllers/BankIntegrationController.php b/app/Http/Controllers/BankIntegrationController.php index 891e7c0ca962..0b89f290cf42 100644 --- a/app/Http/Controllers/BankIntegrationController.php +++ b/app/Http/Controllers/BankIntegrationController.php @@ -298,11 +298,14 @@ class BankIntegrationController extends BaseController $account = $user->account; - $bank_integration = BankIntegration::withTrashed()->where('bank_account_id', $acc_id)->orWhere('nordigen_account_id', $acc_id)->company()->firstOrFail(); // @turbo124 please check + $bank_integration = BankIntegration::withTrashed() + ->where('bank_account_id', $acc_id) + ->orWhere('nordigen_account_id', $acc_id) + ->company() + ->firstOrFail(); if ($bank_integration->integration_type == BankIntegration::INTEGRATION_TYPE_YODLEE) $this->removeAccountYodlee($account, $bank_integration); - // we dont remove Accounts from nordigen, because they could be used within other companies $this->bank_integration_repo->delete($bank_integration); diff --git a/app/Http/Requests/Nordigen/ConnectNordigenBankIntegrationRequest.php b/app/Http/Requests/Nordigen/ConnectNordigenBankIntegrationRequest.php index 1ba76062c593..a34d2c0e09d5 100644 --- a/app/Http/Requests/Nordigen/ConnectNordigenBankIntegrationRequest.php +++ b/app/Http/Requests/Nordigen/ConnectNordigenBankIntegrationRequest.php @@ -36,24 +36,19 @@ class ConnectNordigenBankIntegrationRequest extends Request public function rules() { return [ - 'lang' => 'string', - 'institution_id' => 'string', - 'redirect' => 'string', ]; } - // @turbo124 @todo please check for validity, when request from frontend public function prepareForValidation() { $input = $this->all(); - if (!array_key_exists('redirect', $input)) { - $context = $this->getTokenContent(); + $context = $this->getTokenContent(); - $input["redirect"] = isset($context["is_react"]) && $context['is_react'] ? config('ninja.react_url') . "/#/settings/bank_accounts" : config('ninja.app_url'); + $input["redirect"] = isset($context["is_react"]) && $context['is_react'] ? config('ninja.react_url') . "/#/settings/bank_accounts" : config('ninja.app_url'); - $this->replace($input); - } + $this->replace($input); + } public function getTokenContent() { diff --git a/app/Jobs/Bank/ProcessBankTransactionsYodlee.php b/app/Jobs/Bank/ProcessBankTransactionsYodlee.php index efa9f1cadaf9..488003597ddb 100644 --- a/app/Jobs/Bank/ProcessBankTransactionsYodlee.php +++ b/app/Jobs/Bank/ProcessBankTransactionsYodlee.php @@ -162,7 +162,7 @@ class ProcessBankTransactionsYodlee implements ShouldQueue $now = now(); foreach ($transactions as $transaction) { - if (BankTransaction::query()->where('transaction_id', $transaction['transaction_id'])->where('company_id', $this->company->id)->where('bank_integration_id', $this->bank_integration->id)->withTrashed()->exists()) { // @turbo124 was not scoped to bank_integration_id => from my pov this should be present, because when an account was historized (is_deleted) a transaction can occur multiple (in the archived bank_integration and in the new one + if (BankTransaction::query()->where('transaction_id', $transaction['transaction_id'])->where('company_id', $this->company->id)->where('bank_integration_id', $this->bank_integration->id)->withTrashed()->exists()) { continue; } diff --git a/app/Jobs/Ninja/BankTransactionSync.php b/app/Jobs/Ninja/BankTransactionSync.php index b7c849681dab..ae963b4c1785 100644 --- a/app/Jobs/Ninja/BankTransactionSync.php +++ b/app/Jobs/Ninja/BankTransactionSync.php @@ -64,12 +64,12 @@ class BankTransactionSync implements ShouldQueue private function processYodlee() { - if (Ninja::isHosted()) { // @turbo124 @todo I migrated the schedule for the job within the kernel to execute on all platforms and use the same expression here to determine if yodlee can run or not. Please chek/verify + if (Ninja::isHosted()) { nlog("syncing transactions - yodlee"); Account::with('bank_integrations')->whereNotNull('bank_integration_account_id')->cursor()->each(function ($account) { - if ($account->isPaid() && $account->plan == 'enterprise') { + if ($account->isEnterprisePaidClient()) { $account->bank_integrations()->where('integration_type', BankIntegration::INTEGRATION_TYPE_YODLEE)->where('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account) { (new ProcessBankTransactionsYodlee($account->id, $bank_integration))->handle(); }); @@ -80,12 +80,12 @@ class BankTransactionSync implements ShouldQueue } private function processNordigen() { - if (config("ninja.nordigen.secret_id") && config("ninja.nordigen.secret_key")) { // @turbo124 check condition, when to execute this should be placed here (isSelfHosted || isPro/isEnterprise) + if (config("ninja.nordigen.secret_id") && config("ninja.nordigen.secret_key")) { nlog("syncing transactions - nordigen"); Account::with('bank_integrations')->cursor()->each(function ($account) { - if ((Ninja::isSelfHost() || (Ninja::isHosted() && $account->isPaid() && $account->plan == 'enterprise'))) { + if ((Ninja::isSelfHost() || (Ninja::isHosted() && $account->isEnterprisePaidClient()))) { $account->bank_integrations()->where('integration_type', BankIntegration::INTEGRATION_TYPE_NORDIGEN)->where('auto_sync', true)->cursor()->each(function ($bank_integration) { (new ProcessBankTransactionsNordigen($bank_integration))->handle(); }); From 338e7a0037731a8b08fc7d66dbc932e5d33799ae Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 14:57:36 +1100 Subject: [PATCH 09/13] Notify on bounced account creation email --- app/Jobs/PostMark/ProcessPostmarkWebhook.php | 8 ++++++-- app/Mail/Admin/VerifyUserObject.php | 1 - app/Notifications/Ninja/EmailBounceNotification.php | 10 ++-------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app/Jobs/PostMark/ProcessPostmarkWebhook.php b/app/Jobs/PostMark/ProcessPostmarkWebhook.php index b2152c8eb017..ea9db0590c16 100644 --- a/app/Jobs/PostMark/ProcessPostmarkWebhook.php +++ b/app/Jobs/PostMark/ProcessPostmarkWebhook.php @@ -30,6 +30,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use App\DataMapper\Analytics\Mail\EmailSpam; use App\DataMapper\Analytics\Mail\EmailBounce; use App\Notifications\Ninja\EmailSpamNotification; +use App\Notifications\Ninja\EmailBounceNotification; class ProcessPostmarkWebhook implements ShouldQueue { @@ -103,6 +104,11 @@ class ProcessPostmarkWebhook implements ShouldQueue case 'Delivery': return $this->processDelivery(); case 'Bounce': + + if($this->request['Subject'] == ctrans('texts.confirmation_subject')) { + $company->notification(new EmailBounceNotification($this->request['Email']))->ninja(); + } + return $this->processBounce(); case 'SpamComplaint': return $this->processSpamComplaint(); @@ -263,8 +269,6 @@ class ProcessPostmarkWebhook implements ShouldQueue (new SystemLogger($data, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company))->handle(); - // if(config('ninja.notification.slack')) - // $this->invitation->company->notification(new EmailBounceNotification($this->invitation->company->account))->ninja(); } // { diff --git a/app/Mail/Admin/VerifyUserObject.php b/app/Mail/Admin/VerifyUserObject.php index 283ce803da55..602be330eb46 100644 --- a/app/Mail/Admin/VerifyUserObject.php +++ b/app/Mail/Admin/VerifyUserObject.php @@ -42,7 +42,6 @@ class VerifyUserObject if($this->is_react) { $react_redirect = '?react=true'; - nlog("is react"); } $data = [ diff --git a/app/Notifications/Ninja/EmailBounceNotification.php b/app/Notifications/Ninja/EmailBounceNotification.php index 4ae6966d725a..40e919155bbc 100644 --- a/app/Notifications/Ninja/EmailBounceNotification.php +++ b/app/Notifications/Ninja/EmailBounceNotification.php @@ -21,11 +21,9 @@ class EmailBounceNotification extends Notification * * @return void */ - protected $account; - public function __construct($account) + public function __construct(private string $email_address) { - $this->account = $account; } /** @@ -64,11 +62,7 @@ class EmailBounceNotification extends Notification public function toSlack($notifiable) { - $content = "Email bounce notification for Account {$this->account->key} \n"; - - $owner = $this->account->companies()->first()->owner(); - - $content .= "Owner {$owner->present()->name() } | {$owner->email}"; + $content = "Email bounce notification for {$this->email_address} \n"; return (new SlackMessage) ->success() From a73bad4bec2638afe67255b9cd8da605591eedec Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 26 Dec 2023 15:00:06 +1100 Subject: [PATCH 10/13] Ensure correct translations for user logged in --- app/Listeners/User/UpdateUserLastLogin.php | 23 ++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/app/Listeners/User/UpdateUserLastLogin.php b/app/Listeners/User/UpdateUserLastLogin.php index c1eec95de915..3168b8981314 100644 --- a/app/Listeners/User/UpdateUserLastLogin.php +++ b/app/Listeners/User/UpdateUserLastLogin.php @@ -11,17 +11,19 @@ namespace App\Listeners\User; -use App\Jobs\Mail\NinjaMailerJob; -use App\Jobs\Mail\NinjaMailerObject; -use App\Jobs\Util\SystemLogger; -use App\Libraries\MultiDB; -use App\Mail\User\UserLoggedIn; +use App\Utils\Ninja; use App\Models\SystemLog; -use Illuminate\Broadcasting\InteractsWithSockets; +use App\Libraries\MultiDB; +use App\Jobs\Util\SystemLogger; +use App\Mail\User\UserLoggedIn; +use App\Jobs\Mail\NinjaMailerJob; +use Illuminate\Support\Facades\App; +use App\Jobs\Mail\NinjaMailerObject; +use Illuminate\Support\Facades\Cache; +use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Events\Dispatchable; -use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Cache; +use Illuminate\Broadcasting\InteractsWithSockets; class UpdateUserLastLogin implements ShouldQueue { @@ -46,6 +48,11 @@ class UpdateUserLastLogin implements ShouldQueue { MultiDB::setDb($event->company->db); + App::forgetInstance('translator'); + $t = app('translator'); + $t->replace(Ninja::transformTranslations($event->company->settings)); + App::setLocale($event->company->locale()); + $user = $event->user; $user->last_login = now(); $user->save(); From 64e2e1d7db6fc573fe8be6ef41127f0b07b10c9f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 27 Dec 2023 08:09:16 +1100 Subject: [PATCH 11/13] Update translations --- lang/en/texts.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lang/en/texts.php b/lang/en/texts.php index e40e8af5f708..86600fde6d47 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5227,23 +5227,23 @@ $lang = array( 'primary_contact' => 'Primary Contact', 'all_contacts' => 'All Contacts', 'insert_below' => 'Insert Below', - 'nordigen_handler_subtitle' => 'will gain access for your selected bank account. After selecting your institution you are redirected to theire front-page to complete the request with your account credentials.', - 'nordigen_handler_error_heading_unknown' => 'An Error has occured', - 'nordigen_handler_error_contents_unknown' => 'An unknown Error has occured! Reason:', + 'nordigen_handler_subtitle' => 'Bank account authentication. Selecting your institution to complete the request with your account credentials.', + 'nordigen_handler_error_heading_unknown' => 'An error has occured', + 'nordigen_handler_error_contents_unknown' => 'An unknown error has occurred! Reason:', 'nordigen_handler_error_heading_token_invalid' => 'Invalid Token', - 'nordigen_handler_error_contents_token_invalid' => 'The provided token was invalid. Please restart the flow, with a valid one_time_token. Contact support for help, if this issue persists.', + 'nordigen_handler_error_contents_token_invalid' => 'The provided token was invalid. Contact support for help, if this issue persists.', 'nordigen_handler_error_heading_account_config_invalid' => 'Missing Credentials', - 'nordigen_handler_error_contents_account_config_invalid' => 'The provided credentials for nordigen are eighter missing or invalid. Contact support for help, if this issue persists.', + 'nordigen_handler_error_contents_account_config_invalid' => 'Invalid or missing credentials for Gocardless Bank Account Data. Contact support for help, if this issue persists.', 'nordigen_handler_error_heading_not_available' => 'Not Available', - 'nordigen_handler_error_contents_not_available' => 'This flow is not available for your account. Considder upgrading to enterprise version. Contact support for help, if this issue persists.', + 'nordigen_handler_error_contents_not_available' => 'Feature unavailable, enterprise plan only.', 'nordigen_handler_error_heading_institution_invalid' => 'Invalid Institution', - 'nordigen_handler_error_contents_institution_invalid' => 'The provided institution-id is invalid or no longer valid. You can go to the bank selection page by clicking the button below or cancel the flow by clicking on the \'X\' above.', + 'nordigen_handler_error_contents_institution_invalid' => 'The provided institution-id is invalid or no longer valid.', 'nordigen_handler_error_heading_ref_invalid' => 'Invalid Reference', - 'nordigen_handler_error_contents_ref_invalid' => 'Nordigen did not provide a valid reference. Please run flow again and contact support, if this issue persists.', + 'nordigen_handler_error_contents_ref_invalid' => 'GoCardless did not provide a valid reference. Please run flow again and contact support, if this issue persists.', 'nordigen_handler_error_heading_not_found' => 'Invalid Requisition', - 'nordigen_handler_error_contents_not_found' => 'Nordigen did not provide a valid reference. Please run flow again and contact support, if this issue persists.', + 'nordigen_handler_error_contents_not_found' => 'GoCardless did not provide a valid reference. Please run flow again and contact support, if this issue persists.', 'nordigen_handler_error_heading_requisition_invalid_status' => 'Not Ready', - 'nordigen_handler_error_contents_requisition_invalid_status' => 'You may called this site to early. Please finish authorization and refresh this page. Contact support for help, if this issue persists.', + 'nordigen_handler_error_contents_requisition_invalid_status' => 'You called this site too early. Please finish authorization and refresh this page. Contact support for help, if this issue persists.', 'nordigen_handler_error_heading_requisition_no_accounts' => 'No Accounts selected', 'nordigen_handler_error_contents_requisition_no_accounts' => 'The service has not returned any valid accounts. Considder restarting the flow.', 'nordigen_handler_restart' => 'Restart flow.', From 057b6401f42b9fade861410f206ec2ceb5dde60f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 27 Dec 2023 13:55:29 +1100 Subject: [PATCH 12/13] Add filters for owner users --- app/Filters/UserFilters.php | 48 ++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/app/Filters/UserFilters.php b/app/Filters/UserFilters.php index 87475f25636f..be5a114e0774 100644 --- a/app/Filters/UserFilters.php +++ b/app/Filters/UserFilters.php @@ -66,21 +66,44 @@ class UserFilters extends QueryFilters */ public function entityFilter() { - return $this->builder->whereHas('company_users', function ($q) { - $q->where('company_id', '=', auth()->user()->company()->id); + + /** @var \App\Models\User $user */ + $user = auth()->user(); + + return $this->builder->whereHas('company_users', function ($q) use ($user){ + $q->where('company_id', '=', $user->company()->id); }); } + + /** + * Hides owner users from the list. + * + * @return Builder + */ + public function hideOwnerUsers(): Builder + { + /** @var \App\Models\User $user */ + $user = auth()->user(); + + return $this->builder->whereHas('company_users', function ($q) use ($user) { + $q->where('company_id', '=', $user->company()->id)->where('is_owner', false); + }); + + } /** * Filters users that have been removed from the * company, but not deleted from the system. * - * @return void + * @return Builder */ - public function hideRemovedUsers() + public function hideRemovedUsers(): Builder { - return $this->builder->whereHas('company_users', function ($q) { - $q->where('company_id', '=', auth()->user()->company()->id)->whereNull('deleted_at'); + /** @var \App\Models\User $user */ + $user = auth()->user(); + + return $this->builder->whereHas('company_users', function ($q) use ($user) { + $q->where('company_id', '=', $user->company()->id)->whereNull('deleted_at'); }); } @@ -98,12 +121,21 @@ class UserFilters extends QueryFilters return $this->builder; } + /** @var \App\Models\User $user */ + $user = auth()->user(); + return $this->builder ->orWhere($this->with_property, $value) ->orderByRaw("{$this->with_property} = ? DESC", [$value]) - ->where('account_id', auth()->user()->account_id); + ->where('account_id', $user->account_id); } - + + /** + * Returns users with permissions to send emails via OAuth + * + * @param string $value + * @return Builder + */ public function sending_users(string $value = ''): Builder { if (strlen($value) == 0 || $value != 'true') { From b69e9a3ae136b29dbd00ec0719b4fb0f22b39ec6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 27 Dec 2023 14:05:19 +1100 Subject: [PATCH 13/13] Fixes for passing Nordigen locales --- app/Helpers/Bank/Nordigen/Nordigen.php | 4 ++-- .../Transformer/TransactionTransformer.php | 4 ++-- app/Http/Controllers/Bank/NordigenController.php | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/Helpers/Bank/Nordigen/Nordigen.php b/app/Helpers/Bank/Nordigen/Nordigen.php index 2cef264335c6..ae060d860c4b 100644 --- a/app/Helpers/Bank/Nordigen/Nordigen.php +++ b/app/Helpers/Bank/Nordigen/Nordigen.php @@ -52,12 +52,12 @@ class Nordigen } // requisition-section - public function createRequisition(string $redirect, string $initutionId, string $reference) + public function createRequisition(string $redirect, string $initutionId, string $reference, string $userLanguage) { if ($this->test_mode && $initutionId != $this->sandbox_institutionId) throw new \Exception('invalid institutionId while in test-mode'); - return $this->client->requisition->createRequisition($redirect, $initutionId, null, $reference); + return $this->client->requisition->createRequisition($redirect, $initutionId, null, $reference, $userLanguage); } public function getRequisition(string $requisitionId) diff --git a/app/Helpers/Bank/Nordigen/Transformer/TransactionTransformer.php b/app/Helpers/Bank/Nordigen/Transformer/TransactionTransformer.php index 30e511d67aec..0850b7f59214 100644 --- a/app/Helpers/Bank/Nordigen/Transformer/TransactionTransformer.php +++ b/app/Helpers/Bank/Nordigen/Transformer/TransactionTransformer.php @@ -113,8 +113,8 @@ class TransactionTransformer implements BankRevenueInterface 'transaction_id' => $transaction["transactionId"], 'amount' => abs((int) $transaction["transactionAmount"]["amount"]), 'currency_id' => $this->convertCurrency($transaction["transactionAmount"]["currency"]), - 'category_id' => null, // nordigen has no categories - 'category_type' => array_key_exists('additionalInformation', $transaction) ? $transaction["additionalInformation"] : null, // TODO: institution specific keys like: GUTSCHRIFT, ABSCHLUSS, MONATSABSCHLUSS etc + 'category_id' => null, + 'category_type' => array_key_exists('additionalInformation', $transaction) ? $transaction["additionalInformation"] : '', 'date' => $transaction["bookingDate"], 'description' => $description, 'participant' => $participant, diff --git a/app/Http/Controllers/Bank/NordigenController.php b/app/Http/Controllers/Bank/NordigenController.php index 72292b5ff561..ce9e56a2d9f6 100644 --- a/app/Http/Controllers/Bank/NordigenController.php +++ b/app/Http/Controllers/Bank/NordigenController.php @@ -34,7 +34,8 @@ class NordigenController extends BaseController /** @var array $context */ $context = $request->getTokenContent(); - $lang = $data['lang'] ?? 'en'; + $company = $request->getCompany(); + $lang = $company->locale(); $context["lang"] = $lang; if (!$context) @@ -87,7 +88,7 @@ class NordigenController extends BaseController // redirect to requisition flow try { - $requisition = $nordigen->createRequisition(config('ninja.app_url') . '/nordigen/confirm', $data['institution_id'], $request->token); + $requisition = $nordigen->createRequisition(config('ninja.app_url') . '/nordigen/confirm', $data['institution_id'], $request->token, $lang); } catch (NordigenException $e) { // TODO: property_exists returns null in these cases... => why => therefore we just get unknown error everytime $responseBody is typeof GuzzleHttp\Psr7\Stream $responseBody = (string) $e->getResponse()->getBody(); @@ -135,12 +136,14 @@ class NordigenController extends BaseController public function confirm(ConfirmNordigenBankIntegrationRequest $request) { $data = $request->all(); - + $company = $request->getCompany(); + $account = $company->account; + $lang = $company->locale(); + /** @var array $context */ $context = $request->getTokenContent(); if (!array_key_exists('lang', $data) && $context['lang'] != 'en') - return redirect()->route('nordigen.confirm', array_merge(["lang" => $context['lang']], $request->query())); // redirect is required in order for the bank-ui to display everything properly - $lang = $data['lang'] ?? 'en'; + return redirect()->route('nordigen.confirm', array_merge(["lang" => $context['lang']], $request->query())); if (!$context || $context["context"] != "nordigen" || !array_key_exists("requisitionId", $context)) return view('bank.nordigen.handler', [ @@ -149,9 +152,6 @@ class NordigenController extends BaseController "redirectUrl" => ($context && array_key_exists("redirect", $context) ? $context["redirect"] : config('ninja.app_url')) . "?action=nordigen_connect&status=failed&reason=ref-invalid", ]); - $company = $request->getCompany(); - $account = $company->account; - if (!(config('ninja.nordigen.secret_id') && config('ninja.nordigen.secret_key'))) return view('bank.nordigen.handler', [ 'lang' => $lang,