diff --git a/app/Http/Controllers/ClientPortal/DashboardController.php b/app/Http/Controllers/ClientPortal/DashboardController.php index 19ff8f57aa1c..1d2b4f04c987 100644 --- a/app/Http/Controllers/ClientPortal/DashboardController.php +++ b/app/Http/Controllers/ClientPortal/DashboardController.php @@ -21,6 +21,6 @@ class DashboardController extends Controller */ public function index() { - return $this->render('dashboard.index'); + return redirect()->route('client.invoices.index'); } } diff --git a/app/Http/Controllers/ClientPortal/PaymentMethodController.php b/app/Http/Controllers/ClientPortal/PaymentMethodController.php index 13556fdc5dcb..e57cc00007a2 100644 --- a/app/Http/Controllers/ClientPortal/PaymentMethodController.php +++ b/app/Http/Controllers/ClientPortal/PaymentMethodController.php @@ -44,8 +44,12 @@ class PaymentMethodController extends Controller { $gateway = auth()->user()->client->getCreditCardGateway(); - return $gateway->driver(auth()->user()->client)->authorizeView(GatewayType::CREDIT_CARD); + $data['gateway'] = $gateway; + return $gateway + ->driver(auth()->user()->client) + ->setPaymentMethod(GatewayType::BANK_TRANSFER) + ->authorizeView($data); } /** @@ -57,9 +61,11 @@ class PaymentMethodController extends Controller public function store(Request $request) { $gateway = auth()->user()->client->getCreditCardGateway(); - - return $gateway->driver(auth()->user()->client)->authorizeResponseView($request->all()); + return $gateway + ->driver(auth()->user()->client) + ->setPaymentMethod(GatewayType::BANK_TRANSFER) + ->authorizeResponse($request); } /** @@ -104,7 +110,7 @@ class PaymentMethodController extends Controller return $gateway ->driver(auth()->user()->client) - ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH') + ->setPaymentMethod(GatewayType::BANK_TRANSFER) ->verificationView($payment_method); } @@ -114,7 +120,7 @@ class PaymentMethodController extends Controller return $gateway ->driver(auth()->user()->client) - ->setPaymentMethod('App\\PaymentDrivers\\Stripe\\ACH') + ->setPaymentMethod(GatewayType::BANK_TRANSFER) ->processVerification($payment_method); } diff --git a/app/Http/Controllers/Traits/VerifiesUserEmail.php b/app/Http/Controllers/Traits/VerifiesUserEmail.php index 889d4dea5c55..5e34fc587e5d 100644 --- a/app/Http/Controllers/Traits/VerifiesUserEmail.php +++ b/app/Http/Controllers/Traits/VerifiesUserEmail.php @@ -15,6 +15,7 @@ namespace App\Http\Controllers\Traits; use App\Models\User; use App\Utils\Traits\UserSessionAttributes; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Hash; /** * Class VerifiesUserEmail @@ -30,20 +31,49 @@ trait VerifiesUserEmail */ public function confirm() { - if ($user = User::whereRaw("BINARY `confirmation_code`= ?", request()->route('confirmation_code'))->first()) { - $user->email_verified_at = now(); - $user->confirmation_code = null; - $user->save(); + $user = User::where('confirmation_code', request()->confirmation_code)->first(); + + // if ($user = User::whereRaw("BINARY `confirmation_code`= ?", request()->input('confirmation_code'))->first()) { - return $this->render('auth.confirmed', [ - 'root' => 'themes', - 'message' => ctrans('texts.security_confirmation'), - ]); + if (!$user) { + return $this->render('auth.confirmed', ['root' => 'themes', 'message' => ctrans('texts.wrong_confirmation')]); } + if (is_null($user->password) || empty($user->password)) { + return $this->render('auth.confirmation_with_password', ['root' => 'themes']); + } + + $user->email_verified_at = now(); + $user->confirmation_code = null; + $user->save(); + return $this->render('auth.confirmed', [ 'root' => 'themes', - 'message' => ctrans('texts.wrong_confirmation'), + 'message' => ctrans('texts.security_confirmation'), + ]); + } + + public function confirmWithPassword() + { + $user = User::where('confirmation_code', request()->confirmation_code)->first(); + + if (!$user) { + return $this->render('auth.confirmed', ['root' => 'themes', 'message' => ctrans('texts.wrong_confirmation')]); + } + + request()->validate([ + 'password' => ['required', 'min:6', 'confirmed'], + ]); + + $user->password = Hash::make(request()->password); + + $user->email_verified_at = now(); + $user->confirmation_code = null; + $user->save(); + + return $this->render('auth.confirmed', [ + 'root' => 'themes', + 'message' => ctrans('texts.security_confirmation'), ]); } } diff --git a/app/Http/ViewComposers/PortalComposer.php b/app/Http/ViewComposers/PortalComposer.php index bd5c63763238..cf7986c6d856 100644 --- a/app/Http/ViewComposers/PortalComposer.php +++ b/app/Http/ViewComposers/PortalComposer.php @@ -60,7 +60,7 @@ class PortalComposer { $data = []; - $data[] = [ 'title' => ctrans('texts.dashboard'), 'url' => 'client.dashboard', 'icon' => 'activity']; + // $data[] = [ 'title' => ctrans('texts.dashboard'), 'url' => 'client.dashboard', 'icon' => 'activity']; $data[] = [ 'title' => ctrans('texts.invoices'), 'url' => 'client.invoices.index', 'icon' => 'file-text']; $data[] = [ 'title' => ctrans('texts.recurring_invoices'), 'url' => 'client.recurring_invoices.index', 'icon' => 'file']; $data[] = [ 'title' => ctrans('texts.payments'), 'url' => 'client.payments.index', 'icon' => 'credit-card']; diff --git a/app/Models/Payment.php b/app/Models/Payment.php index 6e0ca307cdf2..36df31e76e8c 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -90,6 +90,11 @@ class Payment extends BaseModel return $this->belongsTo(Client::class)->withTrashed(); } + public function company_gateway() + { + return $this->belongsTo(CompanyGateway::class)->withTrashed(); + } + public function company() { return $this->belongsTo(Company::class); diff --git a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php index ded2837787fd..160790415bac 100644 --- a/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php +++ b/app/PaymentDrivers/Authorize/AuthorizeCreditCard.php @@ -120,6 +120,7 @@ class AuthorizeCreditCard $payment->client_id = $this->authorize->client->id; $payment->company_gateway_id = $this->authorize->company_gateway->id; $payment->status_id = Payment::STATUS_COMPLETED; + $payment->gateway_type_id = $this->authorize->payment_method; $payment->type_id = PaymentType::CREDIT_CARD_OTHER; $payment->currency_id = $this->authorize->client->getSetting('currency_id'); $payment->date = Carbon::now(); @@ -129,7 +130,6 @@ class AuthorizeCreditCard $payment->client->getNextPaymentNumber($this->authorize->client); $payment->save(); - $this->authorize->attachInvoices($payment, $request->hashed_ids); $payment->service()->updateInvoicePayment(); diff --git a/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php b/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php index 649009dcd7a6..96d072994941 100644 --- a/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php +++ b/app/PaymentDrivers/Authorize/AuthorizePaymentMethod.php @@ -64,11 +64,11 @@ class AuthorizePaymentMethod } - public function authorizeResponseView($payment_method, $data) + public function authorizeResponseView($data) { - $this->payment_method = $payment_method; + $this->payment_method = $data['payment_method_id']; - switch ($payment_method) { + switch ($this->payment_method) { case GatewayType::CREDIT_CARD: return $this->authorizeCreditCardResponse($data); break; diff --git a/app/PaymentDrivers/AuthorizePaymentDriver.php b/app/PaymentDrivers/AuthorizePaymentDriver.php index bc83570df46c..d9a60dd5cdb5 100644 --- a/app/PaymentDrivers/AuthorizePaymentDriver.php +++ b/app/PaymentDrivers/AuthorizePaymentDriver.php @@ -102,7 +102,7 @@ class AuthorizePaymentDriver extends BaseDriver public function authorizeResponseView(array $data) { - return (new AuthorizePaymentMethod($this))->authorizeResponseView($data['gateway_type_id'], $data); + return (new AuthorizePaymentMethod($this))->authorizeResponseView($data); } public function authorize($payment_method) diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index cff4ceda3e34..3e329e14ba1e 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -172,7 +172,7 @@ class StripePaymentDriver extends BasePaymentDriver * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ - public function authorizeCreditCardResponse($request) + public function authorizeResponse($request) { return $this->payment_method->authorizeResponse($request); } diff --git a/app/Services/Payment/RefundPayment.php b/app/Services/Payment/RefundPayment.php index 1c84507ce082..9f3b7644a3bc 100644 --- a/app/Services/Payment/RefundPayment.php +++ b/app/Services/Payment/RefundPayment.php @@ -44,8 +44,9 @@ class RefundPayment return $this->calculateTotalRefund() //sets amount for the refund (needed if we are refunding multiple invoices in one payment) ->setStatus() //sets status of payment - ->buildCreditNote() //generate the credit note - ->buildCreditLineItems() //generate the credit note items + //->reversePayment() + //->buildCreditNote() //generate the credit note + //->buildCreditLineItems() //generate the credit note items ->updateCreditables() //return the credits first ->updatePaymentables() //update the paymentable items ->adjustInvoices() @@ -53,19 +54,23 @@ class RefundPayment ->save(); } + /** + * Process the refund through the gateway + * + * @return $this + */ private function processGatewayRefund() { if ($this->refund_data['gateway_refund'] !== false && $this->total_refund > 0) { - $gateway = CompanyGateway::first(); - if ($gateway) { + if ($this->payment->company_gateway) { $response = $gateway->driver($this->payment->client)->refund($this->payment, $this->total_refund); if ($response['success']) { throw new PaymentRefundFailed(); } - $this->payment->refunded = $this->total_refund; + $this->payment->refunded += $this->total_refund; $this ->createActivity($gateway) @@ -78,20 +83,12 @@ class RefundPayment return $this; } - public function updateCreditNoteBalance() - { - $this->credit_note->balance -= $this->total_refund; - $this->credit_note->status_id = Credit::STATUS_APPLIED; - - $this->credit_note->balance === 0 - ? $this->credit_note->status_id = Credit::STATUS_APPLIED - : $this->credit_note->status_id = Credit::STATUS_PARTIAL; - - $this->credit_note->save(); - - return $this; - } - + /** + * Create the payment activity + * + * @param json $notes gateway_transaction + * @return $this + */ private function createActivity($notes) { $fields = new \stdClass; @@ -116,90 +113,43 @@ class RefundPayment return $this; } + /** + * Determine the amount of refund + * + * @return $this + */ private function calculateTotalRefund() { - if (array_key_exists('invoices', $this->refund_data) && count($this->refund_data['invoices']) > 0){ - info("array of invoice to refund"); + if (array_key_exists('invoices', $this->refund_data) && count($this->refund_data['invoices']) > 0) $this->total_refund = collect($this->refund_data['invoices'])->sum('amount'); - } - else{ - info("no invoices found - refunding total."); + else $this->total_refund = $this->refund_data['amount']; - } return $this; + } + /** + * Set the payment status + */ private function setStatus() { - if ($this->refund_data['amount'] == $this->payment->amount) { + + if ($this->refund_data['amount'] == $this->payment->amount) $this->payment->status_id = Payment::STATUS_REFUNDED; - } else { + else $this->payment->status_id = Payment::STATUS_PARTIALLY_REFUNDED; - } - - return $this; - } - - private function buildCreditNote() - { - $this->credit_note = CreditFactory::create($this->payment->company_id, $this->payment->user_id); - $this->credit_note->assigned_user_id = isset($this->payment->assigned_user_id) ?: null; - $this->credit_note->date = $this->refund_data['date']; - $this->credit_note->status_id = Credit::STATUS_SENT; - $this->credit_note->client_id = $this->payment->client->id; - $this->credit_note->amount = $this->total_refund; - $this->credit_note->balance = $this->total_refund; - - $this->credit_note->save(); - $this->credit_note->number = $this->payment->client->getNextCreditNumber($this->payment->client); - $this->credit_note->save(); - - return $this; - } - - private function buildCreditLineItems() - { - $ledger_string = ''; - - if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) { - foreach ($this->refund_data['invoices'] as $invoice) { - - $inv = Invoice::find($invoice['invoice_id']); - - $credit_line_item = InvoiceItemFactory::create(); - $credit_line_item->quantity = 1; - $credit_line_item->cost = $invoice['amount']; - $credit_line_item->product_key = ctrans('texts.invoice'); - $credit_line_item->notes = ctrans('texts.refund_body', ['amount' => $invoice['amount'], 'invoice_number' => $inv->number]); - $credit_line_item->line_total = $invoice['amount']; - $credit_line_item->date = $this->refund_data['date']; - - $ledger_string .= $credit_line_item->notes . ' '; - - $line_items[] = $credit_line_item; - } - } else { - - $credit_line_item = InvoiceItemFactory::create(); - $credit_line_item->quantity = 1; - $credit_line_item->cost = $this->refund_data['amount']; - $credit_line_item->product_key = ctrans('texts.credit'); - $credit_line_item->notes = ctrans('texts.credit_created_by', ['transaction_reference' => $this->payment->number]); - $credit_line_item->line_total = $this->refund_data['amount']; - $credit_line_item->date = $this->refund_data['date']; - - $line_items = []; - $line_items[] = $credit_line_item; - } - - $this->credit_note->line_items = $line_items; - $this->credit_note->save(); return $this; + } + /** + * Update the paymentable records + * + * @return $this + */ private function updatePaymentables() { if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) { @@ -218,6 +168,12 @@ class RefundPayment return $this; } + /** + * If credits have been bundled in this payment, we + * need to reverse these + * + * @return $this + */ private function updateCreditables() { @@ -229,9 +185,9 @@ class RefundPayment if ($available_credit > $this->total_refund) { $paymentable_credit->pivot->refunded += $this->total_refund; $paymentable_credit->pivot->save(); - $paymentable_credit->balance += $this->total_refund; - $paymentable_credit->save(); + $paymentable_credit->service()->setStatus(Credit::STATUS_SENT)->save(); + //$paymentable_credit->save(); $this->total_refund = 0; } else { @@ -239,7 +195,8 @@ class RefundPayment $paymentable_credit->pivot->save(); $paymentable_credit->balance += $available_credit; - $paymentable_credit->save(); + $paymentable_credit->service()->setStatus(Credit::STATUS_SENT)->save(); +// $paymentable_credit->save(); $this->total_refund -= $available_credit; } @@ -253,50 +210,137 @@ class RefundPayment return $this; } - + /** + * Reverse the payments made on invoices + * + * @return $this + */ private function adjustInvoices() { $adjustment_amount = 0; if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) { - foreach ($this->refund_data['invoices'] as $refunded_invoice) { + + foreach ($this->refund_data['invoices'] as $refunded_invoice) + { + $invoice = Invoice::find($refunded_invoice['invoice_id']); $invoice->service()->updateBalance($refunded_invoice['amount'])->save(); + $invoice->ledger()->updateInvoiceBalance($refunded_invoice['amount'], "Refund of payment # {$this->payment->number}")->save(); - if ($invoice->amount == $invoice->balance) { + if ($invoice->amount == $invoice->balance) $invoice->service()->setStatus(Invoice::STATUS_SENT); - } else { + else $invoice->service()->setStatus(Invoice::STATUS_PARTIAL); - } - + $invoice->save(); $client = $invoice->client; $adjustment_amount += $refunded_invoice['amount']; $client->balance += $refunded_invoice['amount']; - $client->save(); //todo adjust ledger balance here? or after and reference the credit and its total } - $ledger_string = ''; //todo + // $ledger_string = "Refund for Invoice {$invoice->number} for amount " . $refunded_invoice['amount']; //todo - $this->credit_note->ledger()->updateCreditBalance($adjustment_amount, $ledger_string); + // $this->credit_note->ledger()->updateCreditBalance($adjustment_amount, $ledger_string); - $this->payment->client->paid_to_date -= $this->refund_data['amount']; - $this->payment->client->save(); + + + $client = $this->payment->client->fresh(); + $client->paid_to_date -= $this->total_refund; + $client->save(); } return $this; } + /** + * Saves the payment + * + * @return Payment $payment + */ private function save() { $this->payment->save(); return $this->payment; } + + // public function updateCreditNoteBalance() + // { + // $this->credit_note->balance -= $this->total_refund; + // $this->credit_note->status_id = Credit::STATUS_APPLIED; + + // $this->credit_note->balance === 0 + // ? $this->credit_note->status_id = Credit::STATUS_APPLIED + // : $this->credit_note->status_id = Credit::STATUS_PARTIAL; + + // $this->credit_note->save(); + + // return $this; + // } + + // private function buildCreditNote() + // { + // $this->credit_note = CreditFactory::create($this->payment->company_id, $this->payment->user_id); + // $this->credit_note->assigned_user_id = isset($this->payment->assigned_user_id) ?: null; + // $this->credit_note->date = $this->refund_data['date']; + // $this->credit_note->status_id = Credit::STATUS_SENT; + // $this->credit_note->client_id = $this->payment->client->id; + // $this->credit_note->amount = $this->total_refund; + // $this->credit_note->balance = $this->total_refund; + + // $this->credit_note->save(); + // $this->credit_note->number = $this->payment->client->getNextCreditNumber($this->payment->client); + // $this->credit_note->save(); + + // return $this; + // } + + // private function buildCreditLineItems() + // { + // $ledger_string = ''; + + // if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) { + // foreach ($this->refund_data['invoices'] as $invoice) { + + // $inv = Invoice::find($invoice['invoice_id']); + + // $credit_line_item = InvoiceItemFactory::create(); + // $credit_line_item->quantity = 1; + // $credit_line_item->cost = $invoice['amount']; + // $credit_line_item->product_key = ctrans('texts.invoice'); + // $credit_line_item->notes = ctrans('texts.refund_body', ['amount' => $invoice['amount'], 'invoice_number' => $inv->number]); + // $credit_line_item->line_total = $invoice['amount']; + // $credit_line_item->date = $this->refund_data['date']; + + // $ledger_string .= $credit_line_item->notes . ' '; + + // $line_items[] = $credit_line_item; + // } + // } else { + + // $credit_line_item = InvoiceItemFactory::create(); + // $credit_line_item->quantity = 1; + // $credit_line_item->cost = $this->refund_data['amount']; + // $credit_line_item->product_key = ctrans('texts.credit'); + // $credit_line_item->notes = ctrans('texts.credit_created_by', ['transaction_reference' => $this->payment->number]); + // $credit_line_item->line_total = $this->refund_data['amount']; + // $credit_line_item->date = $this->refund_data['date']; + + // $line_items = []; + // $line_items[] = $credit_line_item; + // } + + // $this->credit_note->line_items = $line_items; + // $this->credit_note->save(); + + // return $this; + // } + } diff --git a/app/Services/Payment/RefundPaymentOld.php b/app/Services/Payment/RefundPaymentOld.php new file mode 100644 index 000000000000..1c84507ce082 --- /dev/null +++ b/app/Services/Payment/RefundPaymentOld.php @@ -0,0 +1,302 @@ +payment = $payment; + + $this->refund_data = $refund_data; + + $this->total_refund = 0; + + $this->gateway_refund_status = false; + + $this->activity_repository = new ActivityRepository(); + } + + public function run() + { + + return $this->calculateTotalRefund() //sets amount for the refund (needed if we are refunding multiple invoices in one payment) + ->setStatus() //sets status of payment + ->buildCreditNote() //generate the credit note + ->buildCreditLineItems() //generate the credit note items + ->updateCreditables() //return the credits first + ->updatePaymentables() //update the paymentable items + ->adjustInvoices() + ->processGatewayRefund() //process the gateway refund if needed + ->save(); + } + + private function processGatewayRefund() + { + if ($this->refund_data['gateway_refund'] !== false && $this->total_refund > 0) { + $gateway = CompanyGateway::first(); + + if ($gateway) { + $response = $gateway->driver($this->payment->client)->refund($this->payment, $this->total_refund); + + if ($response['success']) { + throw new PaymentRefundFailed(); + } + + $this->payment->refunded = $this->total_refund; + + $this + ->createActivity($gateway) + ->updateCreditNoteBalance(); + } + } else { + $this->payment->refunded += $this->total_refund; + } + + return $this; + } + + public function updateCreditNoteBalance() + { + $this->credit_note->balance -= $this->total_refund; + $this->credit_note->status_id = Credit::STATUS_APPLIED; + + $this->credit_note->balance === 0 + ? $this->credit_note->status_id = Credit::STATUS_APPLIED + : $this->credit_note->status_id = Credit::STATUS_PARTIAL; + + $this->credit_note->save(); + + return $this; + } + + private function createActivity($notes) + { + $fields = new \stdClass; + $activity_repo = new ActivityRepository(); + + $fields->payment_id = $this->payment->id; + $fields->user_id = $this->payment->user_id; + $fields->company_id = $this->payment->company_id; + $fields->activity_type_id = Activity::REFUNDED_PAYMENT; + $fields->credit_id = $this->credit_note->id; + $fields->notes = json_encode($notes); + + if (isset($this->refund_data['invoices'])) { + foreach ($this->refund_data['invoices'] as $invoice) { + $fields->invoice_id = $invoice['invoice_id']; + $activity_repo->save($fields, $this->payment); + } + } else { + $activity_repo->save($fields, $this->payment); + } + + return $this; + } + + private function calculateTotalRefund() + { + + if (array_key_exists('invoices', $this->refund_data) && count($this->refund_data['invoices']) > 0){ + info("array of invoice to refund"); + $this->total_refund = collect($this->refund_data['invoices'])->sum('amount'); + } + else{ + info("no invoices found - refunding total."); + $this->total_refund = $this->refund_data['amount']; + } + + return $this; + } + + private function setStatus() + { + if ($this->refund_data['amount'] == $this->payment->amount) { + $this->payment->status_id = Payment::STATUS_REFUNDED; + } else { + $this->payment->status_id = Payment::STATUS_PARTIALLY_REFUNDED; + } + + return $this; + } + + private function buildCreditNote() + { + $this->credit_note = CreditFactory::create($this->payment->company_id, $this->payment->user_id); + $this->credit_note->assigned_user_id = isset($this->payment->assigned_user_id) ?: null; + $this->credit_note->date = $this->refund_data['date']; + $this->credit_note->status_id = Credit::STATUS_SENT; + $this->credit_note->client_id = $this->payment->client->id; + $this->credit_note->amount = $this->total_refund; + $this->credit_note->balance = $this->total_refund; + + $this->credit_note->save(); + $this->credit_note->number = $this->payment->client->getNextCreditNumber($this->payment->client); + $this->credit_note->save(); + + return $this; + } + + private function buildCreditLineItems() + { + $ledger_string = ''; + + if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) { + foreach ($this->refund_data['invoices'] as $invoice) { + + $inv = Invoice::find($invoice['invoice_id']); + + $credit_line_item = InvoiceItemFactory::create(); + $credit_line_item->quantity = 1; + $credit_line_item->cost = $invoice['amount']; + $credit_line_item->product_key = ctrans('texts.invoice'); + $credit_line_item->notes = ctrans('texts.refund_body', ['amount' => $invoice['amount'], 'invoice_number' => $inv->number]); + $credit_line_item->line_total = $invoice['amount']; + $credit_line_item->date = $this->refund_data['date']; + + $ledger_string .= $credit_line_item->notes . ' '; + + $line_items[] = $credit_line_item; + } + } else { + + $credit_line_item = InvoiceItemFactory::create(); + $credit_line_item->quantity = 1; + $credit_line_item->cost = $this->refund_data['amount']; + $credit_line_item->product_key = ctrans('texts.credit'); + $credit_line_item->notes = ctrans('texts.credit_created_by', ['transaction_reference' => $this->payment->number]); + $credit_line_item->line_total = $this->refund_data['amount']; + $credit_line_item->date = $this->refund_data['date']; + + $line_items = []; + $line_items[] = $credit_line_item; + } + + $this->credit_note->line_items = $line_items; + $this->credit_note->save(); + + return $this; + } + + private function updatePaymentables() + { + if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) { + $this->payment->invoices->each(function ($paymentable_invoice) { + + collect($this->refund_data['invoices'])->each(function ($refunded_invoice) use ($paymentable_invoice) { + + if ($refunded_invoice['invoice_id'] == $paymentable_invoice->id) { + $paymentable_invoice->pivot->refunded += $refunded_invoice['amount']; + $paymentable_invoice->pivot->save(); + } + }); + }); + } + + return $this; + } + + private function updateCreditables() + { + + if ($this->payment->credits()->exists()) { + //Adjust credits first!!! + foreach ($this->payment->credits as $paymentable_credit) { + $available_credit = $paymentable_credit->pivot->amount - $paymentable_credit->pivot->refunded; + + if ($available_credit > $this->total_refund) { + $paymentable_credit->pivot->refunded += $this->total_refund; + $paymentable_credit->pivot->save(); + + $paymentable_credit->balance += $this->total_refund; + $paymentable_credit->save(); + + $this->total_refund = 0; + } else { + $paymentable_credit->pivot->refunded += $available_credit; + $paymentable_credit->pivot->save(); + + $paymentable_credit->balance += $available_credit; + $paymentable_credit->save(); + + $this->total_refund -= $available_credit; + } + + if ($this->total_refund == 0) { + break; + } + } + } + + return $this; + } + + + private function adjustInvoices() + { + $adjustment_amount = 0; + + if (isset($this->refund_data['invoices']) && count($this->refund_data['invoices']) > 0) { + foreach ($this->refund_data['invoices'] as $refunded_invoice) { + $invoice = Invoice::find($refunded_invoice['invoice_id']); + + $invoice->service()->updateBalance($refunded_invoice['amount'])->save(); + + if ($invoice->amount == $invoice->balance) { + $invoice->service()->setStatus(Invoice::STATUS_SENT); + } else { + $invoice->service()->setStatus(Invoice::STATUS_PARTIAL); + } + + $invoice->save(); + + $client = $invoice->client; + + $adjustment_amount += $refunded_invoice['amount']; + $client->balance += $refunded_invoice['amount']; + + $client->save(); + + //todo adjust ledger balance here? or after and reference the credit and its total + } + + $ledger_string = ''; //todo + + $this->credit_note->ledger()->updateCreditBalance($adjustment_amount, $ledger_string); + + $this->payment->client->paid_to_date -= $this->refund_data['amount']; + $this->payment->client->save(); + } + + return $this; + } + + private function save() + { + $this->payment->save(); + + return $this->payment; + } +} diff --git a/database/migrations/2014_10_13_000000_create_users_table.php b/database/migrations/2014_10_13_000000_create_users_table.php index 8baaaf600022..5109954e312f 100644 --- a/database/migrations/2014_10_13_000000_create_users_table.php +++ b/database/migrations/2014_10_13_000000_create_users_table.php @@ -954,6 +954,7 @@ class CreateUsersTable extends Migration $t->unsignedInteger('client_contact_id')->nullable(); $t->unsignedInteger('invitation_id')->nullable(); $t->unsignedInteger('company_gateway_id')->nullable(); + $t->unsignedInteger('gateway_type_id')->nullable(); $t->unsignedInteger('type_id')->nullable(); $t->unsignedInteger('status_id')->index(); $t->decimal('amount', 16, 4)->default(0); diff --git a/database/seeds/RandomDataSeeder.php b/database/seeds/RandomDataSeeder.php index 855dfecf51dc..c4eed11060dc 100644 --- a/database/seeds/RandomDataSeeder.php +++ b/database/seeds/RandomDataSeeder.php @@ -265,29 +265,29 @@ class RandomDataSeeder extends Seeder ]); - if (config('ninja.testvars.stripe')) { - $cg = new CompanyGateway; - $cg->company_id = $company->id; - $cg->user_id = $user->id; - $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; - $cg->require_cvv = true; - $cg->show_billing_address = true; - $cg->show_shipping_address = true; - $cg->update_details = true; - $cg->config = encrypt(config('ninja.testvars.stripe')); - $cg->save(); + // if (config('ninja.testvars.stripe')) { + // $cg = new CompanyGateway; + // $cg->company_id = $company->id; + // $cg->user_id = $user->id; + // $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; + // $cg->require_cvv = true; + // $cg->show_billing_address = true; + // $cg->show_shipping_address = true; + // $cg->update_details = true; + // $cg->config = encrypt(config('ninja.testvars.stripe')); + // $cg->save(); - $cg = new CompanyGateway; - $cg->company_id = $company->id; - $cg->user_id = $user->id; - $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; - $cg->require_cvv = true; - $cg->show_billing_address = true; - $cg->show_shipping_address = true; - $cg->update_details = true; - $cg->config = encrypt(config('ninja.testvars.stripe')); - $cg->save(); - } + // $cg = new CompanyGateway; + // $cg->company_id = $company->id; + // $cg->user_id = $user->id; + // $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; + // $cg->require_cvv = true; + // $cg->show_billing_address = true; + // $cg->show_shipping_address = true; + // $cg->update_details = true; + // $cg->config = encrypt(config('ninja.testvars.stripe')); + // $cg->save(); + // } // if (config('ninja.testvars.paypal')) { // $cg = new CompanyGateway; @@ -315,18 +315,18 @@ class RandomDataSeeder extends Seeder // $cg->save(); // } - // if(config('ninja.testvars.authorize')) { - // $cg = new CompanyGateway; - // $cg->company_id = $company->id; - // $cg->user_id = $user->id; - // $cg->gateway_key = '3b6621f970ab18887c4f6dca78d3f8bb'; - // $cg->require_cvv = true; - // $cg->show_billing_address = true; - // $cg->show_shipping_address = true; - // $cg->update_details = true; - // $cg->config = encrypt(config('ninja.testvars.authorize')); - // $cg->save(); - // } + if(config('ninja.testvars.authorize')) { + $cg = new CompanyGateway; + $cg->company_id = $company->id; + $cg->user_id = $user->id; + $cg->gateway_key = '3b6621f970ab18887c4f6dca78d3f8bb'; + $cg->require_cvv = true; + $cg->show_billing_address = true; + $cg->show_shipping_address = true; + $cg->update_details = true; + $cg->config = encrypt(config('ninja.testvars.authorize')); + $cg->save(); + } } } diff --git a/public/js/clients/invoices/action-selectors.js b/public/js/clients/invoices/action-selectors.js index d51d2a3a147e..fe74ca68c27f 100644 --- a/public/js/clients/invoices/action-selectors.js +++ b/public/js/clients/invoices/action-selectors.js @@ -1,2 +1,2 @@ /*! For license information please see action-selectors.js.LICENSE.txt */ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=5)}({5:function(e,t,n){e.exports=n("Boob")},Boob:function(e,t){function n(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{};n.hasOwnProperty("single")&&document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()}));var r=document.createElement("INPUT");r.setAttribute("name","invoices[]"),r.setAttribute("value",e.dataset.value),r.setAttribute("class","child-hidden-input"),r.hidden=!0,t.append(r)}},{key:"handle",value:function(){var e=this;this.parentElement.addEventListener("click",(function(){e.watchCheckboxes(e.parentElement)}));var t=!0,n=!1,r=void 0;try{for(var o,c=function(){var t=o.value;t.addEventListener("click",(function(){e.processChildItem(t,e.parentForm)}))},u=document.querySelectorAll(".form-check-child")[Symbol.iterator]();!(t=(o=u.next()).done);t=!0)c()}catch(e){n=!0,r=e}finally{try{t||null==u.return||u.return()}finally{if(n)throw r}}}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()}}); \ No newline at end of file +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=5)}({5:function(e,t,n){e.exports=n("Boob")},Boob:function(e,t){function n(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{};if(n.hasOwnProperty("single")&&document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()})),!1!==e.checked){var r=document.createElement("INPUT");r.setAttribute("name","invoices[]"),r.setAttribute("value",e.dataset.value),r.setAttribute("class","child-hidden-input"),r.hidden=!0,t.append(r)}else{var o=document.querySelectorAll("input.child-hidden-input"),c=!0,u=!1,l=void 0;try{for(var i,a=o[Symbol.iterator]();!(c=(i=a.next()).done);c=!0){var d=i.value;d.value==e.dataset.value&&d.remove()}}catch(e){u=!0,l=e}finally{try{c||null==a.return||a.return()}finally{if(u)throw l}}}}},{key:"handle",value:function(){var e=this;this.parentElement.addEventListener("click",(function(){e.watchCheckboxes(e.parentElement)}));var t=!0,n=!1,r=void 0;try{for(var o,c=function(){var t=o.value;t.addEventListener("click",(function(){e.processChildItem(t,e.parentForm)}))},u=document.querySelectorAll(".form-check-child")[Symbol.iterator]();!(t=(o=u.next()).done);t=!0)c()}catch(e){n=!0,r=e}finally{try{t||null==u.return||u.return()}finally{if(n)throw r}}}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()}}); \ No newline at end of file diff --git a/public/js/clients/payment_methods/authorize-authorize-card.js b/public/js/clients/payment_methods/authorize-authorize-card.js index 9e3b91b50224..c09786af1ac1 100644 --- a/public/js/clients/payment_methods/authorize-authorize-card.js +++ b/public/js/clients/payment_methods/authorize-authorize-card.js @@ -1 +1,2 @@ -!function(e){var t={};function n(a){if(t[a])return t[a].exports;var r=t[a]={i:a,l:!1,exports:{}};return e[a].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,a){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(a,r,function(t){return e[t]}.bind(null,r));return a},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=2)}({2:function(e,t,n){e.exports=n("6vDv")},"6vDv":function(e,t){function n(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{};n.hasOwnProperty("single")&&document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()}));var r=document.createElement("INPUT");r.setAttribute("name","quotes[]"),r.setAttribute("value",e.dataset.value),r.setAttribute("class","child-hidden-input"),r.hidden=!0,t.append(r)}},{key:"handle",value:function(){var e=this;this.parentElement.addEventListener("click",(function(){e.watchCheckboxes(e.parentElement)}));var t=!0,n=!1,r=void 0;try{for(var o,c=function(){var t=o.value;t.addEventListener("click",(function(){e.processChildItem(t,e.parentForm)}))},u=document.querySelectorAll(".form-check-child")[Symbol.iterator]();!(t=(o=u.next()).done);t=!0)c()}catch(e){n=!0,r=e}finally{try{t||null==u.return||u.return()}finally{if(n)throw r}}}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()}}); \ No newline at end of file +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=10)}({10:function(e,t,n){e.exports=n("ydWM")},ydWM:function(e,t){function n(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{};if(n.hasOwnProperty("single")&&document.querySelectorAll(".child-hidden-input").forEach((function(e){return e.remove()})),!1!==e.checked){var r=document.createElement("INPUT");r.setAttribute("name","invoices[]"),r.setAttribute("value",e.dataset.value),r.setAttribute("class","child-hidden-input"),r.hidden=!0,t.append(r)}else{var o=document.querySelectorAll("input.child-hidden-input"),c=!0,u=!1,l=void 0;try{for(var i,a=o[Symbol.iterator]();!(c=(i=a.next()).done);c=!0){var d=i.value;d.value==e.dataset.value&&d.remove()}}catch(e){u=!0,l=e}finally{try{c||null==a.return||a.return()}finally{if(u)throw l}}}}},{key:"handle",value:function(){var e=this;this.parentElement.addEventListener("click",(function(){e.watchCheckboxes(e.parentElement)}));var t=!0,n=!1,r=void 0;try{for(var o,c=function(){var t=o.value;t.addEventListener("click",(function(){e.processChildItem(t,e.parentForm)}))},u=document.querySelectorAll(".form-check-child")[Symbol.iterator]();!(t=(o=u.next()).done);t=!0)c()}catch(e){n=!0,r=e}finally{try{t||null==u.return||u.return()}finally{if(n)throw r}}}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()}}); \ No newline at end of file diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 99e4e2e9c95c..4904b4d14a92 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -1,10 +1,10 @@ { "/js/app.js": "/js/app.js?id=baf7fef12d5e65c3d9ff", "/css/app.css": "/css/app.css?id=369c7335c317e8ac0212", - "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=0632d6281202800e0921", + "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=d244486b16dc6f94a726", "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=d7e708d66a9c769b4c6e", "/js/clients/payment_methods/authorize-ach.js": "/js/clients/payment_methods/authorize-ach.js?id=9e6495d9ae236b3cb5ad", - "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=3f6129bf10ff3bf20332", + "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=83c223814db63f9db590", "/js/clients/payment_methods/authorize-stripe-card.js": "/js/clients/payment_methods/authorize-stripe-card.js?id=f4c45f0da9868d840799", "/js/clients/payments/alipay.js": "/js/clients/payments/alipay.js?id=428ac5b81ed722636e8f", "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=3a74587f41c617a7cc81", @@ -12,7 +12,7 @@ "/js/clients/payments/checkout.com.js": "/js/clients/payments/checkout.com.js?id=42d239882b80af83ad22", "/js/clients/payments/process.js": "/js/clients/payments/process.js?id=49b220081e0d7fa8140b", "/js/clients/payments/sofort.js": "/js/clients/payments/sofort.js?id=ff4ad07a93bd9fb327c1", - "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=2fe0ad3e46ead2edb8c3", + "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=b6b33ab51b58b51e1212", "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=1c5d76fb5f98bd49f6c8", "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=ba1182244cda0e0ffbeb", "/js/setup/setup.js": "/js/setup/setup.js?id=87653cfb4084aadea7a2", diff --git a/resources/js/clients/invoices/action-selectors.js b/resources/js/clients/invoices/action-selectors.js index 22d4795fbdd6..7f2792def0fb 100644 --- a/resources/js/clients/invoices/action-selectors.js +++ b/resources/js/clients/invoices/action-selectors.js @@ -10,48 +10,65 @@ class ActionSelectors { constructor() { - this.parentElement = document.querySelector(".form-check-parent"); - this.parentForm = document.getElementById("bulkActions"); + this.parentElement = document.querySelector('.form-check-parent'); + this.parentForm = document.getElementById('bulkActions'); } watchCheckboxes(parentElement) { - document.querySelectorAll(".form-check-child").forEach(child => { + document + .querySelectorAll('.child-hidden-input') + .forEach((element) => element.remove()); + + document.querySelectorAll('.form-check-child').forEach((child) => { if (parentElement.checked) { child.checked = parentElement.checked; - this.processChildItem(child, document.getElementById("bulkActions")); + this.processChildItem( + child, + document.getElementById('bulkActions') + ); } else { child.checked = false; document - .querySelectorAll(".child-hidden-input") - .forEach(element => element.remove()); + .querySelectorAll('.child-hidden-input') + .forEach((element) => element.remove()); } }); } processChildItem(element, parent, options = {}) { - if (options.hasOwnProperty("single")) { + if (options.hasOwnProperty('single')) { document - .querySelectorAll(".child-hidden-input") - .forEach(element => element.remove()); + .querySelectorAll('.child-hidden-input') + .forEach((element) => element.remove()); } - let _temp = document.createElement("INPUT"); + if (element.checked === false) { + let inputs = document.querySelectorAll('input.child-hidden-input'); - _temp.setAttribute("name", "invoices[]"); - _temp.setAttribute("value", element.dataset.value); - _temp.setAttribute("class", "child-hidden-input"); + for (let i of inputs) { + if (i.value == element.dataset.value) i.remove(); + } + + return; + } + + let _temp = document.createElement('INPUT'); + + _temp.setAttribute('name', 'invoices[]'); + _temp.setAttribute('value', element.dataset.value); + _temp.setAttribute('class', 'child-hidden-input'); _temp.hidden = true; parent.append(_temp); } handle() { - this.parentElement.addEventListener("click", () => { + this.parentElement.addEventListener('click', () => { this.watchCheckboxes(this.parentElement); }); - for (let child of document.querySelectorAll(".form-check-child")) { - child.addEventListener("click", () => { + for (let child of document.querySelectorAll('.form-check-child')) { + child.addEventListener('click', () => { this.processChildItem(child, this.parentForm); }); } diff --git a/resources/js/clients/payment_methods/authorize-authorize-card.js b/resources/js/clients/payment_methods/authorize-authorize-card.js index 4870ac9218b5..0c5651926dce 100644 --- a/resources/js/clients/payment_methods/authorize-authorize-card.js +++ b/resources/js/clients/payment_methods/authorize-authorize-card.js @@ -15,34 +15,26 @@ class AuthorizeAuthorizeCard { this.loginId = loginId; this.cardHolderName = document.getElementById("cardholder_name"); this.cardButton = document.getElementById("card_button"); - this.form = { valid: false }; - - this.translations = { - invalidCard: document.querySelector('meta[name="credit-card-invalid"]').content, - invalidMonth: document.querySelector('meta[name="month-invalid"]').content, - invalidYear: document.querySelector('meta[name="year-invalid"]').content, - } } handleAuthorization() { + + var myCard = $('#my-card'); - var authData = {}; + var authData = {}; authData.clientKey = this.publicKey; authData.apiLoginID = this.loginId; - var cardData = {}; - cardData.cardNumber = document.getElementById("card_number").value; - cardData.month = document.getElementById("expiration_month").value; - cardData.year = document.getElementById("expiration_year").value; + var cardData = {}; + cardData.cardNumber = myCard.CardJs('cardNumber'); + cardData.month = myCard.CardJs('expiryMonth'); + cardData.year = myCard.CardJs('expiryYear');; cardData.cardCode = document.getElementById("cvv").value; - var secureData = {}; + var secureData = {}; secureData.authData = authData; secureData.cardData = cardData; - // If using banking information instead of card information, - // send the bankData object instead of the cardData object. - // - // secureData.bankData = bankData; + Accept.dispatchData(secureData, this.responseHandler); return false; @@ -71,53 +63,15 @@ class AuthorizeAuthorizeCard { return false; } - handleFormValidation = () => { - document.getElementById("card_number").addEventListener('keyup', (e) => { - let errors = document.getElementById('card_number_errors'); - if (valid.number(e.target.value).isValid) { - errors.hidden = true; - this.form.valid = true; - } else { - errors.textContent = this.translations.invalidCard; - errors.hidden = false; - this.form.valid = false; - } - }); - - document.getElementById("expiration_month").addEventListener('keyup', (e) => { - let errors = document.getElementById('expiration_month_errors'); - if (valid.expirationMonth(e.target.value).isValid) { - errors.hidden = true; - this.form.valid = true; - } else { - errors.textContent = this.translations.invalidMonth; - errors.hidden = false; - this.form.valid = false; - } - }); - - document.getElementById("expiration_year").addEventListener('keyup', (e) => { - let errors = document.getElementById('expiration_year_errors'); - if (valid.expirationYear(e.target.value).isValid) { - errors.hidden = true; - this.form.valid = true; - } else { - errors.textContent = this.translations.invalidYear; - errors.hidden = false; - this.form.valid = false; - } - }); - } - handle() { - this.handleFormValidation(); + //this.handleFormValidation(); // At this point as an small API you can request this.form.valid to check if input elements are valid. // Note: this.form.valid will not handle empty fields. this.cardButton.addEventListener("click", () => { this.cardButton.disabled = !this.cardButton.disabled; - // this.handleAuthorization(); + this.handleAuthorization(); }); diff --git a/resources/js/clients/quotes/action-selectors.js b/resources/js/clients/quotes/action-selectors.js index 751bad043125..7f2792def0fb 100644 --- a/resources/js/clients/quotes/action-selectors.js +++ b/resources/js/clients/quotes/action-selectors.js @@ -10,48 +10,65 @@ class ActionSelectors { constructor() { - this.parentElement = document.querySelector(".form-check-parent"); - this.parentForm = document.getElementById("bulkActions"); + this.parentElement = document.querySelector('.form-check-parent'); + this.parentForm = document.getElementById('bulkActions'); } watchCheckboxes(parentElement) { - document.querySelectorAll(".form-check-child").forEach(child => { + document + .querySelectorAll('.child-hidden-input') + .forEach((element) => element.remove()); + + document.querySelectorAll('.form-check-child').forEach((child) => { if (parentElement.checked) { child.checked = parentElement.checked; - this.processChildItem(child, document.getElementById("bulkActions")); + this.processChildItem( + child, + document.getElementById('bulkActions') + ); } else { child.checked = false; document - .querySelectorAll(".child-hidden-input") - .forEach(element => element.remove()); + .querySelectorAll('.child-hidden-input') + .forEach((element) => element.remove()); } }); } processChildItem(element, parent, options = {}) { - if (options.hasOwnProperty("single")) { + if (options.hasOwnProperty('single')) { document - .querySelectorAll(".child-hidden-input") - .forEach(element => element.remove()); + .querySelectorAll('.child-hidden-input') + .forEach((element) => element.remove()); } - let _temp = document.createElement("INPUT"); + if (element.checked === false) { + let inputs = document.querySelectorAll('input.child-hidden-input'); - _temp.setAttribute("name", "quotes[]"); - _temp.setAttribute("value", element.dataset.value); - _temp.setAttribute("class", "child-hidden-input"); + for (let i of inputs) { + if (i.value == element.dataset.value) i.remove(); + } + + return; + } + + let _temp = document.createElement('INPUT'); + + _temp.setAttribute('name', 'invoices[]'); + _temp.setAttribute('value', element.dataset.value); + _temp.setAttribute('class', 'child-hidden-input'); _temp.hidden = true; parent.append(_temp); } handle() { - this.parentElement.addEventListener("click", () => { + this.parentElement.addEventListener('click', () => { this.watchCheckboxes(this.parentElement); }); - for (let child of document.querySelectorAll(".form-check-child")) { - child.addEventListener("click", () => { + for (let child of document.querySelectorAll('.form-check-child')) { + child.addEventListener('click', () => { this.processChildItem(child, this.parentForm); }); } diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 42c5f2bfaf78..0bf6303e63ee 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -3223,5 +3223,9 @@ return [ 'month_invalid' => 'Provided month is not valid.', 'year_invalid' => 'Provided year is not valid.', - 'if_you_need_help' => 'If you need help you can either post to our', + 'https_required' => 'HTTPS is required, form will fail', + 'if_you_need_help' => 'If you need help you can either post to our', + 'reversed' => 'Reversed', + 'update_password_on_confirm' => 'After updating password, your account will be confirmed.', + 'bank_account_not_linked' => 'To pay with bank account, first you have to add it as payment method.', ]; diff --git a/resources/views/portal/ninja2020/components/livewire/quotes-table.blade.php b/resources/views/portal/ninja2020/components/livewire/quotes-table.blade.php index eddeec9a1f44..5c7ef5f35286 100644 --- a/resources/views/portal/ninja2020/components/livewire/quotes-table.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/quotes-table.blade.php @@ -90,7 +90,7 @@ {!! App\Models\Quote::badgeForStatus($quote->status_id) !!} - + @lang('texts.view') diff --git a/resources/views/portal/ninja2020/gateways/authorize/add_credit_card.blade.php b/resources/views/portal/ninja2020/gateways/authorize/add_credit_card.blade.php index 74c9c03f02d6..1249b394c48e 100644 --- a/resources/views/portal/ninja2020/gateways/authorize/add_credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/authorize/add_credit_card.blade.php @@ -30,6 +30,9 @@
+ @if(!Request::isSecure()) +

{{ ctrans('texts.https_required') }}

+ @endif

diff --git a/resources/views/portal/ninja2020/gateways/stripe/ach/pay.blade.php b/resources/views/portal/ninja2020/gateways/stripe/ach/pay.blade.php index 46182a54c5d0..d4ffdf84a2fd 100644 --- a/resources/views/portal/ninja2020/gateways/stripe/ach/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/stripe/ach/pay.blade.php @@ -2,18 +2,20 @@ @section('meta_title', ctrans('texts.ach')) @section('body') -
- @csrf - @foreach($invoices as $invoice) - - @endforeach - - - - - - -
+ @if($token) +
+ @csrf + @foreach($invoices as $invoice) + + @endforeach + + + + + + +
+ @endif
@@ -29,27 +31,36 @@

-
-
- {{ ctrans('texts.payment_type') }} -
-
- {{ ctrans('texts.ach') }} ({{ ctrans('texts.bank_transfer') }}) (****{{ $token->meta->last4 }}) -
-
-
-
- {{ ctrans('texts.amount') }} -
-
- {{ App\Utils\Number::formatMoney($amount, $client) }} -
-
-
- -
+ @if($token) +
+
+ {{ ctrans('texts.payment_type') }} +
+
+ {{ ctrans('texts.ach') }} ({{ ctrans('texts.bank_transfer') }}) (****{{ $token->meta->last4 }}) +
+
+
+
+ {{ ctrans('texts.amount') }} +
+
+ {{ App\Utils\Number::formatMoney($amount, $client) }} +
+
+
+ +
+ @else +
+
+ {{ ctrans('texts.bank_account_not_linked') }} + {{ ctrans('texts.add_payment_method') }} +
+
+ @endif

diff --git a/resources/views/portal/ninja2020/quotes/index.blade.php b/resources/views/portal/ninja2020/quotes/index.blade.php index e2e452c0136b..0258c6c5eff5 100644 --- a/resources/views/portal/ninja2020/quotes/index.blade.php +++ b/resources/views/portal/ninja2020/quotes/index.blade.php @@ -13,7 +13,6 @@ @section('body')
- {{ ctrans('texts.with_selected') }}
@csrf +
+ +
+
+
+@endsection diff --git a/resources/views/themes/ninja2020/auth/passwords/reset.blade.php b/resources/views/themes/ninja2020/auth/passwords/reset.blade.php index deaa68e6e9d9..f59686661e99 100644 --- a/resources/views/themes/ninja2020/auth/passwords/reset.blade.php +++ b/resources/views/themes/ninja2020/auth/passwords/reset.blade.php @@ -38,7 +38,7 @@ @enderror
- + diff --git a/routes/web.php b/routes/web.php index be67e615f033..8e71835375f5 100644 --- a/routes/web.php +++ b/routes/web.php @@ -25,4 +25,5 @@ Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('passw */ Route::group(['middleware' => ['url_db']], function () { Route::get('/user/confirm/{confirmation_code}', 'UserController@confirm'); + Route::post('/user/confirm/{confirmation_code}', 'UserController@confirmWithPassword'); }); \ No newline at end of file diff --git a/tests/Feature/UserTest.php b/tests/Feature/UserTest.php index 167b8a468d4d..5c9057fb7a4a 100644 --- a/tests/Feature/UserTest.php +++ b/tests/Feature/UserTest.php @@ -47,9 +47,6 @@ class UserTest extends TestCase $this->makeTestData(); - $this->withoutMiddleware( - ThrottleRequests::class - ); } public function testUserList() @@ -68,7 +65,7 @@ class UserTest extends TestCase $data = [ 'first_name' => 'hey', 'last_name' => 'you', - 'email' => 'bob@good.ole.boys.com', + 'email' => 'bob1@good.ole.boys.com', 'company_user' => [ 'is_admin' => false, 'is_owner' => false, @@ -79,7 +76,7 @@ class UserTest extends TestCase $response = $this->withHeaders([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, - 'X-API-PASSWORD' => 'ALongAndBriliantPassword', + 'X-API-PASSWORD' => 'ALongAndBriliantPassword', ])->post('/api/v1/users?include=company_user', $data); $response->assertStatus(200);