diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index 22fcba5c52ab..95df25c6c2cf 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -13,6 +13,7 @@ namespace App\Console\Commands; use App; use App\Factory\ClientContactFactory; +use App\Factory\VendorContactFactory; use App\Models\Account; use App\Models\Client; use App\Models\ClientContact; @@ -72,7 +73,7 @@ class CheckData extends Command /** * @var string */ - protected $signature = 'ninja:check-data {--database=} {--fix=} {--client_id=} {--paid_to_date=} {--client_balance=}'; + protected $signature = 'ninja:check-data {--database=} {--fix=} {--client_id=} {--vendor_id=} {--paid_to_date=} {--client_balance=}'; /** * @var string @@ -112,6 +113,7 @@ class CheckData extends Command $this->checkClientBalances(); $this->checkContacts(); + $this->checkVendorContacts(); $this->checkEntityInvitations(); $this->checkCompanyData(); @@ -248,6 +250,65 @@ class CheckData extends Command } + private function checkVendorContacts() + { + // check for contacts with the contact_key value set + $contacts = DB::table('vendor_contacts') + ->whereNull('contact_key') + ->orderBy('id') + ->get(['id']); + $this->logMessage($contacts->count().' contacts without a contact_key'); + + if ($contacts->count() > 0) { + $this->isValid = false; + } + + if ($this->option('fix') == 'true') { + foreach ($contacts as $contact) { + DB::table('vendor_contacts') + ->where('id', '=', $contact->id) + ->whereNull('contact_key') + ->update([ + 'contact_key' => Str::random(config('ninja.key_length')), + ]); + } + } + + // check for missing contacts + $vendors = DB::table('vendors') + ->leftJoin('vendor_contacts', function ($join) { + $join->on('vendor_contacts.vendor_id', '=', 'vendors.id') + ->whereNull('vendor_contacts.deleted_at'); + }) + ->groupBy('vendors.id', 'vendors.user_id', 'vendors.company_id') + ->havingRaw('count(vendor_contacts.id) = 0'); + + if ($this->option('vendor_id')) { + $vendors->where('vendors.id', '=', $this->option('vendor_id')); + } + + $vendors = $vendors->get(['vendors.id', 'vendors.user_id', 'vendors.company_id']); + $this->logMessage($vendors->count().' vendors without any contacts'); + + if ($vendors->count() > 0) { + $this->isValid = false; + } + + if ($this->option('fix') == 'true') { + foreach ($vendors as $vendor) { + $this->logMessage("Fixing missing vendor contacts #{$vendor->id}"); + + $new_contact = VendorContactFactory::create($vendor->company_id, $vendor->user_id); + $new_contact->vendor_id = $vendor->id; + $new_contact->contact_key = Str::random(40); + $new_contact->is_primary = true; + $new_contact->save(); + } + } + + } + + private function checkFailedJobs() { if (config('ninja.testvars.travis')) { diff --git a/app/Jobs/Entity/CreateEntityPdf.php b/app/Jobs/Entity/CreateEntityPdf.php index e0d32d9532c9..f89ca3103990 100644 --- a/app/Jobs/Entity/CreateEntityPdf.php +++ b/app/Jobs/Entity/CreateEntityPdf.php @@ -168,10 +168,10 @@ class CreateEntityPdf implements ShouldQueue ]), 'variables' => $variables, 'options' => [ - 'all_pages_header' => $this->client->getSetting('all_pages_header'), - 'all_pages_footer' => $this->client->getSetting('all_pages_footer'), + 'all_pages_header' => $this->entity->client->getSetting('all_pages_header'), + 'all_pages_footer' => $this->entity->client->getSetting('all_pages_footer'), ], - 'process_markdown' => $this->client->company->markdown_enabled, + 'process_markdown' => $this->entity->client->company->markdown_enabled, ]; $maker = new PdfMakerService($state); diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index 84b13e104c03..3890db1c03f4 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -149,6 +149,7 @@ class Gateway extends StaticModel GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded','payment_intent.succeeded']], GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded','payment_intent.succeeded']], GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded','payment_intent.succeeded']], + GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], ]; break; case 57: diff --git a/app/PaymentDrivers/Stripe/SEPA.php b/app/PaymentDrivers/Stripe/SEPA.php index ac2d0ccd81f6..afa218097f61 100644 --- a/app/PaymentDrivers/Stripe/SEPA.php +++ b/app/PaymentDrivers/Stripe/SEPA.php @@ -47,7 +47,7 @@ class SEPA $data['country'] = $this->stripe->client->country->iso_3166_2; $data['payment_hash'] = $this->stripe->payment_hash->hash; - $intent = \Stripe\PaymentIntent::create([ + $intent_data = [ 'amount' => $data['stripe_amount'], 'currency' => 'eur', 'payment_method_types' => ['sepa_debit'], @@ -58,19 +58,12 @@ class SEPA 'payment_hash' => $this->stripe->payment_hash->hash, 'gateway_type_id' => GatewayType::SEPA, ], - ], $this->stripe->stripe_connect_auth); + ]; + + $intent = \Stripe\PaymentIntent::create($intent_data, $this->stripe->stripe_connect_auth); $data['pi_client_secret'] = $intent->client_secret; - if (count($data['tokens']) > 0) { - $setup_intent = $this->stripe->stripe->setupIntents->create([ - 'payment_method_types' => ['sepa_debit'], - 'customer' => $this->stripe->findOrCreateCustomer()->id, - ]); - - $data['si_client_secret'] = $setup_intent->client_secret; - } - $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]); $this->stripe->payment_hash->save(); diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php index ca4a7e5d5b7f..c80b55dc6089 100644 --- a/app/Utils/Helpers.php +++ b/app/Utils/Helpers.php @@ -263,9 +263,7 @@ class Helpers } return $value; - // $x = str_replace(["\n", "
"], ["\r", "
"], $value); - // return $x; } diff --git a/public/js/clients/payments/stripe-sepa.js b/public/js/clients/payments/stripe-sepa.js index 5769f96f3c3b..308730e5e60a 100644 --- a/public/js/clients/payments/stripe-sepa.js +++ b/public/js/clients/payments/stripe-sepa.js @@ -1,2 +1,2 @@ /*! For license information please see stripe-sepa.js.LICENSE.txt */ -(()=>{var e,t,n,o;function a(e,t){for(var n=0;n svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),void o.stripe.confirmSepaDebitSetup(document.querySelector("meta[name=si-client-secret").content,{payment_method:document.querySelector("input[name=token]").value}).then((function(e){if(!e.error)return document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.setupIntent),document.querySelector("#server-response").submit();console.error(error)})).catch((function(t){e.textContent=t,e.hidden=!1}))):""===document.getElementById("sepa-name").value?(document.getElementById("sepa-name").focus(),e.textContent=document.querySelector("meta[name=translation-name-required]").content,void(e.hidden=!1)):""===document.getElementById("sepa-email-address").value?(document.getElementById("sepa-email-address").focus(),e.textContent=document.querySelector("meta[name=translation-email-required]").content,void(e.hidden=!1)):document.getElementById("sepa-mandate-acceptance").checked?(document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),void o.stripe.confirmSepaDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{sepa_debit:o.iban,billing_details:{name:document.getElementById("sepa-name").value,email:document.getElementById("sepa-email-address").value}}}).then((function(e){return e.error?o.handleFailure(e.error.message):o.handleSuccess(e)}))):(e.textContent=document.querySelector("meta[name=translation-terms-required]").content,e.hidden=!1,void console.log("Terms"))}))})),this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=n}var t,n,o;return t=e,(n=[{key:"handleSuccess",value:function(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);var t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server-response").submit()}},{key:"handleFailure",value:function(e){var t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}}])&&a(t.prototype,n),o&&a(t,o),e}();new c(null!==(e=null===(t=document.querySelector('meta[name="stripe-publishable-key"]'))||void 0===t?void 0:t.content)&&void 0!==e?e:"",null!==(n=null===(o=document.querySelector('meta[name="stripe-account-id"]'))||void 0===o?void 0:o.content)&&void 0!==n?n:"").setupStripe().handle()})(); \ No newline at end of file +(()=>{var e,t,n,a;function o(e,t){for(var n=0;n svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),a.stripe.confirmSepaDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:document.querySelector("input[name=token]").value}).then((function(e){return e.error?a.handleFailure(e.error.message):a.handleSuccess(e)}));else{if(""===document.getElementById("sepa-name").value)return document.getElementById("sepa-name").focus(),e.textContent=document.querySelector("meta[name=translation-name-required]").content,void(e.hidden=!1);if(""===document.getElementById("sepa-email-address").value)return document.getElementById("sepa-email-address").focus(),e.textContent=document.querySelector("meta[name=translation-email-required]").content,void(e.hidden=!1);if(!document.getElementById("sepa-mandate-acceptance").checked)return e.textContent=document.querySelector("meta[name=translation-terms-required]").content,void(e.hidden=!1);document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),a.stripe.confirmSepaDebitPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{sepa_debit:a.iban,billing_details:{name:document.getElementById("sepa-name").value,email:document.getElementById("sepa-email-address").value}}}).then((function(e){return e.error?a.handleFailure(e.error.message):a.handleSuccess(e)}))}}))})),this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=n}var t,n,a;return t=e,(n=[{key:"handleSuccess",value:function(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.paymentIntent);var t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server-response").submit()}},{key:"handleFailure",value:function(e){var t=document.getElementById("errors");t.textContent="",t.textContent=e,t.hidden=!1,document.getElementById("pay-now").disabled=!1,document.querySelector("#pay-now > svg").classList.add("hidden"),document.querySelector("#pay-now > span").classList.remove("hidden")}}])&&o(t.prototype,n),a&&o(t,a),e}();new c(null!==(e=null===(t=document.querySelector('meta[name="stripe-publishable-key"]'))||void 0===t?void 0:t.content)&&void 0!==e?e:"",null!==(n=null===(a=document.querySelector('meta[name="stripe-account-id"]'))||void 0===a?void 0:a.content)&&void 0!==n?n:"").setupStripe().handle()})(); \ No newline at end of file diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 5ea689623061..57a00472a44c 100755 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -27,7 +27,7 @@ "/js/clients/payments/square-credit-card.js": "/js/clients/payments/square-credit-card.js?id=8f05ce6bd2d6cae7e5f2", "/js/clients/statements/view.js": "/js/clients/statements/view.js?id=4ed4c8a09803ddd0a9a7", "/js/clients/payments/razorpay-aio.js": "/js/clients/payments/razorpay-aio.js?id=c36ab5621413ef1de7c8", - "/js/clients/payments/stripe-sepa.js": "/js/clients/payments/stripe-sepa.js?id=2daa1a70aa5f8e6988f5", + "/js/clients/payments/stripe-sepa.js": "/js/clients/payments/stripe-sepa.js?id=da7b16ffaf5645535c7c", "/js/clients/payment_methods/authorize-checkout-card.js": "/js/clients/payment_methods/authorize-checkout-card.js?id=61becda97682c7909f29", "/js/clients/payments/stripe-giropay.js": "/js/clients/payments/stripe-giropay.js?id=2a973971ed2b890524ee", "/js/clients/payments/stripe-acss.js": "/js/clients/payments/stripe-acss.js?id=41367f4e80e52a0ab436", diff --git a/resources/js/clients/payments/stripe-sepa.js b/resources/js/clients/payments/stripe-sepa.js index e794eb9c823e..28e6fa69dfca 100644 --- a/resources/js/clients/payments/stripe-sepa.js +++ b/resources/js/clients/payments/stripe-sepa.js @@ -17,26 +17,21 @@ class ProcessSEPA { setupStripe = () => { - if (this.stripeConnect){ - // this.stripe.stripeAccount = this.stripeConnect; - - this.stripe = Stripe(this.key, { - stripeAccount: this.stripeConnect, - }); - - } - else { + if (this.stripeConnect) { + + this.stripe = Stripe(this.key, { + stripeAccount: this.stripeConnect, + }); + + } else { this.stripe = Stripe(this.key); } - - const elements = this.stripe.elements(); var style = { base: { color: '#32325d', - fontFamily: - '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', fontSmoothing: 'antialiased', fontSize: '16px', '::placeholder': { @@ -65,6 +60,10 @@ class ProcessSEPA { }; this.iban = elements.create('iban', options); this.iban.mount('#sepa-iban'); + + document.getElementById('sepa-name').value = document.querySelector('meta[name=client_name]').content; + document.getElementById('sepa-email-address').value = document.querySelector('meta[name=client_email]').content; + return this; }; @@ -97,107 +96,92 @@ class ProcessSEPA { }); document.getElementById('pay-now').addEventListener('click', (e) => { - if ( - document.querySelector('input[name=token]').value.length !== 0 - ) { - document.querySelector('#errors').hidden = true; + + console.log(document.querySelector('input[name=token]').value); + + if (document.querySelector('input[name=token]').value.length !== 0) { document.getElementById('pay-now').disabled = true; - document - .querySelector('#pay-now > svg') - .classList.remove('hidden'); - document - .querySelector('#pay-now > span') - .classList.add('hidden'); + document.querySelector('#pay-now > svg').classList.remove('hidden'); + document.querySelector('#pay-now > span').classList.add('hidden'); this.stripe - .confirmSepaDebitSetup( - document.querySelector('meta[name=si-client-secret') - .content, - { - payment_method: document.querySelector( - 'input[name=token]' - ).value, + .confirmSepaDebitPayment( + document.querySelector('meta[name=pi-client-secret') + .content, { + payment_method: document.querySelector('input[name=token]').value } ) .then((result) => { if (result.error) { - console.error(error); - - return; + return this.handleFailure(result.error.message); } - document.querySelector( - 'input[name="gateway_response"]' - ).value = JSON.stringify(result.setupIntent); - - return document - .querySelector('#server-response') - .submit(); - }) - .catch((error) => { - errors.textContent = error; - errors.hidden = false; + return this.handleSuccess(result); }); - return; - } + } else { - if (document.getElementById('sepa-name').value === '') { - document.getElementById('sepa-name').focus(); - errors.textContent = document.querySelector( - 'meta[name=translation-name-required]' - ).content; - errors.hidden = false; - return; - } + if (document.getElementById('sepa-name').value === '') { + document.getElementById('sepa-name').focus(); + errors.textContent = document.querySelector( + 'meta[name=translation-name-required]' + ).content; + errors.hidden = false; + return; + } - if (document.getElementById('sepa-email-address').value === '') { - document.getElementById('sepa-email-address').focus(); - errors.textContent = document.querySelector( - 'meta[name=translation-email-required]' - ).content; - errors.hidden = false; - return; - } + if (document.getElementById('sepa-email-address').value === '') { + document.getElementById('sepa-email-address').focus(); + errors.textContent = document.querySelector( + 'meta[name=translation-email-required]' + ).content; + errors.hidden = false; + return; + } - if (!document.getElementById('sepa-mandate-acceptance').checked) { - errors.textContent = document.querySelector( - 'meta[name=translation-terms-required]' - ).content; - errors.hidden = false; - console.log('Terms'); - return; - } + if (!document.getElementById('sepa-mandate-acceptance').checked) { + errors.textContent = document.querySelector( + 'meta[name=translation-terms-required]' + ).content; + errors.hidden = false; - document.getElementById('pay-now').disabled = true; - document.querySelector('#pay-now > svg').classList.remove('hidden'); - document.querySelector('#pay-now > span').classList.add('hidden'); + return; + } - this.stripe - .confirmSepaDebitPayment( - document.querySelector('meta[name=pi-client-secret') - .content, - { - payment_method: { - sepa_debit: this.iban, - billing_details: { - name: document.getElementById('sepa-name') - .value, - email: document.getElementById( - 'sepa-email-address' - ).value, + + document.getElementById('pay-now').disabled = true; + document.querySelector('#pay-now > svg').classList.remove('hidden'); + document.querySelector('#pay-now > span').classList.add('hidden'); + + + + this.stripe + .confirmSepaDebitPayment( + document.querySelector('meta[name=pi-client-secret') + .content, { + payment_method: { + sepa_debit: this.iban, + billing_details: { + name: document.getElementById('sepa-name') + .value, + email: document.getElementById( + 'sepa-email-address' + ).value, + }, }, - }, - } - ) - .then((result) => { - if (result.error) { - return this.handleFailure(result.error.message); - } + } + ) + .then((result) => { + if (result.error) { + return this.handleFailure(result.error.message); + } + + return this.handleSuccess(result); + }); + + } - return this.handleSuccess(result); - }); }); }; @@ -232,10 +216,10 @@ class ProcessSEPA { } const publishableKey = - document.querySelector('meta[name="stripe-publishable-key"]')?.content ?? + document.querySelector('meta[name="stripe-publishable-key"]') ? .content ? ? ''; const stripeConnect = - document.querySelector('meta[name="stripe-account-id"]')?.content ?? ''; + document.querySelector('meta[name="stripe-account-id"]') ? .content ? ? ''; new ProcessSEPA(publishableKey, stripeConnect).setupStripe().handle(); diff --git a/resources/views/portal/ninja2020/gateways/stripe/sepa/pay.blade.php b/resources/views/portal/ninja2020/gateways/stripe/sepa/pay.blade.php index 0eb9248ec828..2fb9c49c94a7 100644 --- a/resources/views/portal/ninja2020/gateways/stripe/sepa/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/stripe/sepa/pay.blade.php @@ -6,6 +6,8 @@ + +