diff --git a/VERSION.txt b/VERSION.txt index 0a4a575a8314..c42b9d4569ec 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.5.65 \ No newline at end of file +5.5.66 \ No newline at end of file diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index 693d42351edf..dd7405278f17 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -149,12 +149,18 @@ class PaymentController extends Controller $payment = $payment->service()->applyCredits($payment_hash)->save(); $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id'))); + + $invoices->each(function ($i){ + $i->is_proforma = false; + $i->saveQuietly(); + }); event('eloquent.created: App\Models\Payment', $payment); if($invoices->sum('balance') > 0){ $invoice = $invoices->first(); + $invoice->service()->touchPdf(true); return redirect()->route('client.invoice.show', ['invoice' => $invoice->hashed_id, 'hash' => $request->input('hash')]); } diff --git a/app/Http/Controllers/CompanyController.php b/app/Http/Controllers/CompanyController.php index 1325c88564e8..092dc9d23082 100644 --- a/app/Http/Controllers/CompanyController.php +++ b/app/Http/Controllers/CompanyController.php @@ -494,7 +494,7 @@ class CompanyController extends BaseController $account->delete(); if (Ninja::isHosted()) { - \Modules\Admin\Jobs\Account\NinjaDeletedAccount::dispatch($account_key, $request->all()); + \Modules\Admin\Jobs\Account\NinjaDeletedAccount::dispatch($account_key, $request->all(), auth()->user()->email); } LightLogs::create(new AccountDeleted()) diff --git a/app/Http/Livewire/InvoicesTable.php b/app/Http/Livewire/InvoicesTable.php index bf8bf2269cfe..9a84f50870a9 100644 --- a/app/Http/Livewire/InvoicesTable.php +++ b/app/Http/Livewire/InvoicesTable.php @@ -52,6 +52,7 @@ class InvoicesTable extends Component $query = Invoice::query() ->where('company_id', $this->company->id) ->where('is_deleted', false) + ->where('is_proforma', false) ->with('client.gateway_tokens', 'client.contacts') ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc'); diff --git a/app/Import/Providers/BaseImport.php b/app/Import/Providers/BaseImport.php index 937ca6447090..938115dfe010 100644 --- a/app/Import/Providers/BaseImport.php +++ b/app/Import/Providers/BaseImport.php @@ -227,6 +227,8 @@ class BaseImport ]; nlog("Ingest {$ex->getMessage()}"); + nlog($record); + } } @@ -368,7 +370,7 @@ class BaseImport $payment_data['invoices'] = [ [ 'invoice_id' => $invoice->id, - 'amount' => $payment_data['amount'] ?? null, + 'amount' => min($invoice->amount, $payment_data['amount']) ?? null, ], ]; diff --git a/app/Import/Transformer/Csv/VendorTransformer.php b/app/Import/Transformer/Csv/VendorTransformer.php index 97356d9f66e7..fde473da01d7 100644 --- a/app/Import/Transformer/Csv/VendorTransformer.php +++ b/app/Import/Transformer/Csv/VendorTransformer.php @@ -52,17 +52,6 @@ class VendorTransformer extends BaseTransformer 'custom_value2' => $this->getString($data, 'vendor.custom_value2'), 'custom_value3' => $this->getString($data, 'vendor.custom_value3'), 'custom_value4' => $this->getString($data, 'vendor.custom_value4'), - // 'vendor_contacts' => [ - // [ - // 'first_name' => $this->getString( - // $data, - // 'vendor.first_name' - // ), - // 'last_name' => $this->getString($data, 'vendor.last_name'), - // 'email' => $this->getString($data, 'vendor.email'), - // 'phone' => $this->getString($data, 'vendor.phone'), - // ], - // ], 'contacts' => [ [ 'first_name' => $this->getString( @@ -70,7 +59,7 @@ class VendorTransformer extends BaseTransformer 'contact.first_name' ), 'last_name' => $this->getString($data, 'contact.last_name'), - 'email' => $this->getString($data, 'contact.email'), + 'email' => strlen($this->getString($data, 'contact.email')) > 1 ? $this->getString($data, 'contact.email') : $this->getString($data, 'vendor.email'), 'phone' => $this->getString($data, 'contact.phone'), 'custom_value1' => $this->getString( $data, diff --git a/app/Jobs/Quote/ZipQuotes.php b/app/Jobs/Quote/ZipQuotes.php index 75f94a27069f..bd4e15ff977d 100644 --- a/app/Jobs/Quote/ZipQuotes.php +++ b/app/Jobs/Quote/ZipQuotes.php @@ -80,7 +80,11 @@ class ZipQuotes implements ShouldQueue $path = $this->quotes->first()->client->quote_filepath($invitation); $this->quotes->each(function ($quote) { + + $quote->service()->createInvitations(); + (new CreateEntityPdf($quote->invitations()->first()))->handle(); + }); try { diff --git a/app/Listeners/Subscription/AppStoreRenewSubscription.php b/app/Listeners/Subscription/AppStoreRenewSubscription.php index aff9af2e84d7..958fd62929de 100644 --- a/app/Listeners/Subscription/AppStoreRenewSubscription.php +++ b/app/Listeners/Subscription/AppStoreRenewSubscription.php @@ -13,6 +13,8 @@ namespace App\Listeners\Subscription; use App\Libraries\MultiDB; use App\Models\Account; +use App\Models\Company; +use App\Notifications\Ninja\RenewalFailureNotification; use Illuminate\Contracts\Queue\ShouldQueue; use Imdhemy\Purchases\Events\AppStore\DidRenew; @@ -38,10 +40,18 @@ class AppStoreRenewSubscription implements ShouldQueue $inapp_transaction_id = $event->getSubscriptionId(); //$subscription_id + nlog("inapp upgrade processing for = {$inapp_transaction_id}"); + MultiDB::findAndSetDbByInappTransactionId($inapp_transaction_id); $account = Account::where('inapp_transaction_id', $inapp_transaction_id)->first(); + if(!$account) { + $ninja_company = Company::on('db-ninja-01')->find(config('ninja.ninja_default_company_id')); + $ninja_company->notification(new RenewalFailureNotification("{$inapp_transaction_id}"))->ninja(); + return; + } + if($account->plan_term == 'month') $account->plan_expires = now()->addMonth(); elseif($account->plan_term == 'year') diff --git a/app/Models/VendorContact.php b/app/Models/VendorContact.php index fc5288e417e9..75fb8a4eb692 100644 --- a/app/Models/VendorContact.php +++ b/app/Models/VendorContact.php @@ -145,7 +145,7 @@ class VendorContact extends Authenticatable implements HasLocalePreference { return $this ->withTrashed() - ->company() + // ->company() ->where('id', $this->decodePrimaryKey($value)) ->firstOrFail(); } diff --git a/app/Notifications/Ninja/ClientAccountNotFound.php b/app/Notifications/Ninja/ClientAccountNotFound.php index 2edddc242339..5a2418cedd2d 100644 --- a/app/Notifications/Ninja/ClientAccountNotFound.php +++ b/app/Notifications/Ninja/ClientAccountNotFound.php @@ -30,13 +30,7 @@ class ClientAccountNotFound extends Notification * @return void */ - - protected string $account_key; - - public function __construct(string $account_key) - { - $this->account_key = $account_key; - } + public function __construct(protected string $account_key, protected string $email){} /** * Get the notification's delivery channels. @@ -77,6 +71,7 @@ class ClientAccountNotFound extends Notification $content = "Client not found, unable to remove account\n"; $content .= "Account: {$this->account_key }\n"; + $content .= "Email: {$this->email}\n"; return (new SlackMessage) ->success() diff --git a/app/Notifications/Ninja/RenewalFailureNotification.php b/app/Notifications/Ninja/RenewalFailureNotification.php index 87bd1bd3ee06..5dbbbe8a6d05 100644 --- a/app/Notifications/Ninja/RenewalFailureNotification.php +++ b/app/Notifications/Ninja/RenewalFailureNotification.php @@ -68,7 +68,7 @@ class RenewalFailureNotification extends Notification public function toSlack($notifiable) { $content = "Plan paid, account not updated\n"; - $content .= "Contact: {$this->notification_message}"; + $content .= "Contact/Inapp Purchase: {$this->notification_message}"; return (new SlackMessage) ->success() diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index 5f6c14caa70d..7efa008f74c8 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -38,9 +38,14 @@ class UserObserver */ public function updated(User $user) { - // if (Ninja::isHosted() && $user->isDirty('phone')) { - // VerifyPhone::dispatch($user); - // } + + if (Ninja::isHosted() && $user->isDirty('email') && $user->company_users()->where('is_owner', true)->exists()) { + //ensure they are owner user and update email on file. + if(class_exists(\Modules\Admin\Jobs\Account\UpdateOwnerUser::class)) + \Modules\Admin\Jobs\Account\UpdateOwnerUser::dispatch($user->account->key, $user, $user->getOriginal('email')); + + } + } /** diff --git a/app/PaymentDrivers/SquarePaymentDriver.php b/app/PaymentDrivers/SquarePaymentDriver.php index 2a92d18321e7..eb4a0bfec9ba 100644 --- a/app/PaymentDrivers/SquarePaymentDriver.php +++ b/app/PaymentDrivers/SquarePaymentDriver.php @@ -99,7 +99,7 @@ class SquarePaymentDriver extends BaseDriver $amount_money = new \Square\Models\Money(); $amount_money->setAmount($this->convertAmount($amount)); - $amount_money->setCurrency($this->square_driver->client->currency()->code); + $amount_money->setCurrency($this->client->currency()->code); $body = new \Square\Models\RefundPaymentRequest(\Illuminate\Support\Str::random(32), $amount_money, $payment->transaction_reference); diff --git a/app/Services/ClientPortal/InstantPayment.php b/app/Services/ClientPortal/InstantPayment.php index 2df5e3ab6974..b1136f5d605b 100644 --- a/app/Services/ClientPortal/InstantPayment.php +++ b/app/Services/ClientPortal/InstantPayment.php @@ -193,7 +193,7 @@ class InstantPayment $payment_method_id = $this->request->input('payment_method_id'); $invoice_totals = $payable_invoices->sum('amount'); $first_invoice = $invoices->first(); - $credit_totals = $first_invoice->client->getSetting('use_credits_payment') == 'always' ? $first_invoice->client->service()->getCreditBalance() : 0; + $credit_totals = in_array($first_invoice->client->getSetting('use_credits_payment'), ['always', 'option']) ? $first_invoice->client->service()->getCreditBalance() : 0; $starting_invoice_amount = $first_invoice->balance; /* Schedule a job to check the gateway fees for this invoice*/ @@ -297,7 +297,7 @@ class InstantPayment } public function processCreditPayment(Request $request, array $data) - { + { return render('gateways.credit.index', $data); } } diff --git a/app/Services/Credit/CreditService.php b/app/Services/Credit/CreditService.php index 5970c42f2860..db02b1127d46 100644 --- a/app/Services/Credit/CreditService.php +++ b/app/Services/Credit/CreditService.php @@ -271,6 +271,7 @@ class CreditService public function restoreCredit() { + $this->credit ->client ->service() diff --git a/app/Services/Payment/DeletePayment.php b/app/Services/Payment/DeletePayment.php index bf5306005061..bd41bcd8af0e 100644 --- a/app/Services/Payment/DeletePayment.php +++ b/app/Services/Payment/DeletePayment.php @@ -138,7 +138,7 @@ class DeletePayment private function updateCreditables() { if ($this->payment->credits()->exists()) { - $this->payment->credits()->each(function ($paymentable_credit) { + $this->payment->credits()->where('is_deleted',0)->each(function ($paymentable_credit) { $multiplier = 1; if ($paymentable_credit->pivot->amount < 0) { diff --git a/app/Services/Subscription/SubscriptionService.php b/app/Services/Subscription/SubscriptionService.php index c7cb6dab9aca..c9862683f843 100644 --- a/app/Services/Subscription/SubscriptionService.php +++ b/app/Services/Subscription/SubscriptionService.php @@ -36,6 +36,7 @@ use App\Models\Subscription; use App\Models\SystemLog; use App\Repositories\CreditRepository; use App\Repositories\InvoiceRepository; +use App\Repositories\PaymentRepository; use App\Repositories\RecurringInvoiceRepository; use App\Repositories\SubscriptionRepository; use App\Services\Subscription\ZeroCostProduct; @@ -58,6 +59,8 @@ class SubscriptionService /** @var subscription */ private $subscription; + private float $credit_payments = 0; + public function __construct(Subscription $subscription) { $this->subscription = $subscription; @@ -530,13 +533,43 @@ class SubscriptionService ->orderBy('id', 'desc') ->first(); - if($this->calculateProRataRefundForSubscription($last_invoice) > 0) + //if last payment was created from a credit, do not generate a new credit, refund the old one. + + if($last_invoice) { + + + $last_invoice->payments->each(function ($payment){ + + $payment->credits()->where('is_deleted', 0)->each(function ($credit){ + + $this->credit_payments += $credit->pivot->sum('amount'); + + }); + + + }); + + $invoice_repo = new InvoiceRepository(); + + $invoice_repo->delete($last_invoice); + + $payment_repo = new PaymentRepository(new CreditRepository()); + + $last_invoice->payments->each(function ($payment) use ($payment_repo){ + $payment_repo->delete($payment); + }); + + } + + //if there are existing credit payments, then we refund directly to the credit. + if($this->calculateProRataRefundForSubscription($last_invoice) > 0 && $this->credit_payments == 0) $credit = $this->createCredit($last_invoice, $target_subscription, false); $new_recurring_invoice = $this->createNewRecurringInvoice($recurring_invoice); $invoice = $this->changePlanInvoice($target_subscription, $recurring_invoice->client_id); $invoice->recurring_id = $new_recurring_invoice->id; + $invoice->is_proforma = false; $invoice->save(); $payment = PaymentFactory::create($invoice->company_id, $invoice->user_id, $invoice->client_id); @@ -545,7 +578,7 @@ class SubscriptionService $payment->is_manual = true; $payment->save(); - $payment->service()->applyCreditsToInvoice($invoice); + $payment->service()->applyNumber()->applyCreditsToInvoice($invoice); $context = [ 'context' => 'change_plan', diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index 7af2a5d4b526..ad80141063d7 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -296,7 +296,7 @@ class HtmlEngine $data['$portal_url'] = ['value' => $this->invitation->getPortalLink(), 'label' =>'']; $data['$entity_number'] = &$data['$number']; - $data['$invoice.discount'] = ['value' => Number::formatMoney($this->entity_calc->getTotalDiscount(), $this->client) ?: ' ', 'label' => ($this->entity->is_amount_discount) ? ctrans('texts.discount') : ctrans('texts.discount').' '.$this->entity->discount.'%']; + $data['$invoice.discount'] = ['value' => Number::formatMoney($this->entity_calc->getTotalDiscount(), $this->client) ?: ' ', 'label' => ($this->entity->is_amount_discount) ? ctrans('texts.discount') : ctrans('texts.discount').' '.$this->entity->discount.'%']; $data['$discount'] = &$data['$invoice.discount']; $data['$subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.subtotal')]; $data['$gross_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getGrossSubTotal(), $this->client) ?: ' ', 'label' => ctrans('texts.subtotal')]; diff --git a/config/ninja.php b/config/ninja.php index 88cb863725bd..65340df466e0 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -14,8 +14,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.5.65', - 'app_tag' => '5.5.65', + 'app_version' => '5.5.66', + 'app_tag' => '5.5.66', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), diff --git a/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php b/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php index 3c88dbd7bc72..446bb79a74c8 100644 --- a/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/billing-portal-purchase.blade.php @@ -101,7 +101,7 @@