diff --git a/app/Http/Controllers/ClientPortal/NinjaPlanController.php b/app/Http/Controllers/ClientPortal/NinjaPlanController.php index 739f573f3556..e41fb872a6ef 100644 --- a/app/Http/Controllers/ClientPortal/NinjaPlanController.php +++ b/app/Http/Controllers/ClientPortal/NinjaPlanController.php @@ -18,14 +18,20 @@ use App\Libraries\MultiDB; use App\Models\Account; use App\Models\ClientContact; use App\Models\Company; +use App\Models\Invoice; +use App\Models\RecurringInvoice; +use App\Models\Subscription; use App\Utils\Ninja; +use App\Utils\Traits\MakesHash; use Illuminate\Contracts\Routing\ResponseFactory; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Auth; class NinjaPlanController extends Controller { + use MakesHash; public function index(string $contact_key, string $account_or_company_key) { @@ -57,4 +63,82 @@ class NinjaPlanController extends Controller return redirect()->route('client.catchall'); } + + public function plan() + { + //harvest the current plan + $data = []; + + if(MultiDB::findAndSetDbByAccountKey(Auth::guard('contact')->user()->client->custom_value2)) + { + $account = Account::where('key', Auth::guard('contact')->user()->client->custom_value2)->first(); + + if($account) + { + + if(Carbon::parse($account->plan_expires)->lt(now())){ + //expired get the most recent invoice for payment + + $late_invoice = Invoice::on('db-ninja-01') + ->where('company_id', Auth::guard('contact')->user()->company->id) + ->where('client_id', Auth::guard('contact')->user()->client->id) + ->where('status_id', Invoice::STATUS_SENT) + ->whereNotNull('subscription_id') + ->orderBy('id', 'DESC') + ->first(); + + //account status means user cannot perform upgrades until they pay their account. + $data['late_invoice'] = $late_invoice; + + } + + $recurring_invoice = RecurringInvoice::on('db-ninja-01') + ->where('client_id', auth('contact')->user()->client->id) + ->where('company_id', Auth::guard('contact')->user()->company->id) + ->whereNotNull('subscription_id') + ->where('status_id', RecurringInvoice::STATUS_ACTIVE) + ->orderBy('id', 'desc') + ->first(); + + $monthly_plans = Subscription::on('db-ninja-01') + ->where('company_id', Auth::guard('contact')->user()->company->id) + ->where('group_id', 6) + ->orderBy('promo_price', 'ASC') + ->get(); + + $yearly_plans = Subscription::on('db-ninja-01') + ->where('company_id', Auth::guard('contact')->user()->company->id) + ->where('group_id', 31) + ->orderBy('promo_price', 'ASC') + ->get(); + + $monthly_plans = $monthly_plans->merge($yearly_plans); + + $current_subscription_id = $recurring_invoice ? $this->encodePrimaryKey($recurring_invoice->subscription_id) : false; + + //remove existing subscription + if($current_subscription_id){ + + $monthly_plans = $monthly_plans->filter(function ($plan) use($current_subscription_id){ + return (string)$plan->hashed_id != (string)$current_subscription_id; + }); + + } + + $data['account'] = $account; + $data['client'] = Auth::guard('contact')->user()->client; + $data['plans'] = $monthly_plans; + $data['current_subscription_id'] = $current_subscription_id; + $data['current_recurring_id'] = $recurring_invoice ? $recurring_invoice->hashed_id : false; + + return $this->render('plan.index', $data); + + } + + } + else + return redirect()->route('client.catchall'); + + + } } diff --git a/app/Http/Livewire/RequiredClientInfo.php b/app/Http/Livewire/RequiredClientInfo.php index 35372358efc1..71f0ac4f5733 100644 --- a/app/Http/Livewire/RequiredClientInfo.php +++ b/app/Http/Livewire/RequiredClientInfo.php @@ -60,8 +60,8 @@ class RequiredClientInfo extends Component 'contact_first_name' => 'first_name', 'contact_last_name' => 'last_name', - 'contact_email' => 'email', - 'contact_phone' => 'phone', + // 'contact_email' => 'email', + // 'contact_phone' => 'phone', ]; public $show_form = false; @@ -141,7 +141,7 @@ class RequiredClientInfo extends Component $_field = $this->mappings[$field['name']]; if (Str::startsWith($field['name'], 'client_')) { - if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || $this->contact->client->{$_field} = 840) { + if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || $this->contact->client->{$_field} == 840) { $this->show_form = true; } else { $this->fields[$index]['filled'] = true; @@ -149,7 +149,7 @@ class RequiredClientInfo extends Component } if (Str::startsWith($field['name'], 'contact_')) { - if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} = 840) { + if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} == 840) { $this->show_form = true; } else { $this->fields[$index]['filled'] = true; diff --git a/app/Http/Livewire/SubscriptionRecurringInvoicesTable.php b/app/Http/Livewire/SubscriptionRecurringInvoicesTable.php index f5ae39999f45..3568ff8cf502 100644 --- a/app/Http/Livewire/SubscriptionRecurringInvoicesTable.php +++ b/app/Http/Livewire/SubscriptionRecurringInvoicesTable.php @@ -38,6 +38,7 @@ class SubscriptionRecurringInvoicesTable extends Component ->where('client_id', auth('contact')->user()->client->id) ->where('company_id', $this->company->id) ->whereNotNull('subscription_id') + ->where('is_deleted', false) ->where('status_id', RecurringInvoice::STATUS_ACTIVE) ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc') ->withTrashed() diff --git a/app/Http/ViewComposers/PortalComposer.php b/app/Http/ViewComposers/PortalComposer.php index 3f589c759ef1..ca8472aaa496 100644 --- a/app/Http/ViewComposers/PortalComposer.php +++ b/app/Http/ViewComposers/PortalComposer.php @@ -121,6 +121,10 @@ class PortalComposer $data[] = ['title' => ctrans('texts.statement'), 'url' => 'client.statement', 'icon' => 'activity']; + if(Ninja::isHosted() && auth('contact')->user()->company->id == config('ninja.ninja_default_company_id')) + $data[] = ['title' => ctrans('texts.plan'), 'url' => 'client.plan', 'icon' => 'credit-card']; + + return $data; } } diff --git a/app/Jobs/Entity/CreateRawPdf.php b/app/Jobs/Entity/CreateRawPdf.php index 585deb9dbe35..c2c28b0c23f1 100644 --- a/app/Jobs/Entity/CreateRawPdf.php +++ b/app/Jobs/Entity/CreateRawPdf.php @@ -188,9 +188,9 @@ class CreateRawPdf implements ShouldQueue nlog(print_r($e->getMessage(), 1)); } - if (config('ninja.log_pdf_html')) { + // if (config('ninja.log_pdf_html')) { info($maker->getCompiledHTML()); - } + // } if ($pdf) return $pdf; diff --git a/app/PaymentDrivers/Braintree/CreditCard.php b/app/PaymentDrivers/Braintree/CreditCard.php index a00dd398a7f9..f66304b52c2d 100644 --- a/app/PaymentDrivers/Braintree/CreditCard.php +++ b/app/PaymentDrivers/Braintree/CreditCard.php @@ -136,7 +136,10 @@ class CreditCard return $this->processSuccessfulPayment($result); } - return $this->processUnsuccessfulPayment($result); + $error = 'Undefined gateway error'; + + return $this->processUnsuccessfulPayment($error); + } private function getPaymentToken(array $data, $customerId): ?string diff --git a/app/PaymentDrivers/MolliePaymentDriver.php b/app/PaymentDrivers/MolliePaymentDriver.php index e05fe4972924..b256fbdecf83 100644 --- a/app/PaymentDrivers/MolliePaymentDriver.php +++ b/app/PaymentDrivers/MolliePaymentDriver.php @@ -294,7 +294,7 @@ class MolliePaymentDriver extends BaseDriver } $this->init(); - + $codes = [ 'open' => Payment::STATUS_PENDING, 'canceled' => Payment::STATUS_CANCELLED, @@ -312,6 +312,9 @@ class MolliePaymentDriver extends BaseDriver $client = $record->client; } else{ + nlog("mollie webhook"); + nlog($payment); + $client = Client::withTrashed()->find($this->decodePrimaryKey($payment->metadata->client_id)); } diff --git a/app/PaymentDrivers/Stripe/ACH.php b/app/PaymentDrivers/Stripe/ACH.php index a0568cb8f6ec..cac64514a20c 100644 --- a/app/PaymentDrivers/Stripe/ACH.php +++ b/app/PaymentDrivers/Stripe/ACH.php @@ -231,12 +231,24 @@ class ACH $this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state); $this->stripe->payment_hash->save(); + $amount = array_sum(array_column($this->stripe->payment_hash->invoices(), 'amount')) + $this->stripe->payment_hash->fee_total; + $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($this->stripe->payment_hash->invoices(), 'invoice_id'))) + ->withTrashed() + ->first(); + + if ($invoice) { + $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; + } else { + $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; + } + try { $state['charge'] = \Stripe\Charge::create([ 'amount' => $state['amount'], 'currency' => $state['currency'], 'customer' => $state['customer'], 'source' => $state['source'], + 'description' => $description, ], $this->stripe->stripe_connect_auth); $state = array_merge($state, $request->all()); diff --git a/app/PaymentDrivers/WePayPaymentDriver.php b/app/PaymentDrivers/WePayPaymentDriver.php index 73b8d7573949..a0009fb6cd4b 100644 --- a/app/PaymentDrivers/WePayPaymentDriver.php +++ b/app/PaymentDrivers/WePayPaymentDriver.php @@ -208,7 +208,7 @@ class WePayPaymentDriver extends BaseDriver return 'Processed successfully'; } elseif ($objectType == 'account') { - if ($accountId !== $objectId) { + if ($accountId != $objectId) { throw new \Exception('Unknown account ' . $accountId . ' does not equal '.$objectId); } diff --git a/app/Services/Subscription/SubscriptionService.php b/app/Services/Subscription/SubscriptionService.php index ba7da5f28670..dad9c094b8c2 100644 --- a/app/Services/Subscription/SubscriptionService.php +++ b/app/Services/Subscription/SubscriptionService.php @@ -18,6 +18,7 @@ use App\Factory\InvoiceToRecurringInvoiceFactory; use App\Factory\RecurringInvoiceFactory; use App\Jobs\Util\SubscriptionWebhookHandler; use App\Jobs\Util\SystemLogger; +use App\Libraries\MultiDB; use App\Models\Client; use App\Models\ClientContact; use App\Models\Credit; @@ -240,10 +241,10 @@ class SubscriptionService elseif ($outstanding->count() > 1) { //user is changing plan mid frequency cycle //we cannot handle this if there are more than one invoice outstanding. - return null; + return $target->price; } - return null; + return $target->price; } @@ -439,7 +440,7 @@ class SubscriptionService $credit = false; /* Only generate a credit if the previous invoice was paid in full. */ - if($last_invoice->balance == 0) + if($last_invoice && $last_invoice->balance == 0) $credit = $this->createCredit($last_invoice, $target_subscription, $is_credit); $new_recurring_invoice = $this->createNewRecurringInvoice($recurring_invoice); diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 3afda95c0172..7e273e7ff56e 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -4340,6 +4340,7 @@ $LANG = array( 'no_available_methods' => 'We can\'t find any credit cards on your device. Read more about this.', 'gocardless_mandate_not_ready' => 'Payment mandate is not ready. Please try again later.', 'payment_type_instant_bank_pay' => 'Instant Bank Pay', + ); return $LANG; diff --git a/resources/views/pdf-designs/modern.html b/resources/views/pdf-designs/modern.html index 608380e11fb4..906237dfe47b 100644 --- a/resources/views/pdf-designs/modern.html +++ b/resources/views/pdf-designs/modern.html @@ -2,8 +2,8 @@ @import url($font_url); :root { - --primary-color: #298aab; - --secondary-color: #7081e0; + --primary-color: $primary_color; + --secondary-color: $secondary_color; } body { @@ -149,13 +149,13 @@ #footer, #footer-spacer { height: 220px; - padding: 1rem 3rem; + padding: 1rem 1.5rem; margin-top: 1rem; } .footer-content { display: flex; - gap: 20px; + gap: 10px; width: 100%; /* grid-template-columns: 1fr 1fr 1fr; */ color: #fff4e9; @@ -165,8 +165,8 @@ .footer-company-details-address-wrapper { display: flex; - gap: 25px; - margin-right: 150px; + gap: 5px; + margin-right: 60px; } #company-address, @@ -348,7 +348,7 @@ $entity_images