1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 12:12:48 +01:00

Paypal refactor

This commit is contained in:
David Bomba 2024-07-27 08:48:51 +10:00
parent 78fc17b1cb
commit 4008b24acd
11 changed files with 185 additions and 141 deletions

View File

@ -159,6 +159,11 @@ class CompanyGateway extends BaseModel
protected $touches = [];
public function isPayPal()
{
return in_array($this->gateway_key, ['80af24a6a691230bbec33e930ab40666','80af24a6a691230bbec33e930ab40665']);
}
public function getEntityType()
{
return self::class;

View File

@ -192,6 +192,7 @@ class PaymentMethod
'label' => ctrans('texts.apply_credit'),
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
'gateway_type_id' => GatewayType::CREDIT,
'is_paypal' => $gateway->isPayPal(),
];
}
@ -210,12 +211,14 @@ class PaymentMethod
'label' => $gateway->getConfigField('name').$fee_label,
'company_gateway_id' => $gateway->id,
'gateway_type_id' => GatewayType::CREDIT_CARD,
'is_paypal' => $gateway->isPayPal(),
];
} else {
$this->payment_urls[] = [
'label' => $gateway->getTypeAlias($type).$fee_label,
'company_gateway_id' => $gateway->id,
'gateway_type_id' => $type,
'is_paypal' => $gateway->isPayPal(),
];
}
@ -236,12 +239,14 @@ class PaymentMethod
'label' => $gateway->getConfigField('name').$fee_label,
'company_gateway_id' => $gateway_id,
'gateway_type_id' => GatewayType::CREDIT_CARD,
'is_paypal' => $gateway->isPayPal(),
];
} else {
$this->payment_urls[] = [
'label' => $gateway->getTypeAlias($gateway_type_id).$fee_label,
'company_gateway_id' => $gateway_id,
'gateway_type_id' => $gateway_type_id,
'is_paypal' => $gateway->isPayPal(),
];
}
}
@ -259,6 +264,7 @@ class PaymentMethod
'label' => ctrans('texts.apply_credit'),
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
'gateway_type_id' => GatewayType::CREDIT,
'is_paypal' => $gateway->isPayPal(),
];
}

View File

@ -42,6 +42,9 @@ class AutoBillInvoice extends AbstractService
public function __construct(private Invoice $invoice, protected string $db)
{
$this->client = $this->invoice->client;
}
public function run()
@ -49,8 +52,7 @@ class AutoBillInvoice extends AbstractService
MultiDB::setDb($this->db);
/* @var \App\Modesl\Client $client */
$this->client = $this->invoice->client;
$is_partial = false;
/* Is the invoice payable? */
@ -272,7 +274,7 @@ class AutoBillInvoice extends AbstractService
*
* @return self
*/
private function applyUnappliedPayment(): self
public function applyUnappliedPayment(): self
{
$unapplied_payments = Payment::query()
->where('client_id', $this->client->id)
@ -284,6 +286,11 @@ class AutoBillInvoice extends AbstractService
->get();
$available_unapplied_balance = $unapplied_payments->sum('amount') - $unapplied_payments->sum('applied');
nlog($this->client->id);
nlog($this->invoice->id);
nlog($unapplied_payments->sum('amount'));
nlog($unapplied_payments->sum('applied'));
nlog("available unapplied balance = {$available_unapplied_balance}");
@ -347,7 +354,7 @@ class AutoBillInvoice extends AbstractService
*
* @return $this
*/
private function applyCreditPayment(): self
public function applyCreditPayment(): self
{
$available_credits = Credit::query()->where('client_id', $this->client->id)
->where('is_deleted', false)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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(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();

View File

@ -9,7 +9,7 @@
]
},
"resources/js/app.js": {
"file": "assets/app-234e3402.js",
"file": "assets/app-e0713224.js",
"imports": [
"_index-08e160a7.js",
"__commonjsHelpers-725317a4.js"
@ -23,7 +23,7 @@
"src": "resources/js/clients/invoices/action-selectors.js"
},
"resources/js/clients/invoices/payment.js": {
"file": "assets/payment-1bdbd169.js",
"file": "assets/payment-357f3929.js",
"isEntry": true,
"src": "resources/js/clients/invoices/payment.js"
},
@ -240,7 +240,7 @@
"src": "resources/js/setup/setup.js"
},
"resources/sass/app.scss": {
"file": "assets/app-f3b33400.css",
"file": "assets/app-906435d5.css",
"isEntry": true,
"src": "resources/sass/app.scss"
}

View File

@ -18,6 +18,7 @@ class Payment {
this.steps = new Map()
if (this.shouldDisplayRff) {
this.steps.set("rff", {
element: document.getElementById('displayRequiredFieldsModal'),
nextButton: document.getElementById('rff-next-step'),
@ -39,6 +40,7 @@ class Payment {
if (fields.email) {
document.querySelector('input[name="contact_email"]').value = fields.email.value;
}
}
});
}
@ -71,7 +73,20 @@ class Payment {
element.dataset.companyGatewayId;
document.getElementById("payment_method_id").value =
element.dataset.gatewayTypeId;
if (element.dataset.isPaypal == '1') {
var rff_city = document.getElementById("rff_city");
var rff_postal_code = document.getElementById("rff_postal_code");
if (rff_city)
rff_city.classList.remove('hidden');
if (rff_postal_code)
rff_postal_code.classList.remove('hidden');
}
if (this.steps.size === 0) {
return this.submitForm();
}

View File

@ -23,6 +23,7 @@
<a href="#" @click="open = false" dusk="pay-with-custom"
data-company-gateway-id="{{ $method['company_gateway_id'] }}"
data-gateway-type-id="{{ $method['gateway_type_id'] }}"
data-is-paypal="{{ $method['is_paypal'] }}"
class="block px-4 py-2 text-sm leading-5 text-gray-700 dropdown-gateway-button hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
dusk="payment-method">
{{ \App\Models\CompanyGateway::find($method['company_gateway_id'])->firstOrFail()->getConfigField('name') }}
@ -31,6 +32,7 @@
<a href="#" @click="open = false" dusk="pay-with-{{ $index }}"
data-company-gateway-id="{{ $method['company_gateway_id'] }}"
data-gateway-type-id="{{ $method['gateway_type_id'] }}"
data-is-paypal="{{ $method['is_paypal'] }}"
class="block px-4 py-2 text-sm leading-5 text-gray-700 dropdown-gateway-button hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
dusk="payment-method">
{{ $method['label'] }}

View File

@ -2,7 +2,7 @@
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 }"
x-data="formValidation()"
>
<div
x-show="open"
@ -45,56 +45,120 @@
/>
</svg>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<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 }}" />
<label for="rff_first_name" class="input-label">{{ ctrans('texts.first_name') }}</label>
<input
id="rff_first_name"
class="input w-full"
name="rff_first_name"
value="{{ auth()->guard('contact')->user()->first_name }}"
x-model="rff_first_name"
@blur="validateFirstName()"
:class="{ 'border-red-500': errors.rff_first_name }"
/>
<span x-show="errors.rff_first_name" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_first_name"></span>
</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 }}"/>
<label for="rff_last_name" class="input-label">{{ ctrans('texts.last_name') }}</label>
<input
id="rff_last_name"
class="input w-full"
name="rff_last_name"
x-model="rff_last_name"
@blur="validateLastName()"
:class="{ 'border-red-500': errors.rff_last_name }"
/>
<span x-show="errors.rff_last_name" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_last_name"></span>
</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 }}"/>
<input
id="rff_email"
class="input w-full"
name="rff_email"
x-model="rff_email"
@blur="validateEmail()"
:class="{ 'border-red-500': errors.rff_email }"
/>
<span x-show="errors.rff_email" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_email"></span>
</div>
@endif
@if(strlen(auth()->guard('contact')->user()->client->city) === 0)
<div class="col-span-6 sm:col-span-3 hidden" id="rff_city">
<label for="city" class="input-label">{{ ctrans('texts.city') }}</label>
<input
id="rff_city"
class="input w-full"
name="rff_city"
x-model="rff_city"
@blur="validateCity()"
:class="{ 'border-red-500': errors.rff_city }"
/>
<span x-show="errors.rff_city" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_city"></span>
</div>
@endif
@if(strlen(auth()->guard('contact')->user()->client->postal_code) === 0)
<div class="col-span-6 sm:col-span-3 hidden" id="rff_postal_code">
<label for="postal_code" class="input-label">{{ ctrans('texts.postal_code') }}</label>
<input
id="rff_postal_code"
class="input w-full"
name="rff_postal_code"
x-model="rff_postal_code"
@blur="validatePostalCode()"
:class="{ 'border-red-500': errors.rff_postal_code }"
/>
<span x-show="errors.rff_postal_code" class="validation validation-fail block w-full" role="alert" x-text="errors.rff_postal_code"></span>
</div>
@endif
</div>
</div>
</div>
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<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
>
class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
<button
type="button"
id="rff-next-step"
@@click="validateForm"
class="button button-primary bg-primary"
>
{{ ctrans('texts.next_step') }}
</button>
<button
type="button"
id="rff-next-step"
class="hidden">
</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"
@click="document.getElementById('displayRequiredFieldsModal').style.display = 'none';"
>
{{ ctrans('texts.close') }}
</button>
@ -102,3 +166,64 @@
</div>
</div>
</div>
<script>
function formValidation() {
return {
open: true,
rff_last_name: '{{ auth()->guard('contact')->user()->last_name }}',
rff_first_name: '{{ auth()->guard('contact')->user()->first_name }}',
rff_email: '{{ auth()->guard('contact')->user()->email }}',
rff_city: '{{ auth()->guard('contact')->user()->client->city }}',
rff_postal_code: '{{ auth()->guard('contact')->user()->client->postal_code }}',
errors: {
rff_first_name: '',
rff_last_name: '',
rff_city: '',
rff_postal_code: '',
rff_email: ''
},
validateFirstName() {
this.errors.rff_first_name = this.rff_first_name.trim() === '' ? '{{ ctrans('texts.first_name') }}' + ' ' + '{{ ctrans('texts.required') }}' : '';
},
validateLastName() {
this.errors.rff_last_name = this.rff_last_name.trim() === '' ? '{{ ctrans('texts.last_name') }}' + ' ' + '{{ ctrans('texts.required') }}' : '';
},
validateEmail() {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
this.errors.rff_email = !emailPattern.test(this.rff_email.trim()) ? '{{ ctrans('texts.provide_email') }}' : '';
},
validatePostalCode() {
this.errors.rff_postal_code = this.rff_postal_code.trim() === '' ? '{{ ctrans('texts.postal_code') }}' + ' ' + '{{ ctrans('texts.required') }}' : '';
},
validateCity() {
this.errors.rff_city = this.rff_city.trim() === '' ? '{{ ctrans('texts.city') }}' + ' ' + '{{ ctrans('texts.required') }}' : '';
},
validateForm() {
this.validateFirstName();
this.validateLastName();
this.validateEmail();
this.validateCity();
this.validatePostalCode();
if (!this.errors.rff_first_name && !this.errors.rff_last_name) {
const next_rff = document.getElementById('rff-next-step');
next_rff.click();
}
},
}
}
</script>

View File

@ -22,6 +22,9 @@
<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 }}">
<input type="hidden" name="client_city" value="{{ auth()->guard('contact')->user()->client->city }}">
<input type="hidden" name="client_postal_code" value="{{ auth()->guard('contact')->user()->client->postal_code }}">
<div class="container mx-auto">
<div class="grid grid-cols-6 gap-4">
<div class="col-span-6 md:col-start-2 md:col-span-4">