diff --git a/VERSION.txt b/VERSION.txt index b7e4264e5292..a1e5644bcb98 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.8.38 \ No newline at end of file +5.8.39 \ No newline at end of file diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 337ea4003119..914090fee954 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -168,7 +168,6 @@ class BaseRule implements RuleInterface /* We should only apply taxes for configured states */ if(!array_key_exists($this->client->country->iso_3166_2, $this->region_codes)) { nlog('Automatic tax calculations not supported for this country - defaulting to company country'); - nlog("With new logic, we should never see this"); } /** Harvest the client_region */ diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index b76ae7327605..3cb019e54d25 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -134,6 +134,7 @@ class BaseExport protected array $invoice_report_keys = [ 'name' => 'client.name', + "currency" => "client.currency_id", "invoice_number" => "invoice.number", "amount" => "invoice.amount", "balance" => "invoice.balance", @@ -174,6 +175,8 @@ class BaseExport ]; protected array $recurring_invoice_report_keys = [ + 'name' => 'client.name', + "currency" => "client.currency_id", "invoice_number" => "recurring_invoice.number", "amount" => "recurring_invoice.amount", "balance" => "recurring_invoice.balance", @@ -298,6 +301,8 @@ class BaseExport ]; protected array $quote_report_keys = [ + 'name' => 'client.name', + "currency" => "client.currency_id", 'custom_value1' => 'quote.custom_value1', 'custom_value2' => 'quote.custom_value2', 'custom_value3' => 'quote.custom_value3', @@ -336,6 +341,8 @@ class BaseExport ]; protected array $credit_report_keys = [ + 'name' => 'client.name', + "currency" => "client.currency_id", "credit_number" => "credit.number", "amount" => "credit.amount", "balance" => "credit.balance", @@ -368,6 +375,7 @@ class BaseExport ]; protected array $payment_report_keys = [ + 'name' => 'client.name', "date" => "payment.date", "amount" => "payment.amount", "refunded" => "payment.refunded", @@ -385,7 +393,6 @@ class BaseExport "custom_value4" => "payment.custom_value4", "user" => "payment.user_id", "assigned_user" => "payment.assigned_user_id", - ]; protected array $expense_report_keys = [ diff --git a/app/Export/CSV/TaskExport.php b/app/Export/CSV/TaskExport.php index f4f677197887..452b806ffaaa 100644 --- a/app/Export/CSV/TaskExport.php +++ b/app/Export/CSV/TaskExport.php @@ -138,9 +138,9 @@ class TaskExport extends BaseExport { $entity = []; $transformed_entity = $this->entity_transformer->transform($task); -nlog($this->input['report_keys']); + foreach (array_values($this->input['report_keys']) as $key) { -nlog($key); + $parts = explode('.', $key); if (is_array($parts) && $parts[0] == 'task' && array_key_exists($parts[1], $transformed_entity)) { diff --git a/app/Http/Controllers/ClientPortal/InvitationController.php b/app/Http/Controllers/ClientPortal/InvitationController.php index 7b5de6d26771..15efa85a4f4d 100644 --- a/app/Http/Controllers/ClientPortal/InvitationController.php +++ b/app/Http/Controllers/ClientPortal/InvitationController.php @@ -87,7 +87,7 @@ class InvitationController extends Controller ->firstOrFail(); if ($invitation->trashed() || $invitation->{$entity}->is_deleted) { - return $this->render('generic.not_available', ['account' => $invitation->company->account, 'company' => $invitation->company]); + return $this->render('generic.not_available', ['passed_account' => $invitation->company->account, 'passed_company' => $invitation->company]); } if ($invitation->contact->trashed()) { @@ -138,11 +138,10 @@ class InvitationController extends Controller return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent]); - return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent])->header('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); } + return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})]); - return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})])->header('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); } private function fireEntityViewedEvent($invitation, $entity_string) diff --git a/app/Http/Controllers/ClientPortal/SubscriptionPurchaseController.php b/app/Http/Controllers/ClientPortal/SubscriptionPurchaseController.php index ad0e0f212fb7..ffc5e517eeaf 100644 --- a/app/Http/Controllers/ClientPortal/SubscriptionPurchaseController.php +++ b/app/Http/Controllers/ClientPortal/SubscriptionPurchaseController.php @@ -26,7 +26,7 @@ class SubscriptionPurchaseController extends Controller App::setLocale($subscription->company->locale()); if ($subscription->trashed()) { - return $this->render('generic.not_available', ['account' => $subscription->company->account, 'company' => $subscription->company]); + return $this->render('generic.not_available', ['passed_account' => $subscription->company->account, 'passed_company' => $subscription->company]); } /* Make sure the contact is logged into the correct company for this subscription */ diff --git a/app/Http/Controllers/QuoteController.php b/app/Http/Controllers/QuoteController.php index 1ac67895f009..2f5f01bb3cb4 100644 --- a/app/Http/Controllers/QuoteController.php +++ b/app/Http/Controllers/QuoteController.php @@ -914,7 +914,7 @@ class QuoteController extends BaseController $contact = $invitation->contact; $quote = $invitation->quote; - $file = $quote->service()->getEInvoice($contact); + $file = $quote->service()->getEQuote($contact); $file_name = $quote->getFileName("xml"); $headers = ['Content-Type' => 'application/xml']; diff --git a/app/Http/Requests/Chart/ShowChartRequest.php b/app/Http/Requests/Chart/ShowChartRequest.php index 5af94a0651f3..3cc1283e6936 100644 --- a/app/Http/Requests/Chart/ShowChartRequest.php +++ b/app/Http/Requests/Chart/ShowChartRequest.php @@ -59,6 +59,7 @@ class ShowChartRequest extends Request } if (! isset($input['end_date'])) { + // $input['end_date'] = now()->lastOfMonth()->format('Y-m-d'); $input['end_date'] = now()->format('Y-m-d'); } diff --git a/app/Jobs/Cron/SubscriptionCron.php b/app/Jobs/Cron/SubscriptionCron.php index 42b61a2ddcd8..6e29ba088384 100644 --- a/app/Jobs/Cron/SubscriptionCron.php +++ b/app/Jobs/Cron/SubscriptionCron.php @@ -11,11 +11,12 @@ namespace App\Jobs\Cron; -use App\Libraries\MultiDB; +use App\Models\Company; use App\Models\Invoice; +use App\Libraries\MultiDB; +use Illuminate\Support\Facades\Auth; use App\Utils\Traits\SubscriptionHooker; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Support\Facades\Auth; class SubscriptionCron { @@ -97,4 +98,61 @@ class SubscriptionCron } } } + + //Requires the crons to be updated and set to hourly @ 00:01 + private function timezoneAware() + { + $grouped_company_ids = + + Invoice::select('company_id') + ->where('is_deleted', 0) + ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) + ->where('balance', '>', 0) + ->where('is_proforma', 0) + ->whereDate('due_date', '<=', now()->addDay()->startOfDay()) + ->whereNull('deleted_at') + ->whereNotNull('subscription_id') + ->groupBy('company_id') + ->cursor() + ->each(function ($company_id){ + + $company = Company::find($company_id); + + $timezone_now = now()->setTimezone($company->timezone()->name); + + //Capture companies within the window of 00:00 and 00:30 + if($timezone_now->gt($timezone_now->copy()->startOfDay()) && $timezone_now->lt($timezone_now->copy()->startOfDay()->addMinutes(30))) { + + Invoice::query() + ->where('company_id', $company->id) + ->whereNull('deleted_at') + ->where('is_deleted', 0) + ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) + ->where('is_proforma', 0) + ->whereNotNull('subscription_id') + ->where('balance', '>', 0) + ->whereDate('due_date', '<=', now()->setTimezone($company->timezone()->name)->addDay()->startOfDay()) + ->cursor() + ->each(function (Invoice $invoice) { + + $subscription = $invoice->subscription; + + $body = [ + 'context' => 'plan_expired', + 'client' => $invoice->client->hashed_id, + 'invoice' => $invoice->hashed_id, + 'subscription' => $subscription->hashed_id, + ]; + + $this->sendLoad($subscription, $body); + //This will send the notification daily. + //We'll need to handle this by performing some action on the invoice to either archive it or delete it? + }); + + } + + + }); + + } } diff --git a/app/Jobs/PostMark/ProcessPostmarkWebhook.php b/app/Jobs/PostMark/ProcessPostmarkWebhook.php index 1664cbafdcc7..c6cdd42545c2 100644 --- a/app/Jobs/PostMark/ProcessPostmarkWebhook.php +++ b/app/Jobs/PostMark/ProcessPostmarkWebhook.php @@ -359,6 +359,17 @@ class ProcessPostmarkWebhook implements ShouldQueue $postmark = new PostmarkClient($postmark_secret); $messageDetail = $postmark->getOutboundMessageDetails($message_id); + + try { + $messageDetail = $postmark->getOutboundMessageDetails($message_id); + } catch(\Exception $e) { + + $postmark_secret = config('services.postmark-outlook.token'); + $postmark = new PostmarkClient($postmark_secret); + $messageDetail = $postmark->getOutboundMessageDetails($message_id); + + } + return $messageDetail; } @@ -391,7 +402,17 @@ class ProcessPostmarkWebhook implements ShouldQueue $postmark_secret = !empty($this->company->settings->postmark_secret) ? $this->company->settings->postmark_secret : config('services.postmark.token'); $postmark = new PostmarkClient($postmark_secret); - $messageDetail = $postmark->getOutboundMessageDetails($this->request['MessageID']); + + try { + $messageDetail = $postmark->getOutboundMessageDetails($this->request['MessageID']); + } + catch(\Exception $e){ + + $postmark_secret = config('services.postmark-outlook.token'); + $postmark = new PostmarkClient($postmark_secret); + $messageDetail = $postmark->getOutboundMessageDetails($this->request['MessageID']); + + } $recipients = collect($messageDetail['recipients'])->flatten()->implode(','); $subject = $messageDetail->subject ?? ''; diff --git a/app/Jobs/Util/Import.php b/app/Jobs/Util/Import.php index 1326262ec297..d1281ebe1ee8 100644 --- a/app/Jobs/Util/Import.php +++ b/app/Jobs/Util/Import.php @@ -1525,10 +1525,11 @@ class Import implements ShouldQueue } } - // throw new Exception("Resource invoice/quote document not available."); } + $entity = false; + if (array_key_exists('expense_id', $resource) && $resource['expense_id'] && array_key_exists('expenses', $this->ids)) { $expense_id = $this->transformId('expenses', $resource['expense_id']); $entity = Expense::query()->where('id', $expense_id)->withTrashed()->first(); diff --git a/app/Livewire/PdfSlot.php b/app/Livewire/PdfSlot.php index 81ad1fab61c1..59fb9bf9625d 100644 --- a/app/Livewire/PdfSlot.php +++ b/app/Livewire/PdfSlot.php @@ -108,7 +108,7 @@ class PdfSlot extends Component } - public function downloadEInvoice() + public function downloadEDocument() { $file_name = $this->entity->numberFormatter().'.xml'; diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index 7e6f6b928c09..8585510e0073 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -790,19 +790,24 @@ class BaseDriver extends AbstractPaymentDriver 'client' => $this->client->present()->name(), ]); - return sprintf('%s: %s', ctrans('texts.invoices'), \implode(', ', collect($this->payment_hash->invoices())->pluck('invoice_number')->toArray())); + // return sprintf('%s: %s', ctrans('texts.invoices'), \implode(', ', collect($this->payment_hash->invoices())->pluck('invoice_number')->toArray())); } /** * Stub for disconnecting from the gateway. * - * @return void + * @return bool */ public function disconnect() { return true; } + /** + * Stub for checking authentication. + * + * @return bool + */ public function auth(): bool { return true; diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index 1b110cc0cde8..720155cab594 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -165,8 +165,10 @@ class StripePaymentDriver extends BaseDriver } if ($this->client - && isset($this->client->country) - && in_array($this->client->country->iso_3166_3, ['AUS', 'DNK', 'DEU', 'ITA', 'LUX', 'NOR', 'SVN', 'GBR', 'AUT', 'EST', 'GRC', 'JPN', 'MYS', 'PRT', 'ESP', 'USA', 'BEL', 'FIN', 'HKG', 'LVA', 'NLD', 'SGP', 'SWE', 'CAN', 'FRA', 'IRL', 'LTU', 'NZL', 'SVK', 'CHE'])) { + && $this->client->currency() + && in_array($this->client->currency()->code, ['CNY', 'AUD', 'CAD', 'EUR', 'GBP', 'HKD', 'JPY', 'SGD', 'MYR', 'NZD', 'USD'])) { + // && isset($this->client->country) + // && in_array($this->client->country->iso_3166_3, ['AUS', 'DNK', 'DEU', 'ITA', 'LUX', 'NOR', 'SVN', 'GBR', 'AUT', 'EST', 'GRC', 'JPN', 'MYS', 'PRT', 'ESP', 'USA', 'BEL', 'FIN', 'HKG', 'LVA', 'NLD', 'SGP', 'SWE', 'CAN', 'FRA', 'IRL', 'LTU', 'NZL', 'SVK', 'CHE'])) { $types[] = GatewayType::ALIPAY; } diff --git a/app/Services/Template/TemplateService.php b/app/Services/Template/TemplateService.php index d36e464efd89..4dcf064c63c6 100644 --- a/app/Services/Template/TemplateService.php +++ b/app/Services/Template/TemplateService.php @@ -271,7 +271,7 @@ class TemplateService { $this->data = $this->preProcessDataBlocks($data); - // nlog($this->data); + // nlog(json_encode($this->data)); return $this; } diff --git a/config/ninja.php b/config/ninja.php index 80ddd773f262..8334f6ff1c06 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -17,8 +17,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => env('APP_VERSION', '5.8.38'), - 'app_tag' => env('APP_TAG', '5.8.38'), + 'app_version' => env('APP_VERSION', '5.8.39'), + 'app_tag' => env('APP_TAG', '5.8.39'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), diff --git a/lang/fr_CA/texts.php b/lang/fr_CA/texts.php index def9b39677a1..303dcbd6a473 100644 --- a/lang/fr_CA/texts.php +++ b/lang/fr_CA/texts.php @@ -5265,6 +5265,8 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'enable_rappen_rounding_help' => 'Arrondir les totaux au 5 le plus proche', 'duration_words' => 'Durée en mots', 'upcoming_recurring_invoices' => 'Factures récurrentes à venir', + 'show_table_footer' => 'Afficher le pied du tableau', + 'show_table_footer_help' => 'Afficher les totaux dans le pied du tableau', 'total_invoices' => 'Total factures', ); diff --git a/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php b/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php index 314f69cca8ee..9284c748cb94 100644 --- a/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php @@ -10,9 +10,9 @@ @if($entity_type == 'invoice' && $settings->enable_e_invoice) - @endif @if($entity_type == 'credit' && $settings->enable_e_invoice) - @endif @if($entity_type == 'quote' && $settings->enable_e_invoice) -