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 = []; protected $touches = [];
public function isPayPal()
{
return in_array($this->gateway_key, ['80af24a6a691230bbec33e930ab40666','80af24a6a691230bbec33e930ab40665']);
}
public function getEntityType() public function getEntityType()
{ {
return self::class; return self::class;

View File

@ -192,6 +192,7 @@ class PaymentMethod
'label' => ctrans('texts.apply_credit'), 'label' => ctrans('texts.apply_credit'),
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT, 'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
'gateway_type_id' => GatewayType::CREDIT, 'gateway_type_id' => GatewayType::CREDIT,
'is_paypal' => $gateway->isPayPal(),
]; ];
} }
@ -210,12 +211,14 @@ class PaymentMethod
'label' => $gateway->getConfigField('name').$fee_label, 'label' => $gateway->getConfigField('name').$fee_label,
'company_gateway_id' => $gateway->id, 'company_gateway_id' => $gateway->id,
'gateway_type_id' => GatewayType::CREDIT_CARD, 'gateway_type_id' => GatewayType::CREDIT_CARD,
'is_paypal' => $gateway->isPayPal(),
]; ];
} else { } else {
$this->payment_urls[] = [ $this->payment_urls[] = [
'label' => $gateway->getTypeAlias($type).$fee_label, 'label' => $gateway->getTypeAlias($type).$fee_label,
'company_gateway_id' => $gateway->id, 'company_gateway_id' => $gateway->id,
'gateway_type_id' => $type, 'gateway_type_id' => $type,
'is_paypal' => $gateway->isPayPal(),
]; ];
} }
@ -236,12 +239,14 @@ class PaymentMethod
'label' => $gateway->getConfigField('name').$fee_label, 'label' => $gateway->getConfigField('name').$fee_label,
'company_gateway_id' => $gateway_id, 'company_gateway_id' => $gateway_id,
'gateway_type_id' => GatewayType::CREDIT_CARD, 'gateway_type_id' => GatewayType::CREDIT_CARD,
'is_paypal' => $gateway->isPayPal(),
]; ];
} else { } else {
$this->payment_urls[] = [ $this->payment_urls[] = [
'label' => $gateway->getTypeAlias($gateway_type_id).$fee_label, 'label' => $gateway->getTypeAlias($gateway_type_id).$fee_label,
'company_gateway_id' => $gateway_id, 'company_gateway_id' => $gateway_id,
'gateway_type_id' => $gateway_type_id, 'gateway_type_id' => $gateway_type_id,
'is_paypal' => $gateway->isPayPal(),
]; ];
} }
} }
@ -259,6 +264,7 @@ class PaymentMethod
'label' => ctrans('texts.apply_credit'), 'label' => ctrans('texts.apply_credit'),
'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT, 'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT,
'gateway_type_id' => GatewayType::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) public function __construct(private Invoice $invoice, protected string $db)
{ {
$this->client = $this->invoice->client;
} }
public function run() public function run()
@ -49,7 +52,6 @@ class AutoBillInvoice extends AbstractService
MultiDB::setDb($this->db); MultiDB::setDb($this->db);
/* @var \App\Modesl\Client $client */ /* @var \App\Modesl\Client $client */
$this->client = $this->invoice->client;
$is_partial = false; $is_partial = false;
@ -272,7 +274,7 @@ class AutoBillInvoice extends AbstractService
* *
* @return self * @return self
*/ */
private function applyUnappliedPayment(): self public function applyUnappliedPayment(): self
{ {
$unapplied_payments = Payment::query() $unapplied_payments = Payment::query()
->where('client_id', $this->client->id) ->where('client_id', $this->client->id)
@ -285,6 +287,11 @@ class AutoBillInvoice extends AbstractService
$available_unapplied_balance = $unapplied_payments->sum('amount') - $unapplied_payments->sum('applied'); $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}"); nlog("available unapplied balance = {$available_unapplied_balance}");
if ((int) $available_unapplied_balance == 0) { if ((int) $available_unapplied_balance == 0) {
@ -347,7 +354,7 @@ class AutoBillInvoice extends AbstractService
* *
* @return $this * @return $this
*/ */
private function applyCreditPayment(): self public function applyCreditPayment(): self
{ {
$available_credits = Credit::query()->where('client_id', $this->client->id) $available_credits = Credit::query()->where('client_id', $this->client->id)
->where('is_deleted', false) ->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": { "resources/js/app.js": {
"file": "assets/app-234e3402.js", "file": "assets/app-e0713224.js",
"imports": [ "imports": [
"_index-08e160a7.js", "_index-08e160a7.js",
"__commonjsHelpers-725317a4.js" "__commonjsHelpers-725317a4.js"
@ -23,7 +23,7 @@
"src": "resources/js/clients/invoices/action-selectors.js" "src": "resources/js/clients/invoices/action-selectors.js"
}, },
"resources/js/clients/invoices/payment.js": { "resources/js/clients/invoices/payment.js": {
"file": "assets/payment-1bdbd169.js", "file": "assets/payment-357f3929.js",
"isEntry": true, "isEntry": true,
"src": "resources/js/clients/invoices/payment.js" "src": "resources/js/clients/invoices/payment.js"
}, },
@ -240,7 +240,7 @@
"src": "resources/js/setup/setup.js" "src": "resources/js/setup/setup.js"
}, },
"resources/sass/app.scss": { "resources/sass/app.scss": {
"file": "assets/app-f3b33400.css", "file": "assets/app-906435d5.css",
"isEntry": true, "isEntry": true,
"src": "resources/sass/app.scss" "src": "resources/sass/app.scss"
} }

View File

@ -18,6 +18,7 @@ class Payment {
this.steps = new Map() this.steps = new Map()
if (this.shouldDisplayRff) { if (this.shouldDisplayRff) {
this.steps.set("rff", { this.steps.set("rff", {
element: document.getElementById('displayRequiredFieldsModal'), element: document.getElementById('displayRequiredFieldsModal'),
nextButton: document.getElementById('rff-next-step'), nextButton: document.getElementById('rff-next-step'),
@ -39,6 +40,7 @@ class Payment {
if (fields.email) { if (fields.email) {
document.querySelector('input[name="contact_email"]').value = fields.email.value; document.querySelector('input[name="contact_email"]').value = fields.email.value;
} }
} }
}); });
} }
@ -72,6 +74,19 @@ class Payment {
document.getElementById("payment_method_id").value = document.getElementById("payment_method_id").value =
element.dataset.gatewayTypeId; 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) { if (this.steps.size === 0) {
return this.submitForm(); return this.submitForm();
} }

View File

@ -23,6 +23,7 @@
<a href="#" @click="open = false" dusk="pay-with-custom" <a href="#" @click="open = false" dusk="pay-with-custom"
data-company-gateway-id="{{ $method['company_gateway_id'] }}" data-company-gateway-id="{{ $method['company_gateway_id'] }}"
data-gateway-type-id="{{ $method['gateway_type_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" 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"> dusk="payment-method">
{{ \App\Models\CompanyGateway::find($method['company_gateway_id'])->firstOrFail()->getConfigField('name') }} {{ \App\Models\CompanyGateway::find($method['company_gateway_id'])->firstOrFail()->getConfigField('name') }}
@ -31,6 +32,7 @@
<a href="#" @click="open = false" dusk="pay-with-{{ $index }}" <a href="#" @click="open = false" dusk="pay-with-{{ $index }}"
data-company-gateway-id="{{ $method['company_gateway_id'] }}" data-company-gateway-id="{{ $method['company_gateway_id'] }}"
data-gateway-type-id="{{ $method['gateway_type_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" 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"> dusk="payment-method">
{{ $method['label'] }} {{ $method['label'] }}

View File

@ -2,7 +2,7 @@
style="display: none" style="display: none"
id="displayRequiredFieldsModal" id="displayRequiredFieldsModal"
class="fixed bottom-0 inset-x-0 px-4 pb-4 sm:inset-0 sm:flex sm:items-center sm:justify-center" 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 <div
x-show="open" x-show="open"
@ -52,49 +52,113 @@
<div class="mt-2"> <div class="mt-2">
@if(strlen(auth()->guard('contact')->user()->first_name) === 0) @if(strlen(auth()->guard('contact')->user()->first_name) === 0)
<div class="col-span-6 sm:col-span-3"> <div class="col-span-6 sm:col-span-3">
<label for="first_name" class="input-label">{{ ctrans('texts.first_name') }}</label> <label for="rff_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 }}" /> <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> </div>
@endif @endif
@if(strlen(auth()->guard('contact')->user()->last_name) === 0) @if(strlen(auth()->guard('contact')->user()->last_name) === 0)
<div class="col-span-6 sm:col-span-3"> <div class="col-span-6 sm:col-span-3">
<label for="last_name" class="input-label">{{ ctrans('texts.last_name') }}</label> <label for="rff_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 }}"/> <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> </div>
@endif @endif
@if(strlen(auth()->guard('contact')->user()->email) === 0) @if(strlen(auth()->guard('contact')->user()->email) === 0)
<div class="col-span-6 sm:col-span-3"> <div class="col-span-6 sm:col-span-3">
<label for="email" class="input-label">{{ ctrans('texts.email') }}</label> <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> </div>
@endif @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>
</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 <div
class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto" class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
x-data
>
<button <button
type="button" type="button"
id="rff-next-step" @@click="validateForm"
class="button button-primary bg-primary" class="button button-primary bg-primary"
> >
{{ ctrans('texts.next_step') }} {{ ctrans('texts.next_step') }}
</button> </button>
<button
type="button"
id="rff-next-step"
class="hidden">
</button>
</div> </div>
<div <div
class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto" class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto"
x-data
> >
<button <button
@click="document.getElementById('displayRequiredFieldsModal').style.display = 'none';"
type="button" type="button"
class="button button-secondary" class="button button-secondary"
id="close-button" id="close-button"
@click="document.getElementById('displayRequiredFieldsModal').style.display = 'none';"
> >
{{ ctrans('texts.close') }} {{ ctrans('texts.close') }}
</button> </button>
@ -102,3 +166,64 @@
</div> </div>
</div> </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_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="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="container mx-auto">
<div class="grid grid-cols-6 gap-4"> <div class="grid grid-cols-6 gap-4">
<div class="col-span-6 md:col-start-2 md:col-span-4"> <div class="col-span-6 md:col-start-2 md:col-span-4">