mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
commit
6966c5cc61
@ -106,6 +106,12 @@ class PaymentController extends Controller
|
||||
*/
|
||||
public function process(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'contact_first_name' => ['required'],
|
||||
'contact_last_name' => ['required'],
|
||||
'contact_email' => ['required', 'email'],
|
||||
]);
|
||||
|
||||
return (new InstantPayment($request))->run();
|
||||
}
|
||||
|
||||
|
@ -351,5 +351,12 @@ class ClientContact extends Authenticatable implements HasLocalePreference
|
||||
return config('ninja.react_url')."/#/clients/{$this->client->hashed_id}";
|
||||
}
|
||||
|
||||
public function showRff(): bool
|
||||
{
|
||||
if (\strlen($this->first_name) === 0 || \strlen($this->last_name) === 0 || \strlen($this->email) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,15 @@ class InstantPayment
|
||||
public function run()
|
||||
{
|
||||
nlog($this->request->all());
|
||||
|
||||
$cc = auth()->guard('contact')->user();
|
||||
|
||||
$cc->first_name = $this->request->contact_first_name;
|
||||
$cc->last_name = $this->request->contact_last_name;
|
||||
$cc->email = $this->request->contact_email;
|
||||
|
||||
$cc->save();
|
||||
|
||||
$is_credit_payment = false;
|
||||
|
||||
$tokens = [];
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "invoiceninja",
|
||||
"name": "@invoiceninja/invoiceninja",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
9
public/build/assets/payment-0ace5bfa.js
vendored
9
public/build/assets/payment-0ace5bfa.js
vendored
@ -1,9 +0,0 @@
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/class s{constructor(e,t){this.shouldDisplayTerms=e,this.shouldDisplaySignature=t,this.termsAccepted=!1,this.submitting=!1}handleMethodSelect(e){document.getElementById("company_gateway_id").value=e.dataset.companyGatewayId,document.getElementById("payment_method_id").value=e.dataset.gatewayTypeId,this.shouldDisplaySignature&&!this.shouldDisplayTerms&&(this.signaturePad&&this.signaturePad.isEmpty()&&alert("Please sign"),this.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",()=>{this.termsAccepted=!0,this.submitForm()})),!this.shouldDisplaySignature&&this.shouldDisplayTerms&&(this.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",()=>{document.querySelector('input[name="signature"').value=this.signaturePad.toDataURL(),this.submitForm()})),this.shouldDisplaySignature&&this.shouldDisplayTerms&&(this.displaySignature(),document.getElementById("signature-next-step").addEventListener("click",()=>{this.displayTerms(),document.getElementById("accept-terms-button").addEventListener("click",()=>{document.querySelector('input[name="signature"').value=this.signaturePad.toDataURL(),this.termsAccepted=!0,this.submitForm()})})),!this.shouldDisplaySignature&&!this.shouldDisplayTerms&&this.submitForm()}submitForm(){this.submitting=!0,document.getElementById("payment-form").submit()}displayTerms(){document.getElementById("displayTermsModal").removeAttribute("style")}displaySignature(){document.getElementById("signature-next-step").disabled=!0,document.getElementById("displaySignatureModal").removeAttribute("style");const t=new SignaturePad(document.getElementById("signature-pad"),{penColor:"rgb(0, 0, 0)"});t.onEnd=function(){document.getElementById("signature-next-step").disabled=!1},this.signaturePad=t}handle(){document.querySelectorAll(".dropdown-gateway-button").forEach(e=>{e.addEventListener("click",()=>{this.submitting||this.handleMethodSelect(e)})})}}const i=document.querySelector('meta[name="require-invoice-signature"]').content,a=document.querySelector('meta[name="show-invoice-terms"]').content;new s(!!+i,!!+a).handle();
|
9
public/build/assets/payment-1bdbd169.js
vendored
Normal file
9
public/build/assets/payment-1bdbd169.js
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/class s{constructor(t,e,a){this.shouldDisplayTerms=t,this.shouldDisplaySignature=e,this.shouldDisplayRff=a,this.submitting=!1,this.steps=new Map,this.shouldDisplayRff&&this.steps.set("rff",{element:document.getElementById("displayRequiredFieldsModal"),nextButton:document.getElementById("rff-next-step"),callback:()=>{const n={firstName:document.querySelector('input[name="rff_first_name"]'),lastName:document.querySelector('input[name="rff_last_name"]'),email:document.querySelector('input[name="rff_email"]')};n.firstName&&(document.querySelector('input[name="contact_first_name"]').value=n.firstName.value),n.lastName&&(document.querySelector('input[name="contact_last_name"]').value=n.lastName.value),n.email&&(document.querySelector('input[name="contact_email"]').value=n.email.value)}}),this.shouldDisplaySignature&&this.steps.set("signature",{element:document.getElementById("displaySignatureModal"),nextButton:document.getElementById("signature-next-step"),boot:()=>this.signaturePad=new SignaturePad(document.getElementById("signature-pad"),{penColor:"rgb(0, 0, 0)"}),callback:()=>document.querySelector('input[name="signature"').value=this.signaturePad.toDataURL()}),this.shouldDisplayTerms&&this.steps.set("terms",{element:document.getElementById("displayTermsModal"),nextButton:document.getElementById("accept-terms-button")})}handleMethodSelect(t){if(document.getElementById("company_gateway_id").value=t.dataset.companyGatewayId,document.getElementById("payment_method_id").value=t.dataset.gatewayTypeId,this.steps.size===0)return this.submitForm();const e=this.steps.values().next().value;e.element.removeAttribute("style"),e.boot&&e.boot(),console.log(e),e.nextButton.addEventListener("click",()=>{e.element.setAttribute("style","display: none;"),this.steps=new Map(Array.from(this.steps.entries()).slice(1)),e.callback&&e.callback(),this.handleMethodSelect(t)})}submitForm(){this.submitting=!0,document.getElementById("payment-form").submit()}handle(){document.querySelectorAll(".dropdown-gateway-button").forEach(t=>{t.addEventListener("click",()=>{this.submitting||this.handleMethodSelect(t)})})}}const i=document.querySelector('meta[name="require-invoice-signature"]').content,o=document.querySelector('meta[name="show-invoice-terms"]').content,l=document.querySelector('meta[name="show-required-fields-form"]').content;new s(!!+o,!!+i,!!+l).handle();
|
@ -23,7 +23,7 @@
|
||||
"src": "resources/js/clients/invoices/action-selectors.js"
|
||||
},
|
||||
"resources/js/clients/invoices/payment.js": {
|
||||
"file": "assets/payment-0ace5bfa.js",
|
||||
"file": "assets/payment-1bdbd169.js",
|
||||
"isEntry": true,
|
||||
"src": "resources/js/clients/invoices/payment.js"
|
||||
},
|
||||
|
141
resources/js/clients/invoices/payment.js
vendored
141
resources/js/clients/invoices/payment.js
vendored
@ -9,11 +9,60 @@
|
||||
*/
|
||||
|
||||
class Payment {
|
||||
constructor(displayTerms, displaySignature) {
|
||||
constructor(displayTerms, displaySignature, displayRff) {
|
||||
this.shouldDisplayTerms = displayTerms;
|
||||
this.shouldDisplaySignature = displaySignature;
|
||||
this.termsAccepted = false;
|
||||
this.shouldDisplayRff = displayRff;
|
||||
|
||||
this.submitting = false;
|
||||
this.steps = new Map()
|
||||
|
||||
if (this.shouldDisplayRff) {
|
||||
this.steps.set("rff", {
|
||||
element: document.getElementById('displayRequiredFieldsModal'),
|
||||
nextButton: document.getElementById('rff-next-step'),
|
||||
callback: () => {
|
||||
const fields = {
|
||||
firstName: document.querySelector('input[name="rff_first_name"]'),
|
||||
lastName: document.querySelector('input[name="rff_last_name"]'),
|
||||
email: document.querySelector('input[name="rff_email"]'),
|
||||
}
|
||||
|
||||
if (fields.firstName) {
|
||||
document.querySelector('input[name="contact_first_name"]').value = fields.firstName.value;
|
||||
}
|
||||
|
||||
if (fields.lastName) {
|
||||
document.querySelector('input[name="contact_last_name"]').value = fields.lastName.value;
|
||||
}
|
||||
|
||||
if (fields.email) {
|
||||
document.querySelector('input[name="contact_email"]').value = fields.email.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.shouldDisplaySignature) {
|
||||
this.steps.set("signature", {
|
||||
element: document.getElementById('displaySignatureModal'),
|
||||
nextButton: document.getElementById('signature-next-step'),
|
||||
boot: () => this.signaturePad = new SignaturePad(
|
||||
document.getElementById("signature-pad"),
|
||||
{
|
||||
penColor: "rgb(0, 0, 0)"
|
||||
}
|
||||
),
|
||||
callback: () => document.querySelector('input[name="signature"').value = this.signaturePad.toDataURL(),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.shouldDisplayTerms) {
|
||||
this.steps.set("terms", {
|
||||
element: document.getElementById('displayTermsModal'),
|
||||
nextButton: document.getElementById('accept-terms-button'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleMethodSelect(element) {
|
||||
@ -22,54 +71,32 @@ class Payment {
|
||||
element.dataset.companyGatewayId;
|
||||
document.getElementById("payment_method_id").value =
|
||||
element.dataset.gatewayTypeId;
|
||||
|
||||
if (this.shouldDisplaySignature && !this.shouldDisplayTerms) {
|
||||
|
||||
if(this.signaturePad && this.signaturePad.isEmpty())
|
||||
alert("Please sign");
|
||||
|
||||
this.displayTerms();
|
||||
|
||||
document
|
||||
.getElementById("accept-terms-button")
|
||||
.addEventListener("click", () => {
|
||||
this.termsAccepted = true;
|
||||
this.submitForm();
|
||||
});
|
||||
|
||||
if (this.steps.size === 0) {
|
||||
return this.submitForm();
|
||||
}
|
||||
|
||||
if (!this.shouldDisplaySignature && this.shouldDisplayTerms) {
|
||||
this.displaySignature();
|
||||
const next = this.steps.values().next().value;
|
||||
|
||||
document
|
||||
.getElementById("signature-next-step")
|
||||
.addEventListener("click", () => {
|
||||
document.querySelector('input[name="signature"').value = this.signaturePad.toDataURL();
|
||||
this.submitForm();
|
||||
});
|
||||
next.element.removeAttribute("style");
|
||||
|
||||
if (next.boot) {
|
||||
next.boot();
|
||||
}
|
||||
|
||||
if (this.shouldDisplaySignature && this.shouldDisplayTerms) {
|
||||
this.displaySignature();
|
||||
console.log(next);
|
||||
|
||||
document
|
||||
.getElementById("signature-next-step")
|
||||
.addEventListener("click", () => {
|
||||
this.displayTerms();
|
||||
next.nextButton.addEventListener('click', () => {
|
||||
next.element.setAttribute("style", "display: none;");
|
||||
|
||||
document
|
||||
.getElementById("accept-terms-button")
|
||||
.addEventListener("click", () => {
|
||||
document.querySelector('input[name="signature"').value = this.signaturePad.toDataURL();
|
||||
this.termsAccepted = true;
|
||||
this.submitForm();
|
||||
});
|
||||
});
|
||||
}
|
||||
this.steps = new Map(Array.from(this.steps.entries()).slice(1));
|
||||
|
||||
if (!this.shouldDisplaySignature && !this.shouldDisplayTerms) {
|
||||
this.submitForm();
|
||||
}
|
||||
if (next.callback) {
|
||||
next.callback();
|
||||
}
|
||||
|
||||
this.handleMethodSelect(element);
|
||||
});
|
||||
}
|
||||
|
||||
submitForm() {
|
||||
@ -78,33 +105,6 @@ class Payment {
|
||||
document.getElementById("payment-form").submit();
|
||||
}
|
||||
|
||||
displayTerms() {
|
||||
let displayTermsModal = document.getElementById("displayTermsModal");
|
||||
displayTermsModal.removeAttribute("style");
|
||||
}
|
||||
|
||||
displaySignature() {
|
||||
document.getElementById("signature-next-step").disabled = true;
|
||||
|
||||
let displaySignatureModal = document.getElementById(
|
||||
"displaySignatureModal"
|
||||
);
|
||||
displaySignatureModal.removeAttribute("style");
|
||||
|
||||
const signaturePad = new SignaturePad(
|
||||
document.getElementById("signature-pad"),
|
||||
{
|
||||
penColor: "rgb(0, 0, 0)"
|
||||
}
|
||||
);
|
||||
|
||||
signaturePad.onEnd = function(){
|
||||
document.getElementById("signature-next-step").disabled = false;
|
||||
};
|
||||
|
||||
this.signaturePad = signaturePad;
|
||||
}
|
||||
|
||||
handle() {
|
||||
|
||||
document
|
||||
@ -124,5 +124,6 @@ const signature = document.querySelector(
|
||||
).content;
|
||||
|
||||
const terms = document.querySelector('meta[name="show-invoice-terms"]').content;
|
||||
const rff = document.querySelector('meta[name="show-required-fields-form"]').content;
|
||||
|
||||
new Payment(Boolean(+signature), Boolean(+terms)).handle();
|
||||
new Payment(Boolean(+terms), Boolean(+signature), Boolean(+rff)).handle();
|
||||
|
@ -0,0 +1,104 @@
|
||||
<div
|
||||
style="display: none"
|
||||
id="displayRequiredFieldsModal"
|
||||
class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center"
|
||||
x-data="{ open: true }"
|
||||
>
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
class="fixed inset-0 transition-opacity"
|
||||
>
|
||||
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
class="bg-white rounded-lg px-4 pt-5 pb-4 overflow-hidden shadow-xl transform transition-all sm:max-w-lg sm:w-full sm:p-6"
|
||||
>
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div
|
||||
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10"
|
||||
>
|
||||
<svg
|
||||
class="h-6 w-6 text-red-600"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.details') }}
|
||||
</h3>
|
||||
<div class="mt-2">
|
||||
@if(strlen(auth()->guard('contact')->user()->first_name) === 0)
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="first_name" class="input-label">{{ ctrans('texts.first_name') }}</label>
|
||||
<input id="first_name" class="input w-full" name="rff_first_name" value="{{ auth()->guard('contact')->user()->first_name }}" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen(auth()->guard('contact')->user()->last_name) === 0)
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="last_name" class="input-label">{{ ctrans('texts.last_name') }}</label>
|
||||
<input id="last_name" class="input w-full" name="rff_last_name" value="{{ auth()->guard('contact')->user()->last_name }}"/>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(strlen(auth()->guard('contact')->user()->email) === 0)
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="email" class="input-label">{{ ctrans('texts.email') }}</label>
|
||||
<input id="email" class="input w-full" name="rff_email" value="{{ auth()->guard('contact')->user()->email }}"/>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<div
|
||||
class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto"
|
||||
x-data
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
id="rff-next-step"
|
||||
class="button button-primary bg-primary"
|
||||
>
|
||||
{{ ctrans('texts.next_step') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto"
|
||||
x-data
|
||||
>
|
||||
<button
|
||||
@click="document.getElementById('displayRequiredFieldsModal').style.display = 'none';"
|
||||
type="button"
|
||||
class="button button-secondary"
|
||||
id="close-button"
|
||||
>
|
||||
{{ ctrans('texts.close') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -4,6 +4,7 @@
|
||||
@push('head')
|
||||
<meta name="show-invoice-terms" content="{{ $settings->show_accept_invoice_terms ? true : false }}">
|
||||
<meta name="require-invoice-signature" content="{{ $client->user->account->hasFeature(\App\Models\Account::FEATURE_INVOICE_SETTINGS) && $settings->require_invoice_signature }}">
|
||||
<meta name="show-required-fields-form" content="{{ auth()->guard('contact')->user()->showRff() }}" />
|
||||
<script src="{{ asset('vendor/signature_pad@2.3.2/signature_pad.min.js') }}"></script>
|
||||
@endpush
|
||||
|
||||
@ -17,6 +18,9 @@
|
||||
<input type="hidden" name="is_recurring" value="{{ isset($is_recurring) ? $is_recurring : false }}">
|
||||
<input type="hidden" name="frequency_id" value="{{ isset($frequency_id) ? $frequency_id : false }}">
|
||||
<input type="hidden" name="remaining_cycles" value="{{ isset($remaining_cycles) ? $remaining_cycles : false }}">
|
||||
<input type="hidden" name="contact_first_name" value="{{ auth()->guard('contact')->user()->first_name }}">
|
||||
<input type="hidden" name="contact_last_name" value="{{ auth()->guard('contact')->user()->last_name }}">
|
||||
<input type="hidden" name="contact_email" value="{{ auth()->guard('contact')->user()->email }}">
|
||||
|
||||
<div class="container mx-auto">
|
||||
<div class="grid grid-cols-6 gap-4">
|
||||
@ -150,6 +154,7 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@include('portal.ninja2020.invoices.includes.required-fields')
|
||||
@include('portal.ninja2020.invoices.includes.terms', ['entities' => $invoices, 'variables' => $variables, 'entity_type' => ctrans('texts.invoice')])
|
||||
@include('portal.ninja2020.invoices.includes.signature')
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user