From 4d4e3745f1a66f062fead3968d2e232b2aa06c02 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 19 Feb 2024 16:10:14 +1100 Subject: [PATCH 01/37] Fixes for company export paths --- app/Jobs/Company/CompanyExport.php | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/app/Jobs/Company/CompanyExport.php b/app/Jobs/Company/CompanyExport.php index 7736a3fdca6b..84ab3b307c5d 100644 --- a/app/Jobs/Company/CompanyExport.php +++ b/app/Jobs/Company/CompanyExport.php @@ -69,10 +69,9 @@ class CompanyExport implements ShouldQueue { MultiDB::setDb($this->company->db); - $this->file_name = date('Y-m-d') . '_' . str_replace([" ", "/"], ["_",""], $this->company->present()->name() . '_' . $this->company->company_key . '.json'); - $this->writer = new File($this->file_name); + $this->writer = new File(sys_get_temp_dir().'/'.$this->file_name); set_time_limit(0); @@ -114,8 +113,6 @@ class CompanyExport implements ShouldQueue return $user; })->all(); - - $x = $this->writer->collection('users'); $x->addItems($this->export_data['users']); $this->export_data = null; @@ -667,7 +664,7 @@ class CompanyExport implements ShouldQueue private function zipAndSend() { - $zip_path = \Illuminate\Support\Str::ascii(str_replace(".json", ".zip", $this->file_name)); + $zip_path = sys_get_temp_dir().'/'.\Illuminate\Support\Str::ascii(str_replace(".json", ".zip", $this->file_name)); $zip = new \ZipArchive(); @@ -675,8 +672,8 @@ class CompanyExport implements ShouldQueue nlog("cannot open {$zip_path}"); } - $zip->addFile($this->file_name); - $zip->renameName($this->file_name, 'backup.json'); + $zip->addFile(sys_get_temp_dir().'/'.$this->file_name, 'backup.json'); + // $zip->renameName($this->file_name, 'backup.json'); $zip->close(); @@ -686,8 +683,8 @@ class CompanyExport implements ShouldQueue unlink($zip_path); } - if(file_exists($this->file_name)) { - unlink($this->file_name); + if(file_exists(sys_get_temp_dir().'/'.$this->file_name)) { + unlink(sys_get_temp_dir().'/'.$this->file_name); } if(Ninja::isSelfHost()) { @@ -717,8 +714,8 @@ class CompanyExport implements ShouldQueue if (Ninja::isHosted()) { sleep(3); - if(file_exists($zip_path)) { - unlink($zip_path); + if(file_exists(sys_get_temp_dir().'/'.$zip_path)) { + unlink(sys_get_temp_dir().'/'.$zip_path); } } } From e87c6187785c006bdf5992d821d5f6ef8e0ce8a5 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 19 Feb 2024 17:28:15 +1100 Subject: [PATCH 02/37] Updated 2FA error message response --- app/Http/Controllers/SmtpController.php | 2 -- app/Http/Controllers/TwoFactorController.php | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/SmtpController.php b/app/Http/Controllers/SmtpController.php index 5588c668edb0..b28275e62fff 100644 --- a/app/Http/Controllers/SmtpController.php +++ b/app/Http/Controllers/SmtpController.php @@ -30,8 +30,6 @@ class SmtpController extends BaseController $user = auth()->user(); $company = $user->company(); - - config([ 'mail.mailers.smtp' => [ 'transport' => 'smtp', diff --git a/app/Http/Controllers/TwoFactorController.php b/app/Http/Controllers/TwoFactorController.php index 083aea6ac011..282b41025879 100644 --- a/app/Http/Controllers/TwoFactorController.php +++ b/app/Http/Controllers/TwoFactorController.php @@ -72,6 +72,10 @@ class TwoFactorController extends BaseController return response()->json(['message' => ctrans('texts.enabled_two_factor')], 200); } elseif (! $secret || ! $google2fa->verifyKey($secret, $oneTimePassword)) { return response()->json(['message' => ctrans('texts.invalid_one_time_password')], 400); + }elseif (! $user->phone) { + return response()->json(['message' => ctrans('texts.set_phone_for_two_factor')], 400); + } elseif (! $user->isVerified()) { + return response()->json(['message' => 'Please confirm your account first'], 400); } return response()->json(['message' => 'No phone record or user is not confirmed'], 400); From 25df7eacad96a89d4c297eb79486353f361abe60 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Feb 2024 07:28:53 +1100 Subject: [PATCH 03/37] Fixes for missing variables prop in pre payments --- app/Http/Controllers/ClientPortal/InvoiceController.php | 1 - app/Http/Controllers/ClientPortal/PrePaymentController.php | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index 2fb5ea47d8b2..95135856d4ab 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -236,7 +236,6 @@ class InvoiceController extends Controller 'hashed_ids' => $invoices->pluck('hashed_id'), 'total' => $total, 'variables' => $variables, - ]; return $this->render('invoices.payment', $data); diff --git a/app/Http/Controllers/ClientPortal/PrePaymentController.php b/app/Http/Controllers/ClientPortal/PrePaymentController.php index 2d80027f0650..a2abb81dfb44 100644 --- a/app/Http/Controllers/ClientPortal/PrePaymentController.php +++ b/app/Http/Controllers/ClientPortal/PrePaymentController.php @@ -88,6 +88,8 @@ class PrePaymentController extends Controller $total = $invoice->balance; + $invitation = $invoice->invitations->first(); + //format totals $formatted_total = Number::formatMoney($invoice->amount, auth()->guard('contact')->user()->client); @@ -113,6 +115,8 @@ class PrePaymentController extends Controller 'frequency_id' => $request->frequency_id, 'remaining_cycles' => $request->remaining_cycles, 'is_recurring' => $request->is_recurring == 'on' ? true : false, + 'variables' => $variables = ($invitation && auth()->guard('contact')->user()->client->getSetting('show_accept_invoice_terms')) ? (new HtmlEngine($invitation))->generateLabelsAndValues() : false, + ]; return $this->render('invoices.payment', $data); From 328d00e2357104f2ce7650edaa1211bb308de536 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Feb 2024 13:29:30 +1100 Subject: [PATCH 04/37] Improve parseFloat function --- .../ClientPortal/PrePaymentController.php | 13 ++--- app/Utils/Number.php | 53 +++++++++++++------ tests/Unit/NumberTest.php | 48 +++++++++++++++++ 3 files changed, 92 insertions(+), 22 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/PrePaymentController.php b/app/Http/Controllers/ClientPortal/PrePaymentController.php index a2abb81dfb44..643a05aba46d 100644 --- a/app/Http/Controllers/ClientPortal/PrePaymentController.php +++ b/app/Http/Controllers/ClientPortal/PrePaymentController.php @@ -12,16 +12,17 @@ namespace App\Http\Controllers\ClientPortal; +use App\Utils\Number; +use App\Utils\HtmlEngine; +use Illuminate\View\View; use App\DataMapper\InvoiceItem; use App\Factory\InvoiceFactory; -use App\Http\Controllers\Controller; -use App\Http\Requests\ClientPortal\PrePayments\StorePrePaymentRequest; -use App\Repositories\InvoiceRepository; -use App\Utils\Number; -use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesHash; +use App\Utils\Traits\MakesDates; +use App\Http\Controllers\Controller; use Illuminate\Contracts\View\Factory; -use Illuminate\View\View; +use App\Repositories\InvoiceRepository; +use App\Http\Requests\ClientPortal\PrePayments\StorePrePaymentRequest; /** * Class PrePaymentController. diff --git a/app/Utils/Number.php b/app/Utils/Number.php index 2fa7e64a462a..ca7a8ad59eca 100644 --- a/app/Utils/Number.php +++ b/app/Utils/Number.php @@ -98,28 +98,49 @@ class Number if(!$value) return 0; - $multiplier = false; + //remove everything except for numbers, decimals, commas and hyphens + $value = preg_replace('/[^0-9.,-]+/', '', $value); - if(substr($value, 0,1) == '-') - $multiplier = -1; + $decimal = strpos($value, '.'); + $comma = strpos($value, ','); + + if(!$comma) //no comma must be a decimal number already + return (float) $value; - // convert "," to "." - $s = str_replace(',', '.', $value); - - // remove everything except numbers and dot "." - $s = preg_replace("/[^0-9\.]/", '', $s); - - if ($s < 1) { - return (float) $s; + if($decimal < $comma){ //decimal before a comma = euro + $value = str_replace(['.',','], ['','.'], $value); + // $value = str_replace(',', '.', $value); + return (float) $value; } - // remove all separators from first part and keep the end - $s = str_replace('.', '', substr($s, 0, -3)).substr($s, -3); + //comma first = traditional thousan separator + $value = str_replace(',', '', $value); + + return (float)$value; + + + // if(!$value) + // return 0; - if($multiplier) - $s = floatval($s)*-1; + // $multiplier = false; - return (float) $s; + // if(substr($value, 0,1) == '-') + // $multiplier = -1; + + // $s = str_replace(',', '.', $value); + + // $s = preg_replace("/[^0-9\.]/", '', $s); + + // if ($s < 1) { + // return (float) $s; + // } + + // $s = str_replace('.', '', substr($s, 0, -3)).substr($s, -3); + + // if($multiplier) + // $s = floatval($s)*-1; + + // return (float) $s; } public static function parseStringFloat($value) diff --git a/tests/Unit/NumberTest.php b/tests/Unit/NumberTest.php index cb77451996d0..5da712ccee3a 100644 --- a/tests/Unit/NumberTest.php +++ b/tests/Unit/NumberTest.php @@ -20,6 +20,36 @@ use Tests\TestCase; */ class NumberTest extends TestCase { + public function testNegativeFloatParse() + { + + $value = '-22,00'; + + $res = Number::parseFloat($value); + + $this->assertEquals(-22.0, $res); + + $value = '-22.00'; + + $res = Number::parseFloat($value); + + $this->assertEquals(-22.0, $res); + + $value = '-2200,00'; + + $res = Number::parseFloat($value); + + $this->assertEquals(-2200.0, $res); + + $value = '-2.200,00'; + + $res = Number::parseFloat($value); + + $this->assertEquals(-2200.0, $res); + $this->assertEquals(-2200, $res); + + + } public function testConvertDecimalCommaFloats() { @@ -123,4 +153,22 @@ class NumberTest extends TestCase $this->assertEquals(7.99, $converted_amount); } + + public function testMultiCommaNumber() + { + $amount = '100,100.00'; + + $converted_amount = Number::parseFloat($amount); + + $this->assertEquals(100100, $converted_amount); + } + + public function testMultiDecimalNumber() + { + $amount = '100.1000.000,00'; + + $converted_amount = Number::parseFloat($amount); + + $this->assertEquals(1001000000, $converted_amount); + } } From 6fcd321ce2069e37698ce0c0414e4df4143a0ef7 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Feb 2024 15:09:21 +1100 Subject: [PATCH 05/37] Additional tests --- tests/Unit/NumberTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/NumberTest.php b/tests/Unit/NumberTest.php index 5da712ccee3a..feecae8abf7d 100644 --- a/tests/Unit/NumberTest.php +++ b/tests/Unit/NumberTest.php @@ -46,9 +46,9 @@ class NumberTest extends TestCase $res = Number::parseFloat($value); $this->assertEquals(-2200.0, $res); + $this->assertEquals(-2200, $res); - } public function testConvertDecimalCommaFloats() From b7bfb4ee0cd2c9f1e2e73e0cb869695fcaaab27f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Feb 2024 19:01:50 +1100 Subject: [PATCH 06/37] Set exchange rate to inverse for invoices --- app/Services/Invoice/InvoiceService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index 8f0116bbce52..effe34daceba 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -90,7 +90,7 @@ class InvoiceService if ($company_currency != $client_currency) { $exchange_rate = new CurrencyApi(); - $this->invoice->exchange_rate = $exchange_rate->exchangeRate($client_currency, $company_currency, now()); + $this->invoice->exchange_rate = 1/$exchange_rate->exchangeRate($client_currency, $company_currency, now()); } return $this; From 6f495a77fa6a105783a8f6878fcdf644b7b1864d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Feb 2024 21:16:00 +1100 Subject: [PATCH 07/37] Updates for exchange rates --- app/Services/Report/ProfitLoss.php | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/Services/Report/ProfitLoss.php b/app/Services/Report/ProfitLoss.php index 03fc029043d8..f393d31621d9 100644 --- a/app/Services/Report/ProfitLoss.php +++ b/app/Services/Report/ProfitLoss.php @@ -286,21 +286,21 @@ class ProfitLoss $pivot_diff = $pivot->amount - $pivot->refunded; $amount_payment_paid += $pivot_diff; - $amount_payment_paid_converted += $pivot_diff / ($payment->exchange_rate ?: 1); + $amount_payment_paid_converted += $pivot_diff * ($payment->exchange_rate ?: 1); if ($invoice->amount > 0) { $tax_amount += ($pivot_diff / $invoice->amount) * $invoice->total_taxes; - $tax_amount_converted += (($pivot_diff / $invoice->amount) * $invoice->total_taxes) / $payment->exchange_rate; + $tax_amount_converted += (($pivot_diff / $invoice->amount) * $invoice->total_taxes) / $invoice->exchange_rate; } } if ($pivot->paymentable_type == 'credits') { $amount_credit_paid += $pivot->amount - $pivot->refunded; - $amount_credit_paid_converted += $pivot_diff / ($payment->exchange_rate ?: 1); + $amount_credit_paid_converted += $pivot_diff * ($payment->exchange_rate ?: 1); $tax_amount_credit += ($pivot_diff / $invoice->amount) * $invoice->total_taxes; - $tax_amount_credit_converted += (($pivot_diff / $invoice->amount) * $invoice->total_taxes) / $payment->exchange_rate; + $tax_amount_credit_converted += (($pivot_diff / $invoice->amount) * $invoice->total_taxes) / $invoice->exchange_rate; } } @@ -316,6 +316,7 @@ class ProfitLoss $this->invoice_payment_map[] = $map; }); +nlog($this->invoice_payment_map); return $this; } @@ -421,6 +422,11 @@ class ProfitLoss private function expenseData() { $expenses = Expense::query()->where('company_id', $this->company->id) + ->where(function ($query){ + $query->whereNull('client_id')->orWhereHas('client', function ($q){ + $q->where('is_deleted', 0); + }); + }) ->where('is_deleted', 0) ->withTrashed() ->whereBetween('date', [$this->start_date, $this->end_date]) @@ -435,7 +441,7 @@ class ProfitLoss $map->total = $expense->amount; $map->converted_total = $converted_total = $this->getConvertedTotal($expense->amount, $expense->exchange_rate); - $map->tax = $tax = $this->getTax($expense); + $map->tax = $tax = $this->getConvertedTotal($this->getTax($expense), $expense->exchange_rate); $map->net_converted_total = $expense->uses_inclusive_taxes ? ($converted_total - $tax) : $converted_total; $map->category_id = $expense->category_id; $map->category_name = $expense->category ? $expense->category->name : 'No Category Defined'; @@ -480,10 +486,6 @@ class ProfitLoss //is amount tax if ($expense->calculate_tax_by_amount) { - nlog($expense->tax_amount1); - nlog($expense->tax_amount2); - nlog($expense->tax_amount3); - return $expense->tax_amount1 + $expense->tax_amount2 + $expense->tax_amount3; } From a2f93f375480db5312679d77f0d6226e941a4473 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Feb 2024 08:46:52 +1100 Subject: [PATCH 08/37] Adjustments for invoicesum --- app/Helpers/Invoice/InvoiceSum.php | 4 +- app/Helpers/Invoice/InvoiceSumInclusive.php | 4 +- tests/Unit/InvoiceBalanceTest.php | 241 ++++++++++++++++++++ 3 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 tests/Unit/InvoiceBalanceTest.php diff --git a/app/Helpers/Invoice/InvoiceSum.php b/app/Helpers/Invoice/InvoiceSum.php index 349d0271c36c..2aeb3e962194 100644 --- a/app/Helpers/Invoice/InvoiceSum.php +++ b/app/Helpers/Invoice/InvoiceSum.php @@ -242,9 +242,9 @@ class InvoiceSum if ($this->invoice->status_id != Invoice::STATUS_DRAFT) { if ($this->invoice->amount != $this->invoice->balance) { - $paid_to_date = $this->invoice->amount - $this->invoice->balance; + // $paid_to_date = $this->invoice->amount - $this->invoice->balance; - $this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision) - $paid_to_date; + $this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision) - $this->invoice->paid_to_date; } else { $this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision); } diff --git a/app/Helpers/Invoice/InvoiceSumInclusive.php b/app/Helpers/Invoice/InvoiceSumInclusive.php index 9afbe1c76db7..8a7df5e68def 100644 --- a/app/Helpers/Invoice/InvoiceSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceSumInclusive.php @@ -259,9 +259,9 @@ class InvoiceSumInclusive /* If amount != balance then some money has been paid on the invoice, need to subtract this difference from the total to set the new balance */ if ($this->invoice->status_id != Invoice::STATUS_DRAFT) { if ($this->invoice->amount != $this->invoice->balance) { - $paid_to_date = $this->invoice->amount - $this->invoice->balance; + // $paid_to_date = $this->invoice->amount - $this->invoice->balance; - $this->invoice->balance = $this->formatValue($this->getTotal(), $this->precision) - $paid_to_date; + $this->invoice->balance = $this->formatValue($this->getTotal(), $this->precision) - $this->invoice->paid_to_date; } else { $this->invoice->balance = $this->formatValue($this->getTotal(), $this->precision); } diff --git a/tests/Unit/InvoiceBalanceTest.php b/tests/Unit/InvoiceBalanceTest.php new file mode 100644 index 000000000000..d3e31b6479ad --- /dev/null +++ b/tests/Unit/InvoiceBalanceTest.php @@ -0,0 +1,241 @@ +makeTestData(); + } + + public function testInvoiceBalances() + { + + $item = new InvoiceItem(); + $item->quantity = 1; + $item->cost = 100; + $item->type_id = '1'; + + $i = Invoice::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + 'line_items' => [$item], + 'status_id' => 1, + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'discount' => 0, + 'paid_to_date' => 0, + ]); + + + $this->assertEquals(1, $i->status_id); + + $i = $i->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(100, $i->amount); + $this->assertEquals(100, $i->balance); + $this->assertEquals(2, $i->status_id); + $this->assertEquals(0, $i->paid_to_date); + + + $item = new InvoiceItem(); + $item->quantity = 1; + $item->cost = 30.37; + $item->type_id = '1'; + + $i->line_items = [$item]; + + $i = $i->calc()->getInvoice(); + + // nlog($i->withoutRelations()->toArray()); + + $this->assertEquals(30.37, $i->amount); + $this->assertEquals(30.37, $i->balance); + $this->assertEquals(2, $i->status_id); + $this->assertEquals(0, $i->paid_to_date); + + $i = $i->service()->applyPaymentAmount(10.37, 'paid')->save(); + + // nlog($i->withoutRelations()->toArray()); + + $this->assertEquals(30.37, $i->amount); + $this->assertEquals(20.00, $i->balance); + $this->assertEquals(3, $i->status_id); + $this->assertEquals(10.37, $i->paid_to_date); + + $item = new InvoiceItem(); + $item->quantity = 1; + $item->cost = 15; + $item->type_id = '1'; + + $i->line_items = [$item]; + + $i = $i->calc()->getInvoice(); + + $this->assertEquals(15, $i->amount); + $this->assertEquals(15-10.37, $i->balance); + $this->assertEquals(3, $i->status_id); + $this->assertEquals(10.37, $i->paid_to_date); + + + } + + + public function testInvoiceBalancesWithNegatives() + { + + $item = new InvoiceItem(); + $item->quantity = 1; + $item->cost = -100; + $item->type_id = '1'; + + $i = Invoice::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + 'line_items' => [$item], + 'status_id' => 1, + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'discount' => 0, + 'paid_to_date' => 0, + ]); + + + $this->assertEquals(1, $i->status_id); + + $i = $i->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(-100, $i->amount); + $this->assertEquals(-100, $i->balance); + $this->assertEquals(2, $i->status_id); + $this->assertEquals(0, $i->paid_to_date); + + + $item = new InvoiceItem(); + $item->quantity = 1; + $item->cost = -30.37; + $item->type_id = '1'; + + $i->line_items = [$item]; + + $i = $i->calc()->getInvoice(); + + $this->assertEquals(-30.37, $i->amount); + $this->assertEquals(-30.37, $i->balance); + $this->assertEquals(2, $i->status_id); + $this->assertEquals(0, $i->paid_to_date); + + $i = $i->service()->markPaid()->save(); + + $this->assertEquals(0, $i->balance); + $this->assertEquals(-30.37, $i->paid_to_date); + } + + + + + public function testPurchaseOrderBalances() + { + + $item = new InvoiceItem(); + $item->quantity = 1; + $item->cost = 100; + $item->type_id = '1'; + + $i = PurchaseOrder::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'vendor_id' => $this->vendor->id, + 'line_items' => [$item], + 'status_id' => 1, + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'discount' => 0, + 'paid_to_date' => 0, + ]); + + $this->assertEquals(1, $i->status_id); + + $i = $i->calc()->getPurchaseOrder(); + $i = $i->service()->markSent()->save(); + + $this->assertEquals(100, $i->amount); + $this->assertEquals(100, $i->balance); + $this->assertEquals(2, $i->status_id); + $this->assertEquals(0, $i->paid_to_date); + + + $item = new InvoiceItem(); + $item->quantity = 1; + $item->cost = 30.37; + $item->type_id = '1'; + + $i->line_items = [$item]; + + $i = $i->calc()->getPurchaseOrder(); + $i = $i->service()->markSent()->save(); + + $this->assertEquals(30.37, $i->amount); + $this->assertEquals(30.37, $i->balance); + $this->assertEquals(2, $i->status_id); + $this->assertEquals(0, $i->paid_to_date); + + $item = new InvoiceItem(); + $item->quantity = 1; + $item->cost =10.37; + $item->type_id = '1'; + + $i->line_items = [$item]; + + $i = $i->calc()->getPurchaseOrder(); + $i = $i->service()->markSent()->save(); + + $this->assertEquals(10.37, $i->amount); + $this->assertEquals(10.37, $i->balance); + $this->assertEquals(2, $i->status_id); + $this->assertEquals(0, $i->paid_to_date); + + + + } + +} From 1f1716a3cdd8881105656ad2472d13616ac7c6ba Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Feb 2024 13:58:08 +1100 Subject: [PATCH 09/37] Updates for profit/loss reporting --- app/Services/Report/ProfitLoss.php | 39 ++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/app/Services/Report/ProfitLoss.php b/app/Services/Report/ProfitLoss.php index f393d31621d9..3364eeb8c526 100644 --- a/app/Services/Report/ProfitLoss.php +++ b/app/Services/Report/ProfitLoss.php @@ -316,7 +316,6 @@ class ProfitLoss $this->invoice_payment_map[] = $map; }); -nlog($this->invoice_payment_map); return $this; } @@ -341,6 +340,10 @@ nlog($this->invoice_payment_map); */ public function getCsv() { + nlog($this->income); + nlog($this->income_taxes); + nlog(array_sum(array_column($this->expense_break_down, 'total'))); + MultiDB::setDb($this->company->db); App::forgetInstance('translator'); App::setLocale($this->company->locale()); @@ -357,7 +360,7 @@ nlog($this->invoice_payment_map); $csv->insertOne(['--------------------']); - $csv->insertOne([ctrans('texts.total_revenue'), Number::formatMoney($this->income, $this->company)]); + $csv->insertOne([ctrans('texts.total_revenue'). "[".ctrans('texts.tax')." " .ctrans('texts.exclusive'). "]", Number::formatMoney($this->income, $this->company)]); //total taxes @@ -372,12 +375,12 @@ nlog($this->invoice_payment_map); //total expense taxes $csv->insertOne(['--------------------']); - $csv->insertOne([ctrans('texts.total_expenses'), Number::formatMoney(array_sum(array_column($this->expense_break_down, 'total')), $this->company)]); + $csv->insertOne([ctrans('texts.total_expenses'). "[".ctrans('texts.tax')." " .ctrans('texts.exclusive'). "]", Number::formatMoney(array_sum(array_column($this->expense_break_down, 'total')), $this->company)]); $csv->insertOne([ctrans('texts.total_taxes'), Number::formatMoney(array_sum(array_column($this->expense_break_down, 'tax')), $this->company)]); $csv->insertOne(['--------------------']); - $csv->insertOne([ctrans('texts.total_profit'), Number::formatMoney($this->income - $this->income_taxes - array_sum(array_column($this->expense_break_down, 'total')) - array_sum(array_column($this->expense_break_down, 'tax')), $this->company)]); + $csv->insertOne([ctrans('texts.total_profit'), Number::formatMoney($this->income - array_sum(array_column($this->expense_break_down, 'total')), $this->company)]); //net profit @@ -385,11 +388,25 @@ nlog($this->invoice_payment_map); $csv->insertOne(['']); $csv->insertOne(['']); + + $csv->insertOne(['--------------------']); + $csv->insertOne([ctrans('texts.revenue')]); + $csv->insertOne(['--------------------']); + $csv->insertOne([ctrans('texts.currency'), ctrans('texts.amount'), ctrans('texts.total_taxes')]); foreach ($this->foreign_income as $foreign_income) { $csv->insertOne([$foreign_income['currency'], ($foreign_income['amount'] - $foreign_income['total_taxes']), $foreign_income['total_taxes']]); } + $csv->insertOne(['']); + $csv->insertOne(['']); + $csv->insertOne(['--------------------']); + $csv->insertOne([ctrans('texts.expenses')]); + $csv->insertOne(['--------------------']); + foreach($this->expenses as $expense){ + $csv->insertOne([$expense->currency, ($expense->total - $expense->foreign_tax_amount), $expense->foreign_tax_amount]); + } + return $csv->toString(); } @@ -434,19 +451,21 @@ nlog($this->invoice_payment_map); $this->expenses = []; + $company_currency_code = $this->company->currency()->code; + foreach ($expenses as $expense) { $map = new \stdClass(); - $amount = $expense->amount; - + $expense_tax_total = $this->getTax($expense); $map->total = $expense->amount; - $map->converted_total = $converted_total = $this->getConvertedTotal($expense->amount, $expense->exchange_rate); - $map->tax = $tax = $this->getConvertedTotal($this->getTax($expense), $expense->exchange_rate); - $map->net_converted_total = $expense->uses_inclusive_taxes ? ($converted_total - $tax) : $converted_total; + $map->converted_total = $converted_total = $this->getConvertedTotal($expense->amount, $expense->exchange_rate); //converted to company currency + $map->tax = $tax = $this->getConvertedTotal($expense_tax_total, $expense->exchange_rate); //tax component + $map->net_converted_total = $expense->uses_inclusive_taxes ? ($converted_total - $tax) : $converted_total; //excludes all taxes $map->category_id = $expense->category_id; $map->category_name = $expense->category ? $expense->category->name : 'No Category Defined'; $map->currency_id = $expense->currency_id ?: $expense->company->settings->currency_id; - + $map->currency = $expense->currency ? $expense->currency->code : $company_currency_code; + $map->foreign_tax_amount = $expense_tax_total; $this->expenses[] = $map; } From efa6743edff28c979f9af5ec4e0801622ec32ef4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Feb 2024 14:01:52 +1100 Subject: [PATCH 10/37] Adjustments for reports --- app/Services/Report/ARDetailReport.php | 3 +++ app/Services/Report/ARSummaryReport.php | 2 +- app/Services/Report/TaxSummaryReport.php | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/Services/Report/ARDetailReport.php b/app/Services/Report/ARDetailReport.php index f63f16e60761..6e5f6869fe86 100644 --- a/app/Services/Report/ARDetailReport.php +++ b/app/Services/Report/ARDetailReport.php @@ -90,6 +90,9 @@ class ARDetailReport extends BaseExport $query = Invoice::query() ->withTrashed() + ->whereHas('client', function ($query){ + $query->where('is_deleted', 0); + }) ->where('company_id', $this->company->id) ->where('is_deleted', 0) ->where('balance', '>', 0) diff --git a/app/Services/Report/ARSummaryReport.php b/app/Services/Report/ARSummaryReport.php index 04edfe0f5701..7dc41d73aef2 100644 --- a/app/Services/Report/ARSummaryReport.php +++ b/app/Services/Report/ARSummaryReport.php @@ -125,9 +125,9 @@ class ARSummaryReport extends BaseExport $amount = Invoice::withTrashed() ->where('client_id', $this->client->id) ->where('company_id', $this->client->company_id) + ->where('is_deleted', 0) ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) ->where('balance', '>', 0) - ->where('is_deleted', 0) ->where(function ($query) { $query->where('due_date', '>', now()->startOfDay()) ->orWhereNull('due_date'); diff --git a/app/Services/Report/TaxSummaryReport.php b/app/Services/Report/TaxSummaryReport.php index 5a856eecd2c9..9e2e6a62cdc7 100644 --- a/app/Services/Report/TaxSummaryReport.php +++ b/app/Services/Report/TaxSummaryReport.php @@ -75,8 +75,8 @@ class TaxSummaryReport extends BaseExport $query = Invoice::query() ->withTrashed() - ->whereIn('status_id', [2,3,4]) ->where('company_id', $this->company->id) + ->whereIn('status_id', [2,3,4]) ->where('is_deleted', 0) ->orderBy('balance', 'desc'); From 440d1a2afbdf362821dcfd20779cac5db9f5a063 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 22 Feb 2024 07:45:06 +1100 Subject: [PATCH 11/37] update commit notes --- app/Helpers/Invoice/InvoiceSum.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Helpers/Invoice/InvoiceSum.php b/app/Helpers/Invoice/InvoiceSum.php index 2aeb3e962194..1d05bcef7eee 100644 --- a/app/Helpers/Invoice/InvoiceSum.php +++ b/app/Helpers/Invoice/InvoiceSum.php @@ -244,7 +244,7 @@ class InvoiceSum if ($this->invoice->amount != $this->invoice->balance) { // $paid_to_date = $this->invoice->amount - $this->invoice->balance; - $this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision) - $this->invoice->paid_to_date; + $this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision) - $this->invoice->paid_to_date; //21-02-2024 cannot use the calculated $paid_to_date here as it could send the balance backward. } else { $this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision); } From a32452f443b6d5b1f66af9ca4ef50b18813e6924 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 22 Feb 2024 09:38:53 +1100 Subject: [PATCH 12/37] Fixes for user filter sort by --- app/Filters/UserFilters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Filters/UserFilters.php b/app/Filters/UserFilters.php index c745f498e302..002562933ad9 100644 --- a/app/Filters/UserFilters.php +++ b/app/Filters/UserFilters.php @@ -50,7 +50,7 @@ class UserFilters extends QueryFilters { $sort_col = explode('|', $sort); - if (!is_array($sort_col) || count($sort_col) != 2) { + if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col, \Illuminate\Support\Facades\Schema::getColumnListing('users'))) { return $this->builder; } From 18bd5bae148ec09fdd4b5c96e83973fd2753ce26 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 22 Feb 2024 15:04:05 +1100 Subject: [PATCH 13/37] Updated translations --- lang/ar/texts.php | 23 ++++++++++++++++++---- lang/ca/texts.php | 23 ++++++++++++++++++---- lang/cs/texts.php | 23 ++++++++++++++++++---- lang/da/texts.php | 23 ++++++++++++++++++---- lang/de/texts.php | 37 +++++++++++++++++++++++++----------- lang/en/texts.php | 3 +++ lang/es/texts.php | 23 ++++++++++++++++++---- lang/es_ES/texts.php | 45 +++++++++++++++++++++++++++++--------------- lang/fr/texts.php | 23 ++++++++++++++++++---- lang/fr_CA/texts.php | 25 +++++++++++++++++++----- lang/fr_CH/texts.php | 23 ++++++++++++++++++---- lang/he/texts.php | 23 ++++++++++++++++++---- lang/hu/texts.php | 23 ++++++++++++++++++---- lang/it/texts.php | 23 ++++++++++++++++++---- lang/ja/texts.php | 23 ++++++++++++++++++---- lang/km_KH/texts.php | 23 ++++++++++++++++++---- lang/lo_LA/texts.php | 23 ++++++++++++++++++---- lang/nl/texts.php | 23 ++++++++++++++++++---- lang/pl/texts.php | 23 ++++++++++++++++++---- lang/pt_BR/texts.php | 23 ++++++++++++++++++---- lang/pt_PT/texts.php | 23 ++++++++++++++++++---- lang/ro/texts.php | 23 ++++++++++++++++++---- lang/sk/texts.php | 23 ++++++++++++++++++---- lang/sr/texts.php | 23 ++++++++++++++++++---- lang/sv/texts.php | 23 ++++++++++++++++++---- lang/zh_TW/texts.php | 23 ++++++++++++++++++---- 26 files changed, 497 insertions(+), 119 deletions(-) diff --git a/lang/ar/texts.php b/lang/ar/texts.php index 6956714a46ff..1d97f0f5c811 100644 --- a/lang/ar/texts.php +++ b/lang/ar/texts.php @@ -3849,7 +3849,7 @@ $lang = array( 'cancellation_pending' => 'الإلغاء معلق ، سنكون على اتصال!', 'list_of_payments' => 'قائمة المدفوعات', 'payment_details' => 'تفاصيل الدفع', - 'list_of_payment_invoices' => 'قائمة الفواتير المتأثرة بالدفع', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'قائمة طرق الدفع', 'payment_method_details' => 'تفاصيل طريقة الدفع', 'permanently_remove_payment_method' => 'قم بإزالة طريقة الدفع هذه بشكل دائم.', @@ -4906,7 +4906,7 @@ $lang = array( 'no_assigned_tasks' => 'لا توجد مهام قابلة للفوترة لهذا المشروع', 'authorization_failure' => 'أذونات غير كافية لتنفيذ هذا الإجراء', 'authorization_sms_failure' => 'يرجى التحقق من حسابك لإرسال رسائل البريد الإلكتروني.', - 'white_label_body' => 'شكرا لشرائك رخصة البطاقة البيضاء.

مفتاح الترخيص الخاص بك هو:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'كلارنا', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'مستحق الدفع paydate: صافي أيام الدفع payeddue: تاريخ الدفع', @@ -5101,7 +5101,7 @@ $lang = array( 'set_private' => 'تعيين خاص', 'individual' => 'فردي', 'business' => 'عمل', - 'partnership' => 'شراكة', + 'partnership' => 'Partnership', 'trust' => 'يثق', 'charity' => 'صدقة', 'government' => 'حكومة', @@ -5202,7 +5202,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/ca/texts.php b/lang/ca/texts.php index 2737137029ca..4e92139f804d 100644 --- a/lang/ca/texts.php +++ b/lang/ca/texts.php @@ -3868,7 +3868,7 @@ $lang = array( 'cancellation_pending' => 'Cancellation pending, we\'ll be in touch!', 'list_of_payments' => 'List of payments', 'payment_details' => 'Details of the payment', - 'list_of_payment_invoices' => 'List of invoices affected by the payment', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'List of payment methods', 'payment_method_details' => 'Details of payment method', 'permanently_remove_payment_method' => 'Permanently remove this payment method.', @@ -4925,7 +4925,7 @@ $lang = array( 'no_assigned_tasks' => 'No hi ha cap tasca cobrable a aquest projecte', 'authorization_failure' => 'Permisos insuficients per a realitzar aquesta acció', 'authorization_sms_failure' => 'Verifiqueu el vostre compte per a poder enviar missatges de correu.', - 'white_label_body' => 'Gràcies per comprar una llicència de marca blanca.

La vostra clau de llicència és:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Transferència Interac E', 'xinvoice_payable' => 'Payable within :payeddue days net until :paydate', @@ -5120,7 +5120,7 @@ $lang = array( 'set_private' => 'Set private', 'individual' => 'Individual', 'business' => 'Business', - 'partnership' => 'partnership', + 'partnership' => 'Partnership', 'trust' => 'Trust', 'charity' => 'Charity', 'government' => 'Government', @@ -5221,7 +5221,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/cs/texts.php b/lang/cs/texts.php index 0c78e601f7e5..474f0177f85b 100644 --- a/lang/cs/texts.php +++ b/lang/cs/texts.php @@ -3869,7 +3869,7 @@ $lang = array( 'cancellation_pending' => 'Cancellation pending, we\'ll be in touch!', 'list_of_payments' => 'Seznam plateb', 'payment_details' => 'Detaily platby', - 'list_of_payment_invoices' => 'List of invoices affected by the payment', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Seznam platebních metod', 'payment_method_details' => 'Detail platební metody', 'permanently_remove_payment_method' => 'Trvale odstranit tuto platební metodu.', @@ -4926,7 +4926,7 @@ $lang = array( 'no_assigned_tasks' => 'No billable tasks for this project', 'authorization_failure' => 'Insufficient permissions to perform this action', 'authorization_sms_failure' => 'Please verify your account to send emails.', - 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Payable within :payeddue days net until :paydate', @@ -5121,7 +5121,7 @@ $lang = array( 'set_private' => 'Nastavit jako soukromé', 'individual' => 'Jednotlivec', 'business' => 'Podnik', - 'partnership' => 'Partnerství', + 'partnership' => 'Partnership', 'trust' => 'Trust', 'charity' => 'Charita', 'government' => 'Vláda', @@ -5222,7 +5222,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/da/texts.php b/lang/da/texts.php index d40a25d784a6..7794b7b986e8 100644 --- a/lang/da/texts.php +++ b/lang/da/texts.php @@ -3867,7 +3867,7 @@ $lang = array( 'cancellation_pending' => 'Aflysning afventer, vi kontakter dig!', 'list_of_payments' => 'Liste over Betalinger', 'payment_details' => 'Detaljer om Betaling', - 'list_of_payment_invoices' => 'Liste over Fakturaer berørt af Betaling', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Liste over Betaling', 'payment_method_details' => 'Detaljer om Betaling', 'permanently_remove_payment_method' => 'Fjern denne Betaling permanent.', @@ -4924,7 +4924,7 @@ $lang = array( 'no_assigned_tasks' => 'Ingen fakturerbare opgaver for dette projekt', 'authorization_failure' => 'Utilstrækkelige tilladelser til at udføre denne handling', 'authorization_sms_failure' => 'Bekræft venligst din konto for at sende e-mails.', - 'white_label_body' => 'Tak fordi du har købt en Hvidmærke licens.

Din licensnøgle er:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Betales inden for :payeddue dage netto indtil :paydate', @@ -5119,7 +5119,7 @@ $lang = array( 'set_private' => 'Indstil privat', 'individual' => 'Individuel', 'business' => 'Forretning', - 'partnership' => 'partnerskab', + 'partnership' => 'Partnership', 'trust' => 'Tillid', 'charity' => 'Velgørenhed', 'government' => 'Regering', @@ -5220,7 +5220,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/de/texts.php b/lang/de/texts.php index 533e70643536..c08170ef7b81 100644 --- a/lang/de/texts.php +++ b/lang/de/texts.php @@ -694,9 +694,9 @@ $lang = array( 'disable' => 'Deaktivieren', 'invoice_quote_number' => 'Rechnungs- und Angebotsnummern', 'invoice_charges' => 'Rechnungsgebühren', - 'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.

:error', + 'notification_invoice_bounced' => 'Wir waren nicht in der Lage, die Rechnung :invoice an :contact zu liefern. \n :error', 'notification_invoice_bounced_subject' => 'Rechnung :invoice nicht zugestellt.', - 'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.

:error', + 'notification_quote_bounced' => 'Wir waren nicht in der Lage, das Angebot :invoice an :contact zu liefern. \n :error', 'notification_quote_bounced_subject' => 'Angebot :invoice wurde nicht zugestellt.', 'custom_invoice_link' => 'Manueller Rechnungs-Link', 'total_invoiced' => 'Rechnungs-Betrag', @@ -3870,7 +3870,7 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting', 'cancellation_pending' => 'Kündigung in Bearbeitung! Wir melden uns bei Ihnen...', 'list_of_payments' => 'Liste der Zahlungen', 'payment_details' => 'Details zu der Zahlung', - 'list_of_payment_invoices' => 'Liste der Rechnungen die von dieser Zahlung betroffenen sind', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Liste der Zahlungsmethoden', 'payment_method_details' => 'Details zu der Zahlungsmethode', 'permanently_remove_payment_method' => 'Zahlungsmethode endgültig entfernen.', @@ -4927,7 +4927,7 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting', 'no_assigned_tasks' => 'Keine abzurechenden Aufgaben für diese Rechnung', 'authorization_failure' => 'Unzureichende Berechtigungen um diese Aktion auszuführen', 'authorization_sms_failure' => 'Bitte bestätigen Sie Ihr Konto um E-Mails zu versenden', - 'white_label_body' => 'Vielen Dank für den Kauf einer White-Label-Lizenz.

Ihr Lizenzschlüssel lautet:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E-Übertragung', 'xinvoice_payable' => 'Zahlbar innerhalb von :payeddue Tagen netto bis :paydate', @@ -5151,14 +5151,14 @@ Leistungsempfängers', 'load_template_description' => 'Das Template wird auf Folgendes angewendet:', 'run_template' => 'Template anwenden', 'statement_design' => 'Statement Design', - 'delivery_note_design' => 'Delivery Note Design', - 'payment_receipt_design' => 'Payment Receipt Design', - 'payment_refund_design' => 'Payment Refund Design', - 'task_extension_banner' => 'Add the Chrome extension to manage your tasks', + 'delivery_note_design' => 'Lieferschein Design', + 'payment_receipt_design' => 'Zahlungsbeleg Design', + 'payment_refund_design' => 'Gutschrift Design', + 'task_extension_banner' => 'Die Chrome Erweiterung hinzufügen, um Aufgaben zu bearbeiten', 'watch_video' => 'Video ansehen', - 'view_extension' => 'View Extension', + 'view_extension' => 'Erweiterung ansehen', 'reactivate_email' => 'E-Mail reaktivieren', - 'email_reactivated' => 'Successfully reactivated email', + 'email_reactivated' => 'Email erfolgreich reaktiviert', 'template_help' => 'Enable using the design as a template', 'quarter' => 'Quartal', 'item_description' => 'Item Description', @@ -5225,7 +5225,22 @@ Leistungsempfängers', 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Später Zahlen', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/en/texts.php b/lang/en/texts.php index 8b6027093966..11811703ee24 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5237,6 +5237,9 @@ $lang = array( 'user_sales' => 'User Sales', 'iframe_url' => 'iFrame URL', 'user_unsubscribed' => 'User unsubscribed from emails :link', + 'use_available_payments' => 'Use Available Payments', + 'test_email_sent' => 'Successfully sent email', + 'gateway_type' => 'Gateway Type', ); return $lang; diff --git a/lang/es/texts.php b/lang/es/texts.php index 26ca679d0c62..b8822da7f21c 100644 --- a/lang/es/texts.php +++ b/lang/es/texts.php @@ -3867,7 +3867,7 @@ $lang = array( 'cancellation_pending' => 'Cancelación pendiente, ¡nos pondremos en contacto!', 'list_of_payments' => 'Lista de pagos', 'payment_details' => 'Detalles del pago', - 'list_of_payment_invoices' => 'Lista de facturas afectadas por el pago', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Lista de métodos de pago', 'payment_method_details' => 'Detalles del método de pago', 'permanently_remove_payment_method' => 'Eliminar permanentemente este método de pago.', @@ -4924,7 +4924,7 @@ $lang = array( 'no_assigned_tasks' => 'No hay tareas facturables para este proyecto', 'authorization_failure' => 'Permisos insuficientes para realizar esta acción', 'authorization_sms_failure' => 'Por favor verifique su cuenta para enviar correos electrónicos.', - 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Transferencia Interac E', 'xinvoice_payable' => 'Payable within :payeddue days net until :paydate', @@ -5119,7 +5119,7 @@ $lang = array( 'set_private' => 'Establecer privado', 'individual' => 'Individual', 'business' => 'Negocio', - 'partnership' => 'camaradería', + 'partnership' => 'Partnership', 'trust' => 'Confianza', 'charity' => 'Caridad', 'government' => 'Gobierno', @@ -5220,7 +5220,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/es_ES/texts.php b/lang/es_ES/texts.php index 65c15f712562..91fd1c22fa63 100644 --- a/lang/es_ES/texts.php +++ b/lang/es_ES/texts.php @@ -693,9 +693,9 @@ $lang = array( 'disable' => 'Deshabilitado', 'invoice_quote_number' => 'Números de Factura y Presupuesto', 'invoice_charges' => 'Recargos de Factura', - 'notification_invoice_bounced' => 'We were unable to deliver Invoice :invoice to :contact.

:error', + 'notification_invoice_bounced' => 'No pudimos entregar la factura :invoice a :contact.

:error', 'notification_invoice_bounced_subject' => 'No se puede entregar la factura :invoice', - 'notification_quote_bounced' => 'We were unable to deliver Quote :invoice to :contact.

:error', + 'notification_quote_bounced' => 'No pudimos entregar el presupuesto :invoice a :contact.

:error', 'notification_quote_bounced_subject' => 'No se puede entregar el presupuesto :invoice', 'custom_invoice_link' => 'Enlace a Factura personalizado', 'total_invoiced' => 'Total Facturado', @@ -3006,7 +3006,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c 'hosted_login' => 'Acceso alojado', 'selfhost_login' => 'Acceso auto alojado', 'google_login' => 'Acceso con Google', - 'thanks_for_patience' => 'Thank for your patience while we work to implement these features.

We hope to have them completed in the next few months.

Until then we\'ll continue to support the', + 'thanks_for_patience' => 'Gracias por su paciencia mientras trabajamos para implementar estas funciones.

Esperamos completarlas en los próximos meses.

Hasta entonces, continuaremos brindando soporte', 'legacy_mobile_app' => 'app móvil heredada', 'today' => 'Hoy', 'current' => 'Actual', @@ -3864,7 +3864,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c 'cancellation_pending' => 'Cancelación pendiente, ¡estaremos en contacto!', 'list_of_payments' => 'Listado de pagos', 'payment_details' => 'Detalles del pago', - 'list_of_payment_invoices' => 'Lista de facturas afectadas por el pago', + 'list_of_payment_invoices' => 'Facturas asociadas', 'list_of_payment_methods' => 'Lista de medios de pago', 'payment_method_details' => 'Detalles del medio de pago', 'permanently_remove_payment_method' => 'Eliminar permanentemente este medio de pago.', @@ -4921,7 +4921,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c 'no_assigned_tasks' => 'No hay tareas facturables para este proyecto', 'authorization_failure' => 'Permisos insuficientes para realizar esta acción', 'authorization_sms_failure' => 'Por favor verifique su cuenta para enviar correos electrónicos.', - 'white_label_body' => 'Gracias por comprar una licencia de marca blanca.

Su clave de licencia es:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Pagadero dentro de :payeddue días de pago vencido neto hasta :paydate', @@ -5117,7 +5117,7 @@ De lo contrario, este campo deberá dejarse en blanco.', 'set_private' => 'Establecer privado', 'individual' => 'Individual', 'business' => 'Negocio', - 'partnership' => 'asociación', + 'partnership' => 'Asociación', 'trust' => 'Confianza', 'charity' => 'Caridad', 'government' => 'Gobierno', @@ -5210,15 +5210,30 @@ De lo contrario, este campo deberá dejarse en blanco.', 'nordigen_requisition_body' => 'El acceso a los feeds de cuentas bancarias ha caducado según lo establecido en el Acuerdo de usuario final.

Inicie sesión en Invoice Ninja y vuelva a autenticarse con sus bancos para continuar recibiendo transacciones.', 'participant' => 'Participante', 'participant_name' => 'Nombre del participante', - 'client_unsubscribed' => 'Client unsubscribed from emails.', - 'client_unsubscribed_help' => 'Client :client has unsubscribed from your e-mails. The client needs to consent to receive future emails from you.', - 'resubscribe' => 'Resubscribe', - 'subscribe' => 'Subscribe', - 'subscribe_help' => 'You are currently subscribed and will continue to receive email communications.', - 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', - 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', - 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'client_unsubscribed' => 'El cliente se dio de baja de los correos electrónicos.', + 'client_unsubscribed_help' => 'El cliente :client se ha dado de baja de sus correos electrónicos. El cliente debe dar su consentimiento para recibir futuros correos electrónicos.', + 'resubscribe' => 'Volver a suscribirse', + 'subscribe' => 'Suscribirse', + 'subscribe_help' => 'Actualmente está suscrito y seguirá recibiendo comunicaciones por correo electrónico.', + 'unsubscribe_help' => 'Actualmente no estás suscrito y, por lo tanto, no recibirás correos electrónicos en este momento.', + 'notification_purchase_order_bounced' => 'No pudimos entregar la orden de compra :invoice a :contact.

:error', + 'notification_purchase_order_bounced_subject' => 'No se puede entregar la orden de compra :invoice', + 'show_pdfhtml_on_mobile' => 'Mostrar la versión HTML de la entidad cuando se visualiza en un dispositivo móvil', + 'show_pdfhtml_on_mobile_help' => 'Para una visualización mejorada, muestra una versión HTML de la factura/presupuesto cuando se visualiza en el dispositivo móvil.', + 'please_select_an_invoice_or_credit' => 'Por favor seleccione una factura o crédito', + 'mobile_version' => 'Version móvil', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Paga después', + 'local_domain' => 'Dominio local', + 'verify_peer' => 'Verificar par', + 'nordigen_help' => 'Nota: conectar una cuenta requiere una clave API de GoCardless/Nordigen', + 'ar_detailed' => 'Cuentas por cobrar detalladas', + 'ar_summary' => 'Resumen de cuentas por cobrar', + 'client_sales' => 'Ventas al cliente', + 'user_sales' => 'Ventas de usuarios', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'Usuario dado de baja de los correos electrónicos :link', ); return $lang; diff --git a/lang/fr/texts.php b/lang/fr/texts.php index d9c085c042ba..f5fccd2ec5f2 100644 --- a/lang/fr/texts.php +++ b/lang/fr/texts.php @@ -3868,7 +3868,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'cancellation_pending' => 'Annulation en cours, nous vous contacterons !', 'list_of_payments' => 'Liste des paiements', 'payment_details' => 'Détails du paiement', - 'list_of_payment_invoices' => 'Liste des factures affectées par le paiement', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Liste des moyens de paiement', 'payment_method_details' => 'Détails du mode de paiement', 'permanently_remove_payment_method' => 'Supprimer définitivement ce mode de paiement.', @@ -4925,7 +4925,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'no_assigned_tasks' => 'Aucune tâche facturable pour ce projet', 'authorization_failure' => 'Autorisations insuffisantes pour effectuer cette action', 'authorization_sms_failure' => 'Veuillez vérifier votre compte pour envoyer des e-mails.', - 'white_label_body' => 'Merci d\'avoir acheté une licence en marque blanche.

Votre clé de licence est :

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Virement Interac E', 'xinvoice_payable' => 'Payable sous :payeddue days net jusqu\'au :paydate', @@ -5120,7 +5120,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'set_private' => 'Définir comme privé', 'individual' => 'Individuel', 'business' => 'Entreprise', - 'partnership' => 'Partenariat', + 'partnership' => 'Partnership', 'trust' => 'Confiance', 'charity' => 'Charité', 'government' => 'Gouvernement', @@ -5221,7 +5221,22 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/fr_CA/texts.php b/lang/fr_CA/texts.php index ed72c6c1f524..1020490d5777 100644 --- a/lang/fr_CA/texts.php +++ b/lang/fr_CA/texts.php @@ -3865,7 +3865,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'cancellation_pending' => 'Annulation en attente. Nous vous tiendrons au courant.', 'list_of_payments' => 'Liste des paiements', 'payment_details' => 'Détails du paiement', - 'list_of_payment_invoices' => 'Liste des factures affectées par le paiement', + 'list_of_payment_invoices' => 'Associer les factures', 'list_of_payment_methods' => 'Liste des modes de paiement', 'payment_method_details' => 'Détails du mode de paiement', 'permanently_remove_payment_method' => 'Supprimer de façon définitive ce mode de paiement', @@ -4295,7 +4295,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'expense_tax_help' => 'Les taxes par article sont désactivées', 'enable_markdown' => 'Activer Markdown', 'enable_markdown_help' => 'Convertir Markdown en HTML dans le PDF', - 'add_second_contact' => 'Ajouter un dexième contact', + 'add_second_contact' => 'Ajouter un deuxième contact', 'previous_page' => 'Page précédente', 'next_page' => 'Page suivante', 'export_colors' => 'Exporter les couleurs', @@ -4922,7 +4922,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'no_assigned_tasks' => 'Aucune tâche facturable pour ce projet.', 'authorization_failure' => 'Permissions insuffisantes pour accomplir cette action', 'authorization_sms_failure' => 'Veuillez vérifier votre compte pour l\'envoi de courriel.', - 'white_label_body' => 'Merci d\'avoir acheté une licence.

Votre clé de licence est:

:license_key', + 'white_label_body' => 'Merci d\'avoir acheté une licence sans marque.

Votre clé de licence est :

:license_key

Vous pouvez gérer votre licence ici : https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Transfert Interac', 'xinvoice_payable' => 'Payable d\'ici :payeddue jours jusqu\'à :paydate', @@ -5117,7 +5117,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'set_private' => 'Privé', 'individual' => 'Individuel', 'business' => 'Entreprise', - 'partnership' => 'partenaire', + 'partnership' => 'Partenariat', 'trust' => 'Fiducie', 'charity' => 'Organisation caritative', 'government' => 'Gouvernement', @@ -5218,7 +5218,22 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'unsubscribe_help' => 'Vous n\'êtes actuellement pas abonné et vous ne recevrez pas de courriels pour le moment.', 'notification_purchase_order_bounced' => 'Nous n\'avons pas pu émettre le bon de commande :invoice à :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Impossible de livrer le bon de commande :invoice', - + 'show_pdfhtml_on_mobile' => 'Afficher la version HTML lors de la visualisation sur mobile.', + 'show_pdfhtml_on_mobile_help' => 'Pour une meilleure visualisation, affiche une version HTML de la facture/soumission lors de la consultation sur mobile.', + 'please_select_an_invoice_or_credit' => 'Veuillez sélectionner une facture ou un crédit.', + 'mobile_version' => 'Version mobile', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Payer plus tard', + 'local_domain' => 'Domaine local', + 'verify_peer' => 'Vérifier le pair', + 'nordigen_help' => 'Note: la connexion d\'un compte nécessite une clé d\'API GoCardless/Nordigen.', + 'ar_detailed' => 'Détails des comptes clients', + 'ar_summary' => 'Résumé des comptes clients', + 'client_sales' => 'Ventes du client', + 'user_sales' => 'Ventes de l\'utilisateur', + 'iframe_url' => 'URL de l\'iFrame', + 'user_unsubscribed' => 'Utilisateur désabonné des courriels :link', ); return $lang; diff --git a/lang/fr_CH/texts.php b/lang/fr_CH/texts.php index df4885a3e223..03716645652f 100644 --- a/lang/fr_CH/texts.php +++ b/lang/fr_CH/texts.php @@ -3865,7 +3865,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'cancellation_pending' => 'Annulation en attente. Nous vous tiendrons au courant.', 'list_of_payments' => 'Liste des paiements', 'payment_details' => 'Détails du paiement', - 'list_of_payment_invoices' => 'Liste des factures affectées par le paiement', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Liste des modes de paiement', 'payment_method_details' => 'Détails du mode de paiement', 'permanently_remove_payment_method' => 'Supprimer de façon définitive ce mode de paiement', @@ -4922,7 +4922,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'no_assigned_tasks' => 'Aucune intervetion facturable pour ce projet', 'authorization_failure' => 'Insufficient permissions to perform this action', 'authorization_sms_failure' => 'Please verify your account to send emails.', - 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Payable sous :payeddue jours net jusqu'à :paydate', @@ -5117,7 +5117,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'set_private' => 'Définir comme privé', 'individual' => 'Individuel', 'business' => 'Entreprise', - 'partnership' => 'partenaire', + 'partnership' => 'Partnership', 'trust' => 'Confiance', 'charity' => 'Charité', 'government' => 'Gouvernement', @@ -5218,7 +5218,22 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/he/texts.php b/lang/he/texts.php index 8f57dfafddf2..52658c6495de 100644 --- a/lang/he/texts.php +++ b/lang/he/texts.php @@ -3866,7 +3866,7 @@ $lang = array( 'cancellation_pending' => 'הביטול בהמתנה, ניצור איתך קשר!', 'list_of_payments' => 'רשימת תשלומים', 'payment_details' => 'פרטי התשלום', - 'list_of_payment_invoices' => 'רשימת חשבוניות שיושפעו מהתשלום', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'רשימת אמצעי תשלום', 'payment_method_details' => 'פרטים על אמצעי תשלום', 'permanently_remove_payment_method' => 'הסר לצמיתות את אמצעי התשלום הזה.', @@ -4923,7 +4923,7 @@ $lang = array( 'no_assigned_tasks' => 'אין משימות שניתנות לחיוב עבור הפרויקט הזה', 'authorization_failure' => 'אין מספיק הרשאות לביצוע פעולה זו', 'authorization_sms_failure' => 'אנא אמת את חשבונך כדי לשלוח אימיילים.', - 'white_label_body' => 'תודה שרכשת רישיון לבן תווית.

מפתח הרישיון שלך הוא:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'קלרנה', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'ניתן לשלם תוך :payeddue ימים נטו עד :paydate', @@ -5118,7 +5118,7 @@ $lang = array( 'set_private' => 'הגדר פרטי', 'individual' => 'אִישִׁי', 'business' => 'עֵסֶק', - 'partnership' => 'שׁוּתָפוּת', + 'partnership' => 'Partnership', 'trust' => 'אמון', 'charity' => 'צדקה', 'government' => 'מֶמְשָׁלָה', @@ -5219,7 +5219,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/hu/texts.php b/lang/hu/texts.php index 1b6f6d35f2f4..81cd1da267fd 100644 --- a/lang/hu/texts.php +++ b/lang/hu/texts.php @@ -3852,7 +3852,7 @@ adva :date', 'cancellation_pending' => 'Lemondás folyamatban', 'list_of_payments' => 'Fizetések listája', 'payment_details' => 'Fizetési részletek', - 'list_of_payment_invoices' => 'Fizetési számlák listája', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Fizetési módok listája', 'payment_method_details' => 'Fizetési mód részletei', 'permanently_remove_payment_method' => 'Fizetési mód végleges eltávolítása', @@ -4909,7 +4909,7 @@ adva :date', 'no_assigned_tasks' => 'nincsenek hozzárendelt feladatok', 'authorization_failure' => 'engedélyezési hiba', 'authorization_sms_failure' => 'engedélyezési SMS-hiba', - 'white_label_body' => 'fehér címke szövege', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'fizetési típus: Klarna', 'payment_type_Interac E Transfer' => 'fizetési típus: Interac E átutalás', 'xinvoice_payable' => 'XInvoice fizetendő', @@ -5104,7 +5104,7 @@ adva :date', 'set_private' => 'Privát beállítás', 'individual' => 'Egyedi', 'business' => 'Üzleti', - 'partnership' => 'partnerség', + 'partnership' => 'Partnership', 'trust' => 'Bizalom', 'charity' => 'Adomány', 'government' => 'Kormány', @@ -5205,7 +5205,22 @@ adva :date', 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/it/texts.php b/lang/it/texts.php index b22ef876a54e..5161891bc11e 100644 --- a/lang/it/texts.php +++ b/lang/it/texts.php @@ -3859,7 +3859,7 @@ $lang = array( 'cancellation_pending' => 'Cancellazione in corso, ci metteremo in contatto!', 'list_of_payments' => 'Elenco dei pagamenti', 'payment_details' => 'Dettagli del pagamento', - 'list_of_payment_invoices' => 'Elenco delle fatture interessate dal pagamento', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Elenco dei metodi di pagamento', 'payment_method_details' => 'Dettagli del metodo di pagamento', 'permanently_remove_payment_method' => 'Rimuovi definitivamente questo metodo di pagamento.', @@ -4916,7 +4916,7 @@ $lang = array( 'no_assigned_tasks' => 'Nessuna attività fatturabile per questo progetto', 'authorization_failure' => 'Autorizzazioni insufficienti per eseguire questa azione', 'authorization_sms_failure' => 'Verifica il tuo account per inviare e-mail.', - 'white_label_body' => 'Grazie per aver acquistato una licenza white label.

La tua chiave di licenza è:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Clarna', 'payment_type_Interac E Transfer' => 'Interac E Trasferimento', 'xinvoice_payable' => 'Pagabile entro :payeddue giorni netti fino :paydate', @@ -5111,7 +5111,7 @@ $lang = array( 'set_private' => 'Imposta privato', 'individual' => 'Individuale', 'business' => 'Attività commerciale', - 'partnership' => 'associazione', + 'partnership' => 'Partnership', 'trust' => 'Fiducia', 'charity' => 'Beneficenza', 'government' => 'Governo', @@ -5212,7 +5212,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/ja/texts.php b/lang/ja/texts.php index 8a77abd016fd..e91cccdee005 100644 --- a/lang/ja/texts.php +++ b/lang/ja/texts.php @@ -3868,7 +3868,7 @@ $lang = array( 'cancellation_pending' => 'Cancellation pending, we\'ll be in touch!', 'list_of_payments' => 'List of payments', 'payment_details' => 'Details of the payment', - 'list_of_payment_invoices' => 'List of invoices affected by the payment', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'List of payment methods', 'payment_method_details' => 'Details of payment method', 'permanently_remove_payment_method' => 'Permanently remove this payment method.', @@ -4925,7 +4925,7 @@ $lang = array( 'no_assigned_tasks' => 'No billable tasks for this project', 'authorization_failure' => 'Insufficient permissions to perform this action', 'authorization_sms_failure' => 'Please verify your account to send emails.', - 'white_label_body' => 'ホワイトレーベルライセンスをお買い上げいただき、誠にありがとうございます。

ライセンス キーは次のとおりです。

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => ':paydate まで正味 :payeddue 日以内に支払い可能', @@ -5120,7 +5120,7 @@ $lang = array( 'set_private' => 'Set private', 'individual' => 'Individual', 'business' => 'Business', - 'partnership' => 'partnership', + 'partnership' => 'Partnership', 'trust' => 'Trust', 'charity' => 'Charity', 'government' => 'Government', @@ -5221,7 +5221,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/km_KH/texts.php b/lang/km_KH/texts.php index 0d117bfc2cc3..c7c501a6965c 100644 --- a/lang/km_KH/texts.php +++ b/lang/km_KH/texts.php @@ -3848,7 +3848,7 @@ $lang = array( 'cancellation_pending' => 'រង់ចាំការលុបចោល យើងនឹងទាក់ទងទៅ!', 'list_of_payments' => 'បញ្ជីនៃការទូទាត់', 'payment_details' => 'ព័ត៌មានលម្អិតនៃការទូទាត់', - 'list_of_payment_invoices' => 'បញ្ជីវិក្កយបត្រដែលរងផលប៉ះពាល់ដោយការទូទាត់', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'បញ្ជីវិធីបង់ប្រាក់', 'payment_method_details' => 'ព័ត៌មានលម្អិតអំពីវិធីបង់ប្រាក់', 'permanently_remove_payment_method' => 'លុបវិធីបង់ប្រាក់នេះចេញជាអចិន្ត្រៃយ៍។', @@ -4905,7 +4905,7 @@ $lang = array( 'no_assigned_tasks' => 'មិនមានកិច្ចការដែលអាចទូទាត់បានសម្រាប់គម្រោងនេះទេ។', 'authorization_failure' => 'ការអនុញ្ញាតមិនគ្រប់គ្រាន់ដើម្បីអនុវត្តសកម្មភាពនេះ។', 'authorization_sms_failure' => 'សូមផ្ទៀងផ្ទាត់គណនីរបស់អ្នក ដើម្បីផ្ញើអ៊ីមែល។', - 'white_label_body' => 'សូមអរគុណសម្រាប់ការទិញអាជ្ញាប័ណ្ណស្លាកពណ៌ស។

លេខកូដអាជ្ញាប័ណ្ណរបស់អ្នកគឺ៖

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'ក្លាណា', 'payment_type_Interac E Transfer' => 'ការផ្ទេរ Interac E', 'xinvoice_payable' => 'អាចបង់បានក្នុងរយៈពេល :payeddue ថ្ងៃសុទ្ធរហូតដល់ :paydate', @@ -5100,7 +5100,7 @@ $lang = array( 'set_private' => 'កំណត់ឯកជន', 'individual' => 'បុគ្គល', 'business' => 'អាជីវកម្ម', - 'partnership' => 'ភាពជាដៃគូ', + 'partnership' => 'Partnership', 'trust' => 'ទុកចិត្ត', 'charity' => 'សប្បុរសធម៌', 'government' => 'រដ្ឋាភិបាល', @@ -5201,7 +5201,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/lo_LA/texts.php b/lang/lo_LA/texts.php index aad9f80d37af..01675e68f642 100644 --- a/lang/lo_LA/texts.php +++ b/lang/lo_LA/texts.php @@ -3868,7 +3868,7 @@ $lang = array( 'cancellation_pending' => 'ລໍຖ້າການຍົກເລີກ, ພວກເຮົາຈະຕິດຕໍ່ຫາ!', 'list_of_payments' => 'ລາຍຊື່ການຈ່າຍເງິນ', 'payment_details' => 'ລາຍລະອຽດຂອງການຈ່າຍເງິນ', - 'list_of_payment_invoices' => 'ລາຍຊື່ໃບແຈ້ງໜີ້ທີ່ໄດ້ຮັບຜົນກະທົບຈາກການຈ່າຍເງິນ', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'ລາຍຊື່ວິທີຈ່າຍເງິນ', 'payment_method_details' => 'ລາຍລະອຽດຂອງວິທີການຊໍາລະ', 'permanently_remove_payment_method' => 'ລຶບວິທີການຈ່າຍເງິນນີ້ອອກຖາວອນ.', @@ -4925,7 +4925,7 @@ $lang = array( 'no_assigned_tasks' => 'ບໍ່ມີໜ້າວຽກທີ່ສາມາດເກັບເງິນໄດ້ສຳລັບໂຄງການນີ້', 'authorization_failure' => 'ການອະນຸຍາດບໍ່ພຽງພໍເພື່ອປະຕິບັດການນີ້', 'authorization_sms_failure' => 'ກະລຸນາກວດສອບບັນຊີຂອງທ່ານເພື່ອສົ່ງອີເມວ.', - 'white_label_body' => 'ຂອບໃຈທີ່ຊື້ໃບຂັບຂີ່ປ້າຍຂາວ.

ກະແຈໃບອະນຸຍາດຂອງທ່ານແມ່ນ:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'ຄລານາ', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'ຊໍາລະພາຍໃນ: payeddue ວັນສຸດທິຈົນກ່ວາ: paydate', @@ -5120,7 +5120,7 @@ $lang = array( 'set_private' => 'ຕັ້ງເປັນສ່ວນຕົວ', 'individual' => 'ບຸກຄົນ', 'business' => 'ທຸລະກິດ', - 'partnership' => 'ຫຸ້ນສ່ວນ', + 'partnership' => 'Partnership', 'trust' => 'ຄວາມໄວ້ວາງໃຈ', 'charity' => 'ການກຸສົນ', 'government' => 'ລັດຖະບານ', @@ -5221,7 +5221,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/nl/texts.php b/lang/nl/texts.php index 86dcf655924c..49b89598c2da 100644 --- a/lang/nl/texts.php +++ b/lang/nl/texts.php @@ -3865,7 +3865,7 @@ Kom terug naar deze betalingsmethode pagina zodra u de bedragen heeft ontvangen 'cancellation_pending' => 'Annulatie in aanvraag, we nemen contact met u op!', 'list_of_payments' => 'Lijst met betalingen', 'payment_details' => 'Details van de betaling', - 'list_of_payment_invoices' => 'Lijst met facturen waarop de betaling betrekking heeft', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Lijst met betalingsmethodes', 'payment_method_details' => 'Details van betalingsmethodes', 'permanently_remove_payment_method' => 'Verwijder deze betalingsmethode definitief', @@ -4925,7 +4925,7 @@ Email: :email
', 'no_assigned_tasks' => 'Geen factureerbare taken voor dit project', 'authorization_failure' => 'Onvoldoende machtigingen om deze actie uit te voeren', 'authorization_sms_failure' => 'Verifieer uw account om e-mails te verzenden.', - 'white_label_body' => 'Bedankt voor het aanschaffen van een white label licentie.

Uw licentiesleutel is:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E-overdracht', 'xinvoice_payable' => 'Te betalen binnen :payeddue vervaldagen netto tot :paydate', @@ -5120,7 +5120,7 @@ Email: :email
', 'set_private' => 'Privé instellen', 'individual' => 'Individueel', 'business' => 'Bedrijf', - 'partnership' => 'vennootschap', + 'partnership' => 'Partnership', 'trust' => 'Vertrouwen', 'charity' => 'Goed doel', 'government' => 'Regering', @@ -5221,7 +5221,22 @@ Email: :email
', 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/pl/texts.php b/lang/pl/texts.php index cdd89a31f711..4db2dab2df10 100644 --- a/lang/pl/texts.php +++ b/lang/pl/texts.php @@ -3866,7 +3866,7 @@ Gdy przelewy zostaną zaksięgowane na Twoim koncie, wróć do tej strony i klik 'cancellation_pending' => 'Cancellation pending, we\'ll be in touch!', 'list_of_payments' => 'Lista płatności', 'payment_details' => 'Szczegóły płatności', - 'list_of_payment_invoices' => 'Lista faktur, których dotyczy płatność', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Lista metod płatności', 'payment_method_details' => 'Szczegóły metody płatności', 'permanently_remove_payment_method' => 'Trwale usuń tę metodę płatności.', @@ -4923,7 +4923,7 @@ Gdy przelewy zostaną zaksięgowane na Twoim koncie, wróć do tej strony i klik 'no_assigned_tasks' => 'No billable tasks for this project', 'authorization_failure' => 'Niewystarczające uprawnienia do wykonania tej czynności', 'authorization_sms_failure' => 'Zweryfikuj swoje konto, aby wysyłać e-maile.', - 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Payable within :payeddue days net until :paydate', @@ -5118,7 +5118,7 @@ Gdy przelewy zostaną zaksięgowane na Twoim koncie, wróć do tej strony i klik 'set_private' => 'Set private', 'individual' => 'Individual', 'business' => 'Business', - 'partnership' => 'partnership', + 'partnership' => 'Partnership', 'trust' => 'Trust', 'charity' => 'Charity', 'government' => 'Government', @@ -5219,7 +5219,22 @@ Gdy przelewy zostaną zaksięgowane na Twoim koncie, wróć do tej strony i klik 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/pt_BR/texts.php b/lang/pt_BR/texts.php index e040b0b5ed39..3c2ef7ce4906 100644 --- a/lang/pt_BR/texts.php +++ b/lang/pt_BR/texts.php @@ -3865,7 +3865,7 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'cancellation_pending' => 'Cancelamento pendente, entraremos em contato!', 'list_of_payments' => 'Lista de pagamentos', 'payment_details' => 'Detalhes do pagamento', - 'list_of_payment_invoices' => 'Lista de faturas afetadas pelo pagamento', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Lista de métodos de pagamento', 'payment_method_details' => 'Detalhes da forma de pagamento', 'permanently_remove_payment_method' => 'Remova permanentemente esta forma de pagamento.', @@ -4922,7 +4922,7 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'no_assigned_tasks' => 'Nenhuma tarefa faturável para este projeto', 'authorization_failure' => 'Permissões insuficientes para executar esta ação', 'authorization_sms_failure' => 'Verifique sua conta para enviar e-mails.', - 'white_label_body' => 'Obrigado por adquirir uma licença de marca branca.

Sua chave de licença é:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Transferência Interac E', 'xinvoice_payable' => 'A pagar dentro de :payeddue dias líquidos até :paydate', @@ -5117,7 +5117,7 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'set_private' => 'Definir como privado', 'individual' => 'Individual', 'business' => 'Negócios', - 'partnership' => 'parceria', + 'partnership' => 'Partnership', 'trust' => 'Confiar', 'charity' => 'Caridade', 'government' => 'Governo', @@ -5218,7 +5218,22 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/pt_PT/texts.php b/lang/pt_PT/texts.php index 0867715b3b00..b64f874782a7 100644 --- a/lang/pt_PT/texts.php +++ b/lang/pt_PT/texts.php @@ -3867,7 +3867,7 @@ debitar da sua conta de acordo com essas instruções. Está elegível a um reem 'cancellation_pending' => 'Cancelamento pendente, entraremos em contacto muito brevemente!', 'list_of_payments' => 'Lista de pagamentos', 'payment_details' => 'Detalhes do pagamento', - 'list_of_payment_invoices' => 'Lista de notas de pagamento afetadas pelo pagamento', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Lista de métodos de pagamento', 'payment_method_details' => 'Detalhes do método de pagamento', 'permanently_remove_payment_method' => 'Eliminar permanentemente este método de pagamento.', @@ -4925,7 +4925,7 @@ O envio de E-mails foi suspenso. Será retomado às 23:00 UTC.', 'no_assigned_tasks' => 'Nenhuma tarefa faturável para este projeto', 'authorization_failure' => 'Permissões insuficientes para executar esta ação', 'authorization_sms_failure' => 'Verifique sua conta para enviar e-mails.', - 'white_label_body' => 'Obrigado por adquirir uma licença de marca branca.

Sua chave de licença é:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Pagável dentro de :payeddue dias líquidos até :paydate', @@ -5120,7 +5120,7 @@ O envio de E-mails foi suspenso. Será retomado às 23:00 UTC.', 'set_private' => 'Definir como privado', 'individual' => 'Individual', 'business' => 'Negócios', - 'partnership' => 'parceria', + 'partnership' => 'Partnership', 'trust' => 'Confiar', 'charity' => 'Caridade', 'government' => 'Governo', @@ -5221,7 +5221,22 @@ O envio de E-mails foi suspenso. Será retomado às 23:00 UTC.', 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/ro/texts.php b/lang/ro/texts.php index 362422178ac6..98419a4861e6 100644 --- a/lang/ro/texts.php +++ b/lang/ro/texts.php @@ -3869,7 +3869,7 @@ Odată ce sumele au ajuns la dumneavoastră, reveniți la pagina cu metode de pl 'cancellation_pending' => 'Anulare în așteptare. Vă vom contacta.', 'list_of_payments' => 'Listă plăți', 'payment_details' => 'Detalii plată', - 'list_of_payment_invoices' => 'Listă facturi afectate de plată', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Listă metode de plată', 'payment_method_details' => 'Detalii metodă de plată', 'permanently_remove_payment_method' => 'Îndepărtați permanent această metodă de plată.', @@ -4926,7 +4926,7 @@ Odată ce sumele au ajuns la dumneavoastră, reveniți la pagina cu metode de pl 'no_assigned_tasks' => 'No billable tasks for this project', 'authorization_failure' => 'Insufficient permissions to perform this action', 'authorization_sms_failure' => 'Please verify your account to send emails.', - 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Payable within :payeddue days net until :paydate', @@ -5121,7 +5121,7 @@ Odată ce sumele au ajuns la dumneavoastră, reveniți la pagina cu metode de pl 'set_private' => 'Set private', 'individual' => 'Individual', 'business' => 'Business', - 'partnership' => 'partnership', + 'partnership' => 'Partnership', 'trust' => 'Trust', 'charity' => 'Charity', 'government' => 'Government', @@ -5222,7 +5222,22 @@ Odată ce sumele au ajuns la dumneavoastră, reveniți la pagina cu metode de pl 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/sk/texts.php b/lang/sk/texts.php index a21e149e447a..47f41dd49fa0 100644 --- a/lang/sk/texts.php +++ b/lang/sk/texts.php @@ -3855,7 +3855,7 @@ $lang = array( 'cancellation_pending' => 'Čaká sa na zrušenie, budeme vás kontaktovať!', 'list_of_payments' => 'Zoznam platieb', 'payment_details' => 'Podrobnosti o platbe', - 'list_of_payment_invoices' => 'Zoznam faktúr ovplyvnených platbou', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Zoznam spôsobov platby', 'payment_method_details' => 'Podrobnosti o spôsobe platby', 'permanently_remove_payment_method' => 'Natrvalo odstrániť tento spôsob platby.', @@ -4912,7 +4912,7 @@ $lang = array( 'no_assigned_tasks' => 'Žiadne fakturovateľné úlohy pre tento projekt', 'authorization_failure' => 'Nedostatočné povolenia na vykonanie tejto akcie', 'authorization_sms_failure' => 'Ak chcete odosielať e-maily, overte svoj účet.', - 'white_label_body' => 'Ďakujeme, že ste si zakúpili licenciu s bielym štítkom.

Váš licenčný kľúč je:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Splatné do :payeddue dní netto do :paydate', @@ -5107,7 +5107,7 @@ $lang = array( 'set_private' => 'Nastaviť ako súkromné', 'individual' => 'Individuálne', 'business' => 'Podnikanie', - 'partnership' => 'partnerstvo', + 'partnership' => 'Partnership', 'trust' => 'Dôvera', 'charity' => 'Dobročinnosť', 'government' => 'vláda', @@ -5208,7 +5208,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/sr/texts.php b/lang/sr/texts.php index aff0375d825f..9a6d73bb8619 100644 --- a/lang/sr/texts.php +++ b/lang/sr/texts.php @@ -3868,7 +3868,7 @@ Kada budete imali iznose, vratite se na ovu stranicu sa načinima plaćanja i k 'cancellation_pending' => 'Otkazivanje je na čekanju, u kontaktu smo!', 'list_of_payments' => 'Spisak uplata', 'payment_details' => 'Detalji o uplati', - 'list_of_payment_invoices' => 'Spisak računa na koje utiče plaćanje', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Spisak metoda plaćanja', 'payment_method_details' => 'Detalji o metodu plaćanja', 'permanently_remove_payment_method' => 'Trajno uklonite ovaj način plaćanja.', @@ -4925,7 +4925,7 @@ Kada budete imali iznose, vratite se na ovu stranicu sa načinima plaćanja i k 'no_assigned_tasks' => 'No billable tasks for this project', 'authorization_failure' => 'Insufficient permissions to perform this action', 'authorization_sms_failure' => 'Please verify your account to send emails.', - 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Payable within :payeddue days net until :paydate', @@ -5120,7 +5120,7 @@ Kada budete imali iznose, vratite se na ovu stranicu sa načinima plaćanja i k 'set_private' => 'Set private', 'individual' => 'Individual', 'business' => 'Business', - 'partnership' => 'partnership', + 'partnership' => 'Partnership', 'trust' => 'Trust', 'charity' => 'Charity', 'government' => 'Government', @@ -5221,7 +5221,22 @@ Kada budete imali iznose, vratite se na ovu stranicu sa načinima plaćanja i k 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/sv/texts.php b/lang/sv/texts.php index 384a853aad52..46d114e622eb 100644 --- a/lang/sv/texts.php +++ b/lang/sv/texts.php @@ -3876,7 +3876,7 @@ Den här funktionen kräver att en produkt skapas och en betalningsgateway är k 'cancellation_pending' => 'Väntande avslut, vi hör av oss!', 'list_of_payments' => 'Lista över betalningar', 'payment_details' => 'Detaljer om betalningen', - 'list_of_payment_invoices' => 'Lista över fakturor som påverkas av betalningen', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => 'Lista över betalningsmetoder', 'payment_method_details' => 'Information om betalningsmetod', 'permanently_remove_payment_method' => 'Ta bort denna betalningsmetod permanent.', @@ -4933,7 +4933,7 @@ Den här funktionen kräver att en produkt skapas och en betalningsgateway är k 'no_assigned_tasks' => 'No billable tasks for this project', 'authorization_failure' => 'Insufficient permissions to perform this action', 'authorization_sms_failure' => 'Please verify your account to send emails.', - 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => 'Klarna', 'payment_type_Interac E Transfer' => 'Interac E Transfer', 'xinvoice_payable' => 'Payable within :payeddue days net until :paydate', @@ -5128,7 +5128,7 @@ Den här funktionen kräver att en produkt skapas och en betalningsgateway är k 'set_private' => 'Set private', 'individual' => 'Individual', 'business' => 'Business', - 'partnership' => 'partnership', + 'partnership' => 'Partnership', 'trust' => 'Trust', 'charity' => 'Charity', 'government' => 'Government', @@ -5229,7 +5229,22 @@ Den här funktionen kräver att en produkt skapas och en betalningsgateway är k 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; diff --git a/lang/zh_TW/texts.php b/lang/zh_TW/texts.php index 7a9c1c60ba26..e35d3f1125a7 100644 --- a/lang/zh_TW/texts.php +++ b/lang/zh_TW/texts.php @@ -3868,7 +3868,7 @@ $lang = array( 'cancellation_pending' => '取消待定,我們會聯絡您!', 'list_of_payments' => '付款清單', 'payment_details' => '付款詳情', - 'list_of_payment_invoices' => '受付款影響的發票列表', + 'list_of_payment_invoices' => 'Associate invoices', 'list_of_payment_methods' => '付款方式一覽', 'payment_method_details' => '付款方式詳情', 'permanently_remove_payment_method' => '永久刪除此付款方式。', @@ -4925,7 +4925,7 @@ $lang = array( 'no_assigned_tasks' => '該項目沒有計費任務', 'authorization_failure' => '權限不足,無法執行此操作', 'authorization_sms_failure' => '請驗證您的帳戶以發送電子郵件。', - 'white_label_body' => '感謝您購買白標許可證。

您的許可證密鑰是:

:license_key', + 'white_label_body' => 'Thank you for purchasing a white label license.

Your license key is:

:license_key

You can manage your license here: https://invoiceninja.invoicing.co/client/login', 'payment_type_Klarna' => '克拉納', 'payment_type_Interac E Transfer' => 'Interac E 傳輸', 'xinvoice_payable' => '在:payeddue天內支付,直至:paydate', @@ -5120,7 +5120,7 @@ $lang = array( 'set_private' => '設定私人', 'individual' => '個人', 'business' => '商業', - 'partnership' => '合夥', + 'partnership' => 'Partnership', 'trust' => '相信', 'charity' => '慈善機構', 'government' => '政府', @@ -5221,7 +5221,22 @@ $lang = array( 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', 'notification_purchase_order_bounced' => 'We were unable to deliver Purchase Order :invoice to :contact.

:error', 'notification_purchase_order_bounced_subject' => 'Unable to deliver Purchase Order :invoice', - + 'show_pdfhtml_on_mobile' => 'Display HTML version of entity when viewing on mobile', + 'show_pdfhtml_on_mobile_help' => 'For improved visualization, displays a HTML version of the invoice/quote when viewing on mobile.', + 'please_select_an_invoice_or_credit' => 'Please select an invoice or credit', + 'mobile_version' => 'Mobile Version', + 'venmo' => 'Venmo', + 'my_bank' => 'MyBank', + 'pay_later' => 'Pay Later', + 'local_domain' => 'Local Domain', + 'verify_peer' => 'Verify Peer', + 'nordigen_help' => 'Note: connecting an account requires a GoCardless/Nordigen API key', + 'ar_detailed' => 'Accounts Receivable Detailed', + 'ar_summary' => 'Accounts Receivable Summary', + 'client_sales' => 'Client Sales', + 'user_sales' => 'User Sales', + 'iframe_url' => 'iFrame URL', + 'user_unsubscribed' => 'User unsubscribed from emails :link', ); return $lang; From 0b3e1d68080b9b95e041633018b29ba20f4917de Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 23 Feb 2024 11:39:30 +1100 Subject: [PATCH 14/37] v5.8.28 --- VERSION.txt | 2 +- config/ninja.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index df3286a2ff46..7086f04f2e05 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.8.27 \ No newline at end of file +5.8.28 \ No newline at end of file diff --git a/config/ninja.php b/config/ninja.php index e1ab63d1bd4d..ec1462381359 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.27'), - 'app_tag' => env('APP_TAG', '5.8.27'), + 'app_version' => env('APP_VERSION', '5.8.28'), + 'app_tag' => env('APP_TAG', '5.8.28'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), From d3cd6765bb49fa4be0706b026c9378763184a660 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 24 Feb 2024 13:28:19 +1100 Subject: [PATCH 15/37] Fixes for taxes not displaying --- app/DataMapper/Tax/RuleInterface.php | 6 ++++++ app/Helpers/Invoice/InvoiceItemSum.php | 10 ++++++---- app/Helpers/Invoice/InvoiceItemSumInclusive.php | 6 +++--- app/Helpers/Invoice/InvoiceSumInclusive.php | 6 +++--- app/Http/Controllers/ImportController.php | 2 ++ app/Import/Providers/BaseImport.php | 1 + app/Services/Report/ProfitLoss.php | 11 +++++++++++ 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/app/DataMapper/Tax/RuleInterface.php b/app/DataMapper/Tax/RuleInterface.php index a30e26f9d666..7ff480e21ce6 100644 --- a/app/DataMapper/Tax/RuleInterface.php +++ b/app/DataMapper/Tax/RuleInterface.php @@ -36,4 +36,10 @@ interface RuleInterface public function override($item); public function calculateRates(); + + public function regionWithNoTaxCoverage(string $iso_3166_2): bool; + + public function setEntity($entity): self; + + public function shouldCalcTax(): bool; } diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 193daf966f97..f9dc63c972b7 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -177,10 +177,12 @@ class InvoiceItemSum if (in_array($this->client->company->country()->iso_3166_2, $this->tax_jurisdictions)) { //only calculate for supported tax jurisdictions + + /** @var \App\DataMapper\Tax\BaseRule $class */ $class = "App\DataMapper\Tax\\".$this->client->company->country()->iso_3166_2."\\Rule"; $this->rule = new $class(); - + if($this->rule->regionWithNoTaxCoverage($this->client->country->iso_3166_2)) { return $this; } @@ -275,7 +277,7 @@ class InvoiceItemSum $item_tax += $item_tax_rate1_total; - if (strlen($this->item->tax_name1) > 1) { + if (strlen($this->item->tax_name1) > 2) { $this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total); } @@ -283,7 +285,7 @@ class InvoiceItemSum $item_tax += $item_tax_rate2_total; - if (strlen($this->item->tax_name2) > 1) { + if (strlen($this->item->tax_name2) > 2) { $this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total); } @@ -291,7 +293,7 @@ class InvoiceItemSum $item_tax += $item_tax_rate3_total; - if (strlen($this->item->tax_name3) > 1) { + if (strlen($this->item->tax_name3) > 2) { $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); } diff --git a/app/Helpers/Invoice/InvoiceItemSumInclusive.php b/app/Helpers/Invoice/InvoiceItemSumInclusive.php index a09bc9314a64..299de3153076 100644 --- a/app/Helpers/Invoice/InvoiceItemSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceItemSumInclusive.php @@ -231,7 +231,7 @@ class InvoiceItemSumInclusive /** @var float $item_tax */ $item_tax += $this->formatValue($item_tax_rate1_total, $this->currency->precision); - if (strlen($this->item->tax_name1) > 1) { + if (strlen($this->item->tax_name1) > 2) { $this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total); } @@ -239,7 +239,7 @@ class InvoiceItemSumInclusive $item_tax += $this->formatValue($item_tax_rate2_total, $this->currency->precision); - if (strlen($this->item->tax_name2) > 1) { + if (strlen($this->item->tax_name2) > 2) { $this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total); } @@ -247,7 +247,7 @@ class InvoiceItemSumInclusive $item_tax += $this->formatValue($item_tax_rate3_total, $this->currency->precision); - if (strlen($this->item->tax_name3) > 1) { + if (strlen($this->item->tax_name3) > 2) { $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); } diff --git a/app/Helpers/Invoice/InvoiceSumInclusive.php b/app/Helpers/Invoice/InvoiceSumInclusive.php index 8a7df5e68def..f4a57f27dedb 100644 --- a/app/Helpers/Invoice/InvoiceSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceSumInclusive.php @@ -131,20 +131,20 @@ class InvoiceSumInclusive $amount = $this->formatValue(($this->sub_total - ($this->sub_total * ($this->invoice->discount / 100))), 2); } - if ($this->invoice->tax_rate1 > 0) { + if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 2) { $tax = $this->calcInclusiveLineTax($this->invoice->tax_rate1, $amount); $this->total_taxes += $tax; $this->total_tax_map[] = ['name' => $this->invoice->tax_name1.' '.floatval($this->invoice->tax_rate1).'%', 'total' => $tax]; } - if ($this->invoice->tax_rate2 > 0) { + if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) > 2) { $tax = $this->calcInclusiveLineTax($this->invoice->tax_rate2, $amount); $this->total_taxes += $tax; $this->total_tax_map[] = ['name' => $this->invoice->tax_name2.' '.floatval($this->invoice->tax_rate2).'%', 'total' => $tax]; } - if ($this->invoice->tax_rate3 > 0) { + if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) > 2) { $tax = $this->calcInclusiveLineTax($this->invoice->tax_rate3, $amount); $this->total_taxes += $tax; $this->total_tax_map[] = ['name' => $this->invoice->tax_name3.' '.floatval($this->invoice->tax_rate3).'%', 'total' => $tax]; diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 81ae40103380..c0e4d7bd644b 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -260,6 +260,8 @@ class ImportController extends Controller } } + + /** @phpstan-ignore-next-line **/ return $bestDelimiter ?? ','; } diff --git a/app/Import/Providers/BaseImport.php b/app/Import/Providers/BaseImport.php index 297973e364ff..07b29aed0b66 100644 --- a/app/Import/Providers/BaseImport.php +++ b/app/Import/Providers/BaseImport.php @@ -153,6 +153,7 @@ class BaseImport } + /** @phpstan-ignore-next-line **/ return $bestDelimiter ?? ','; } diff --git a/app/Services/Report/ProfitLoss.php b/app/Services/Report/ProfitLoss.php index 3364eeb8c526..3fe21670abe9 100644 --- a/app/Services/Report/ProfitLoss.php +++ b/app/Services/Report/ProfitLoss.php @@ -25,6 +25,8 @@ use Illuminate\Support\Facades\App; use Illuminate\Support\Str; use League\Csv\Writer; +use function Sentry\continueTrace; + class ProfitLoss { private bool $is_income_billed = true; @@ -280,10 +282,15 @@ class ProfitLoss $tax_amount_credit = 0; $tax_amount_credit_converted = $tax_amount_credit_converted = 0; + $invoice = false; + foreach ($payment->paymentables as $pivot) { if ($pivot->paymentable_type == 'invoices') { $invoice = Invoice::query()->withTrashed()->find($pivot->paymentable_id); + if(!$invoice) + continue; + $pivot_diff = $pivot->amount - $pivot->refunded; $amount_payment_paid += $pivot_diff; $amount_payment_paid_converted += $pivot_diff * ($payment->exchange_rate ?: 1); @@ -294,6 +301,10 @@ class ProfitLoss } } + + if(!$invoice) { + continue; + } if ($pivot->paymentable_type == 'credits') { $amount_credit_paid += $pivot->amount - $pivot->refunded; From 5a0f24ea33fb12a2cb06a5379fa26692402f22eb Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 24 Feb 2024 13:28:55 +1100 Subject: [PATCH 16/37] v5.8.29 --- VERSION.txt | 2 +- config/ninja.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 7086f04f2e05..5f3d50881709 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.8.28 \ No newline at end of file +5.8.29 \ No newline at end of file diff --git a/config/ninja.php b/config/ninja.php index ec1462381359..889e6874c022 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.28'), - 'app_tag' => env('APP_TAG', '5.8.28'), + 'app_version' => env('APP_VERSION', '5.8.29'), + 'app_tag' => env('APP_TAG', '5.8.29'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), From c2037bbfec274cd6e46719508554cd05b6124e01 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 24 Feb 2024 13:39:51 +1100 Subject: [PATCH 17/37] Minor fixers --- app/Helpers/Invoice/InvoiceSum.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Helpers/Invoice/InvoiceSum.php b/app/Helpers/Invoice/InvoiceSum.php index 1d05bcef7eee..fd1472c18e7f 100644 --- a/app/Helpers/Invoice/InvoiceSum.php +++ b/app/Helpers/Invoice/InvoiceSum.php @@ -122,7 +122,7 @@ class InvoiceSum private function calculateInvoiceTaxes(): self { - if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) { + if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 2) { $tax = $this->taxer($this->total, $this->invoice->tax_rate1); $tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name1, $this->invoice->tax_rate1); @@ -130,7 +130,7 @@ class InvoiceSum $this->total_tax_map[] = ['name' => $this->invoice->tax_name1.' '.floatval($this->invoice->tax_rate1).'%', 'total' => $tax]; } - if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) > 1) { + if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) > 2) { $tax = $this->taxer($this->total, $this->invoice->tax_rate2); $tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name2, $this->invoice->tax_rate2); @@ -138,7 +138,7 @@ class InvoiceSum $this->total_tax_map[] = ['name' => $this->invoice->tax_name2.' '.floatval($this->invoice->tax_rate2).'%', 'total' => $tax]; } - if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) > 1) { + if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) > 2) { $tax = $this->taxer($this->total, $this->invoice->tax_rate3); $tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name3, $this->invoice->tax_rate3); From 3aa6bf6eb1e3afcbedfaaeaa2d358c4785c82b6e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 24 Feb 2024 13:57:44 +1100 Subject: [PATCH 18/37] Fixes for tax tests --- app/Helpers/Invoice/InvoiceSumInclusive.php | 8 ++--- tests/Unit/InvoiceInclusiveTest.php | 38 +++++++++++++++++++-- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/app/Helpers/Invoice/InvoiceSumInclusive.php b/app/Helpers/Invoice/InvoiceSumInclusive.php index f4a57f27dedb..3786b55f99e6 100644 --- a/app/Helpers/Invoice/InvoiceSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceSumInclusive.php @@ -122,7 +122,7 @@ class InvoiceSumInclusive private function calculateInvoiceTaxes() { $amount = $this->total; - + if ($this->invoice->discount > 0 && $this->invoice->is_amount_discount) { $amount = $this->formatValue(($this->sub_total - $this->invoice->discount), 2); } @@ -131,20 +131,20 @@ class InvoiceSumInclusive $amount = $this->formatValue(($this->sub_total - ($this->sub_total * ($this->invoice->discount / 100))), 2); } - if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 2) { + if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) { $tax = $this->calcInclusiveLineTax($this->invoice->tax_rate1, $amount); $this->total_taxes += $tax; $this->total_tax_map[] = ['name' => $this->invoice->tax_name1.' '.floatval($this->invoice->tax_rate1).'%', 'total' => $tax]; } - if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) > 2) { + if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) > 1) { $tax = $this->calcInclusiveLineTax($this->invoice->tax_rate2, $amount); $this->total_taxes += $tax; $this->total_tax_map[] = ['name' => $this->invoice->tax_name2.' '.floatval($this->invoice->tax_rate2).'%', 'total' => $tax]; } - if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) > 2) { + if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) > 1) { $tax = $this->calcInclusiveLineTax($this->invoice->tax_rate3, $amount); $this->total_taxes += $tax; $this->total_tax_map[] = ['name' => $this->invoice->tax_name3.' '.floatval($this->invoice->tax_rate3).'%', 'total' => $tax]; diff --git a/tests/Unit/InvoiceInclusiveTest.php b/tests/Unit/InvoiceInclusiveTest.php index e4b6b5d870d8..c5e8eba22ae0 100644 --- a/tests/Unit/InvoiceInclusiveTest.php +++ b/tests/Unit/InvoiceInclusiveTest.php @@ -66,6 +66,8 @@ class InvoiceInclusiveTest extends TestCase public function testInvoiceTotals() { + + $this->invoice_calc = new InvoiceSumInclusive($this->invoice); $this->invoice_calc->build(); $this->assertEquals($this->invoice_calc->getSubTotal(), 20); @@ -76,6 +78,8 @@ class InvoiceInclusiveTest extends TestCase { $this->invoice->discount = 5; + + $this->invoice_calc = new InvoiceSumInclusive($this->invoice); $this->invoice_calc->build(); $this->assertEquals($this->invoice_calc->getSubTotal(), 20); @@ -88,6 +92,8 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->discount = 5; $this->invoice->custom_surcharge1 = 5; + + $this->invoice_calc = new InvoiceSumInclusive($this->invoice); $this->invoice_calc->build(); $this->assertEquals($this->invoice_calc->getSubTotal(), 20); @@ -103,6 +109,8 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->tax_rate1 = 10; $this->invoice->is_amount_discount = true; + + $this->invoice_calc = new InvoiceSumInclusive($this->invoice); $this->invoice_calc->build(); $this->assertEquals($this->invoice_calc->getSubTotal(), 20); @@ -119,6 +127,7 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->tax_rate1 = 10; $this->invoice->is_amount_discount = false; + $this->invoice_calc = new InvoiceSumInclusive($this->invoice); $this->invoice_calc->build(); $this->assertEquals($this->invoice_calc->getSubTotal(), 20); @@ -135,6 +144,7 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->uses_inclusive_taxes = true; $this->invoice->is_amount_discount = true; + $this->invoice_calc = new InvoiceSumInclusive($this->invoice); $this->invoice_calc->build(); $this->assertEquals($this->invoice_calc->getSubTotal(), 20); @@ -154,6 +164,8 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->uses_inclusive_taxes = true; $this->invoice->is_amount_discount = true; + + $this->invoice_calc = new InvoiceSumInclusive($this->invoice); $this->invoice_calc->build(); $this->assertEquals($this->invoice_calc->getSubTotal(), 20); @@ -170,7 +182,10 @@ class InvoiceInclusiveTest extends TestCase $item->cost = 10; $item->tax_rate1 = 10; $item->tax_name1 = 10; - + $item->tax_rate2 = 0; + $item->tax_name2 = ''; + $item->tax_rate3 = 0; + $item->tax_name3 = ''; $line_items[] = $item; $item = InvoiceItemFactory::create(); @@ -178,6 +193,10 @@ class InvoiceInclusiveTest extends TestCase $item->cost = 10; $item->tax_rate1 = 10; $item->tax_name1 = 10; + $item->tax_rate2 = 0; + $item->tax_name2 = ''; + $item->tax_rate3 = 0; + $item->tax_name3 = ''; $line_items[] = $item; @@ -223,14 +242,17 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->discount = 0; $this->invoice->custom_surcharge1 = 0; + $this->invoice->tax_name1 = 'dog'; + $this->invoice->tax_name2 = 'cat'; $this->invoice->tax_rate1 = 10; $this->invoice->tax_rate2 = 10; + $this->invoice_calc = null; $this->invoice_calc = new InvoiceSumInclusive($this->invoice, $this->settings); $this->invoice_calc->build(); - $this->assertEquals($this->invoice_calc->getSubTotal(), 20); - $this->assertEquals($this->invoice_calc->getTotalTaxes(), 5.46); + $this->assertEquals(20, $this->invoice_calc->getSubTotal()); + $this->assertEquals(5.46, $this->invoice_calc->getTotalTaxes()); $this->assertEquals(count($this->invoice_calc->getTaxMap()), 1); $this->assertEquals($this->invoice_calc->getTotal(), 20); $this->assertEquals($this->invoice_calc->getBalance(), 20); @@ -268,6 +290,10 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->tax_rate1 = 10; $this->invoice->tax_rate2 = 10; +$this->invoice->tax_name1 = 'dog'; +$this->invoice->tax_name2 = 'cat'; + + $this->invoice_calc = null; $this->invoice_calc = new InvoiceSumInclusive($this->invoice, $this->settings); $this->invoice_calc->build(); @@ -316,6 +342,9 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->tax_rate1 = 10; $this->invoice->tax_rate2 = 10; +$this->invoice->tax_name1 = 'dog'; +$this->invoice->tax_name2 = 'cat'; + $this->invoice_calc = new InvoiceSumInclusive($this->invoice, $this->settings); $this->invoice_calc->build(); @@ -361,6 +390,9 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->tax_rate1 = 10; $this->invoice->tax_rate2 = 10; + $this->invoice->tax_name1 = 'dog'; + $this->invoice->tax_name2 = 'cat'; + $this->invoice_calc = new InvoiceSumInclusive($this->invoice, $this->settings); $this->invoice_calc->build(); From ad77aa80a0d4a8e194dbf8e291a9fa8934a42e22 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 24 Feb 2024 14:09:55 +1100 Subject: [PATCH 19/37] Fixes for tests --- app/Helpers/Invoice/InvoiceSumInclusive.php | 3 ++- tests/Unit/InvoiceInclusiveTest.php | 17 ++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/Helpers/Invoice/InvoiceSumInclusive.php b/app/Helpers/Invoice/InvoiceSumInclusive.php index 3786b55f99e6..d213874e68b2 100644 --- a/app/Helpers/Invoice/InvoiceSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceSumInclusive.php @@ -340,7 +340,8 @@ class InvoiceSumInclusive $this->total_taxes += $total_line_tax; } - +nlog($this->tax_map); +nlog($this->total_taxes); return $this; } diff --git a/tests/Unit/InvoiceInclusiveTest.php b/tests/Unit/InvoiceInclusiveTest.php index c5e8eba22ae0..0c557438cf4a 100644 --- a/tests/Unit/InvoiceInclusiveTest.php +++ b/tests/Unit/InvoiceInclusiveTest.php @@ -266,7 +266,7 @@ class InvoiceInclusiveTest extends TestCase $item->quantity = 1; $item->cost = 10; $item->tax_rate1 = 10; - $item->tax_name1 = 10; + $item->tax_name1 = 'a10'; $item->discount = 5; $line_items[] = $item; @@ -275,7 +275,7 @@ class InvoiceInclusiveTest extends TestCase $item->quantity = 1; $item->cost = 10; $item->tax_rate1 = 10; - $item->tax_name1 = 10; + $item->tax_name1 = 'a10'; $item->discount = 5; $line_items[] = $item; @@ -289,21 +289,20 @@ class InvoiceInclusiveTest extends TestCase $this->invoice->tax_rate1 = 10; $this->invoice->tax_rate2 = 10; - -$this->invoice->tax_name1 = 'dog'; -$this->invoice->tax_name2 = 'cat'; + $this->invoice->tax_name1 = 'VAT'; + $this->invoice->tax_name2 = 'VAT'; $this->invoice_calc = null; $this->invoice_calc = new InvoiceSumInclusive($this->invoice, $this->settings); $this->invoice_calc->build(); $line_items = $this->invoice_calc->invoice_items->getLineItems(); + nlog($this->invoice_calc->getTaxMap()); - $this->assertEquals($this->invoice_calc->getSubTotal(), 19); - $this->assertEquals($this->invoice_calc->getTotalDiscount(), 0.95); - $this->assertEquals($this->invoice_calc->getTotalTaxes(), 4.92); + $this->assertEquals(19, $this->invoice_calc->getSubTotal()); + $this->assertEquals(0.95, $this->invoice_calc->getTotalDiscount()); + $this->assertEquals(4.92, $this->invoice_calc->getTotalTaxes()); - // nlog($this->invoice_calc->getTaxMap()); $this->assertEquals(count($this->invoice_calc->getTaxMap()), 1); $this->assertEquals($this->invoice_calc->getTotal(), 18.05); From adf913754211902ea4545530604bf82de4a0822c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 24 Feb 2024 14:15:57 +1100 Subject: [PATCH 20/37] Set verify peer - default = true --- database/migrations/2024_02_16_011055_smtp_configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/2024_02_16_011055_smtp_configuration.php b/database/migrations/2024_02_16_011055_smtp_configuration.php index 38e7ea54e53b..df54821c1b6e 100644 --- a/database/migrations/2024_02_16_011055_smtp_configuration.php +++ b/database/migrations/2024_02_16_011055_smtp_configuration.php @@ -19,7 +19,7 @@ return new class extends Migration $table->text('smtp_username')->nullable(); $table->text('smtp_password')->nullable(); $table->string('smtp_local_domain')->nullable(); - $table->boolean('smtp_verify_peer')->default(0); + $table->boolean('smtp_verify_peer')->default(true); }); } From 9ecf49f6d5756c8253ddaaebc1c40d09a6b75542 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 24 Feb 2024 14:20:50 +1100 Subject: [PATCH 21/37] Fixes for null coalescing --- app/DataMapper/Tax/BaseRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index ccc0d46a4449..d921f2006887 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -271,7 +271,7 @@ class BaseRule implements RuleInterface public function isTaxableRegion(): bool { return $this->client->company->tax_data->regions->{$this->client_region}->tax_all_subregions || - (property_exists($this->client->company->tax_data->regions->{$this->client_region}->subregions, $this->client_subregion) && $this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->apply_tax); + (property_exists($this->client->company->tax_data->regions->{$this->client_region}->subregions, $this->client_subregion) && ($this->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->apply_tax ?? false)); } public function defaultForeign(): self From b38ddffee95716845862b89106242642ed255501 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 25 Feb 2024 09:45:51 +1100 Subject: [PATCH 22/37] Fixes for payment filters --- app/Filters/PaymentFilters.php | 2 +- app/Filters/UserFilters.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Filters/PaymentFilters.php b/app/Filters/PaymentFilters.php index 5c0b733f9398..29b596e013e8 100644 --- a/app/Filters/PaymentFilters.php +++ b/app/Filters/PaymentFilters.php @@ -164,7 +164,7 @@ class PaymentFilters extends QueryFilters { $sort_col = explode('|', $sort); - if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col, Schema::getColumnListing('payments'))) { + if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col[0], Schema::getColumnListing('payments'))) { return $this->builder; } diff --git a/app/Filters/UserFilters.php b/app/Filters/UserFilters.php index 002562933ad9..7988265e327a 100644 --- a/app/Filters/UserFilters.php +++ b/app/Filters/UserFilters.php @@ -50,7 +50,7 @@ class UserFilters extends QueryFilters { $sort_col = explode('|', $sort); - if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col, \Illuminate\Support\Facades\Schema::getColumnListing('users'))) { + if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col[0], \Illuminate\Support\Facades\Schema::getColumnListing('users'))) { return $this->builder; } From 8ffedbd128625901197dfb345d87c19c30e96d56 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 25 Feb 2024 09:46:11 +1100 Subject: [PATCH 23/37] V5.8.30 --- VERSION.txt | 2 +- config/ninja.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 5f3d50881709..40fdb9da290f 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.8.29 \ No newline at end of file +5.8.30 \ No newline at end of file diff --git a/config/ninja.php b/config/ninja.php index 889e6874c022..3f9061733a16 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.29'), - 'app_tag' => env('APP_TAG', '5.8.29'), + 'app_version' => env('APP_VERSION', '5.8.30'), + 'app_tag' => env('APP_TAG', '5.8.30'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), From 8ca9d0ff0ee3f754701e49c773d8c30420da1e68 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 26 Feb 2024 07:32:23 +1100 Subject: [PATCH 24/37] Better handling of failsafe for US State calculations --- app/DataMapper/Tax/BaseRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index d921f2006887..337ea4003119 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -264,7 +264,7 @@ class BaseRule implements RuleInterface return USStates::getState(strlen($this->client->postal_code) > 1 ? $this->client->postal_code : $this->client->shipping_postal_code); } catch (\Exception $e) { - return $this->client->company->country()->iso_3166_2 == 'US' ? $this->client->company->tax_data->seller_subregion : 'CA'; + return 'CA'; } } From bdbe8f8f6cce374825023ebce91b515a5c9ad0c7 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 28 Feb 2024 18:09:13 +1100 Subject: [PATCH 25/37] Remove redundant include --- app/Jobs/Util/ReminderJob.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index c299232d2e50..e73897378eec 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -26,7 +26,6 @@ use App\Utils\Traits\MakesReminders; use Illuminate\Support\Facades\Auth; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; -use Spatie\OpenTelemetry\Jobs\TraceAware; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; From 2858ff8946ea3b5931745a3005758d41065dcf3b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 28 Feb 2024 19:04:06 +1100 Subject: [PATCH 26/37] Additional checks --- app/Console/Commands/CheckData.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index ba95b675c5dd..b843f33cb9e4 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -947,7 +947,35 @@ class CheckData extends Command }); + Company::whereDoesntHave('company_users', function ($query){ + $query->where('is_owner', 1); + }) + ->cursor() + ->when(Ninja::isHosted()) + ->each(function ($c){ + $this->logMessage("Orphan Account # {$c->account_id}"); + + }); + + CompanyUser::whereDoesntHave('tokens') + ->cursor() + ->when(Ninja::isHosted()) + ->each(function ($cu){ + + $this->logMessage("Missing tokens for Company User # {$cu->id}"); + + }); + + + CompanyUser::whereDoesntHave('user') + ->cursor() + ->when(Ninja::isHosted()) + ->each(function ($cu) { + + $this->logMessage("Missing user for Company User # {$cu->id}"); + + }); } From 3ad5de1d2cbe53445c47966d1a3c9a413fb1e28d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 28 Feb 2024 21:15:43 +1100 Subject: [PATCH 27/37] Add filters for documents --- app/Filters/DocumentFilters.php | 39 ++++++- app/Models/Document.php | 20 ++++ app/Transformers/DocumentTransformer.php | 1 + app/Utils/SystemHealth.php | 1 + lang/en/texts.php | 1 + tests/Feature/DocumentsApiTest.php | 136 ++++++++++++++++++++++- 6 files changed, 193 insertions(+), 5 deletions(-) diff --git a/app/Filters/DocumentFilters.php b/app/Filters/DocumentFilters.php index 73b9b97a5b59..15856a2e762d 100644 --- a/app/Filters/DocumentFilters.php +++ b/app/Filters/DocumentFilters.php @@ -29,11 +29,13 @@ class DocumentFilters extends QueryFilters */ public function filter(string $filter = ''): Builder { + if (strlen($filter) == 0) { return $this->builder; } - return $this->builder; + return $this->builder->where('name', 'like', '%'.$filter.'%'); + } /** @@ -47,9 +49,42 @@ class DocumentFilters extends QueryFilters */ public function client_id(string $client_id = ''): Builder { - return $this->builder; + + return $this->builder->where(function ($query) use ($client_id) { + $query->whereHasMorph('documentable', [ + \App\Models\Invoice::class, + \App\Models\Quote::class, + \App\Models\Credit::class, + \App\Models\Expense::class, + \App\Models\Payment::class, + \App\Models\Task::class], function ($q2) use ($client_id) { + $q2->where('client_id', $this->decodePrimaryKey($client_id)); + })->orWhereHasMorph('documentable', [\App\Models\Client::class], function ($q3) use ($client_id) { + $q3->where('id', $this->decodePrimaryKey($client_id)); + }); + }); + } + public function type(string $types = '') + { + $types = explode(',', $types); + + foreach ($types as $type) + { + match($type) { + 'private' => $this->builder->where('is_public', 0), + 'public' => $this->builder->where('is_public', 1), + 'pdf' => $this->builder->where('type', 'pdf'), + 'image' => $this->builder->whereIn('type', ['png','jpeg','jpg','gif','svg']), + 'other' => $this->builder->whereNotIn('type', ['pdf','png','jpeg','jpg','gif','svg']), + default => $this->builder, + }; + } + + return $this->builder; + } + /** * Sorts the list based on $sort. * diff --git a/app/Models/Document.php b/app/Models/Document.php index 061210041ff8..8b2a45f7a508 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -208,6 +208,26 @@ class Document extends BaseModel return ctrans('texts.document'); } + public function link() + { + $entity_id = $this->encodePrimaryKey($this->documentable_id); + + match($this->documentable_type) { + 'App\Models\Vendor' => $link = "vendors/{$entity_id}", + 'App\Models\Project' => $link = "projects/{$entity_id}", + 'invoices' => $link = "invoices/{$entity_id}/edit", + 'App\Models\Quote' => $link = "quotes/{$entity_id}/edit", + 'App\Models\Credit' => $link = "credits/{$entity_id}/edit", + 'App\Models\Expense' => $link = "expenses/{$entity_id}/edit", + 'App\Models\Payment' => $link = "payments/{$entity_id}/edit", + 'App\Models\Task' => $link = "tasks/{$entity_id}/edit", + 'App\Models\Client' => $link = "clients/{$entity_id}", + default => $link = '' + }; + + return $link; + } + public function compress(): mixed { diff --git a/app/Transformers/DocumentTransformer.php b/app/Transformers/DocumentTransformer.php index c916efe9d440..baab0e4271fd 100644 --- a/app/Transformers/DocumentTransformer.php +++ b/app/Transformers/DocumentTransformer.php @@ -52,6 +52,7 @@ class DocumentTransformer extends EntityTransformer 'created_at' => (int) $document->created_at, 'is_deleted' => (bool) false, 'is_public' => (bool) $document->is_public, + 'link' => (string) $document->link(), ]; } } diff --git a/app/Utils/SystemHealth.php b/app/Utils/SystemHealth.php index 6b60a7d845e7..c862091c162e 100644 --- a/app/Utils/SystemHealth.php +++ b/app/Utils/SystemHealth.php @@ -84,6 +84,7 @@ class SystemHealth 'trailing_slash' => (bool) self::checkUrlState(), 'file_permissions' => (string) self::checkFileSystem(), 'exchange_rate_api_not_configured' => (bool)self::checkCurrencySanity(), + 'api_version' => (string) config('ninja.app_version'), ]; } diff --git a/lang/en/texts.php b/lang/en/texts.php index 11811703ee24..217ce04ed9f1 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5240,6 +5240,7 @@ $lang = array( 'use_available_payments' => 'Use Available Payments', 'test_email_sent' => 'Successfully sent email', 'gateway_type' => 'Gateway Type', + 'save_template_body' => 'Would you like to save this import mapping as a template for future use?', ); return $lang; diff --git a/tests/Feature/DocumentsApiTest.php b/tests/Feature/DocumentsApiTest.php index 2c46c6b2effb..4537019e37ae 100644 --- a/tests/Feature/DocumentsApiTest.php +++ b/tests/Feature/DocumentsApiTest.php @@ -11,13 +11,14 @@ namespace Tests\Feature; +use Tests\TestCase; +use App\Models\Task; use App\Models\Document; +use Tests\MockAccountData; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; -use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Session; -use Tests\MockAccountData; -use Tests\TestCase; +use Illuminate\Foundation\Testing\DatabaseTransactions; /** * @test @@ -44,6 +45,135 @@ class DocumentsApiTest extends TestCase Model::reguard(); } + public function testDocumentFilters() + { + Document::query()->withTrashed()->cursor()->each(function ($d){ + $d->forceDelete(); + }); + + $d = Document::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'name' => 'searchable.jpg', + 'type' => 'jpg', + ]); + + $this->client->documents()->save($d); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents/{$d->hashed_id}?client_id={$this->client->hashed_id}"); + + $response->assertStatus(200); + + $this->assertCount(1, $response->json()); + } + + + public function testDocumentFilters2() + { + Document::query()->withTrashed()->cursor()->each(function ($d){ + $d->forceDelete(); + }); + + $d = Document::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'name' => 'searchable.jpg', + 'type' => 'jpg', + ]); + + $this->task->documents()->save($d); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents/{$d->hashed_id}?client_id={$this->client->hashed_id}"); + + $response->assertStatus(200); + + $this->assertCount(1, $response->json()); + } + + public function testDocumentFilters3() + { + Document::query()->withTrashed()->cursor()->each(function ($d){ + $d->forceDelete(); + }); + + $d = Document::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'name' => 'searchable.jpg', + 'type' => 'jpg', + ]); + + $t = Task::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + ]); + + $t->documents()->save($d); + + $dd = Document::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'name' => 'searchable2.jpg', + 'type' => 'jpg', + ]); + + $this->client->documents()->save($dd); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}"); + + $response->assertStatus(200); + + $this->assertCount(2, $response->json()['data']); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=craycray"); + + $response->assertStatus(200); + + $this->assertCount(0, $response->json()['data']); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=s"); + + $response->assertStatus(200); + + $this->assertCount(2, $response->json()['data']); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=searchable"); + + $response->assertStatus(200); + + $this->assertCount(2, $response->json()['data']); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=searchable2"); + + $response->assertStatus(200); + + $this->assertCount(1, $response->json()['data']); + + } + + public function testIsPublicTypesForDocumentRequest() { $d = Document::factory()->create([ From d96a5ca4f23d0f323e56c65ac592ae573b5e7ab3 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 29 Feb 2024 12:01:02 +1100 Subject: [PATCH 28/37] V5.8.31 --- VERSION.txt | 2 +- app/Helpers/Invoice/InvoiceItemSum.php | 6 ++-- .../Invoice/InvoiceItemSumInclusive.php | 6 ++-- app/Helpers/Invoice/InvoiceSum.php | 2 ++ app/Helpers/Invoice/InvoiceSumInclusive.php | 3 +- config/ninja.php | 4 +-- tests/Unit/InvoiceItemTest.php | 36 +++++++++++++++++++ 7 files changed, 48 insertions(+), 11 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 40fdb9da290f..1dcfa2b73936 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.8.30 \ No newline at end of file +5.8.31 \ No newline at end of file diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index f9dc63c972b7..b4b8bd5980d1 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -277,7 +277,7 @@ class InvoiceItemSum $item_tax += $item_tax_rate1_total; - if (strlen($this->item->tax_name1) > 2) { + if (strlen($this->item->tax_name1) > 1) { $this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total); } @@ -285,7 +285,7 @@ class InvoiceItemSum $item_tax += $item_tax_rate2_total; - if (strlen($this->item->tax_name2) > 2) { + if (strlen($this->item->tax_name2) > 1) { $this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total); } @@ -293,7 +293,7 @@ class InvoiceItemSum $item_tax += $item_tax_rate3_total; - if (strlen($this->item->tax_name3) > 2) { + if (strlen($this->item->tax_name3) > 1) { $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); } diff --git a/app/Helpers/Invoice/InvoiceItemSumInclusive.php b/app/Helpers/Invoice/InvoiceItemSumInclusive.php index 299de3153076..a09bc9314a64 100644 --- a/app/Helpers/Invoice/InvoiceItemSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceItemSumInclusive.php @@ -231,7 +231,7 @@ class InvoiceItemSumInclusive /** @var float $item_tax */ $item_tax += $this->formatValue($item_tax_rate1_total, $this->currency->precision); - if (strlen($this->item->tax_name1) > 2) { + if (strlen($this->item->tax_name1) > 1) { $this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total); } @@ -239,7 +239,7 @@ class InvoiceItemSumInclusive $item_tax += $this->formatValue($item_tax_rate2_total, $this->currency->precision); - if (strlen($this->item->tax_name2) > 2) { + if (strlen($this->item->tax_name2) > 1) { $this->groupTax($this->item->tax_name2, $this->item->tax_rate2, $item_tax_rate2_total); } @@ -247,7 +247,7 @@ class InvoiceItemSumInclusive $item_tax += $this->formatValue($item_tax_rate3_total, $this->currency->precision); - if (strlen($this->item->tax_name3) > 2) { + if (strlen($this->item->tax_name3) > 1) { $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); } diff --git a/app/Helpers/Invoice/InvoiceSum.php b/app/Helpers/Invoice/InvoiceSum.php index fd1472c18e7f..de498a2dfa91 100644 --- a/app/Helpers/Invoice/InvoiceSum.php +++ b/app/Helpers/Invoice/InvoiceSum.php @@ -316,8 +316,10 @@ class InvoiceSum $this->tax_map = collect(); $keys = $this->invoice_items->getGroupedTaxes()->pluck('key')->unique(); +nlog($keys); $values = $this->invoice_items->getGroupedTaxes(); +nlog($values); foreach ($keys as $key) { $tax_name = $values->filter(function ($value, $k) use ($key) { diff --git a/app/Helpers/Invoice/InvoiceSumInclusive.php b/app/Helpers/Invoice/InvoiceSumInclusive.php index d213874e68b2..3786b55f99e6 100644 --- a/app/Helpers/Invoice/InvoiceSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceSumInclusive.php @@ -340,8 +340,7 @@ class InvoiceSumInclusive $this->total_taxes += $total_line_tax; } -nlog($this->tax_map); -nlog($this->total_taxes); + return $this; } diff --git a/config/ninja.php b/config/ninja.php index 3f9061733a16..d9174d53721e 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.30'), - 'app_tag' => env('APP_TAG', '5.8.30'), + 'app_version' => env('APP_VERSION', '5.8.31'), + 'app_tag' => env('APP_TAG', '5.8.31'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), diff --git a/tests/Unit/InvoiceItemTest.php b/tests/Unit/InvoiceItemTest.php index 6b736f4005e5..d3aeb1a72999 100644 --- a/tests/Unit/InvoiceItemTest.php +++ b/tests/Unit/InvoiceItemTest.php @@ -36,6 +36,42 @@ class InvoiceItemTest extends TestCase } + public function testEdgeCasewithDiscountsPercentageAndTaxCalculations() + { + $invoice = InvoiceFactory::create($this->company->id, $this->user->id); + $invoice->client_id = $this->client->id; + $invoice->uses_inclusive_taxes = false; + $invoice->is_amount_discount =false; + $invoice->discount = 0; + $invoice->tax_rate1 = 0; + $invoice->tax_rate2 = 0; + $invoice->tax_rate3 = 0; + $invoice->tax_name1 = ''; + $invoice->tax_name2 = ''; + $invoice->tax_name3 = ''; + + $line_items = []; + + $line_item = new InvoiceItem; + $line_item->quantity = 1; + $line_item->cost = 100; + $line_item->tax_rate1 = 22; + $line_item->tax_name1 = 'Km'; + $line_item->product_key = 'Test'; + $line_item->notes = 'Test'; + $line_item->is_amount_discount = false; + $line_items[] = $line_item; + + $invoice->line_items = $line_items; + $invoice->save(); + + $invoice = $invoice->calc()->getInvoice(); + + $this->assertEquals(122, $invoice->amount); + $this->assertEquals(22, $invoice->total_taxes); + } + + public function testDiscountsWithInclusiveTaxes() { $invoice = InvoiceFactory::create($this->company->id, $this->user->id); From 902cae6585e193fdf8f76d1fb60a3250885a9ece Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 29 Feb 2024 12:17:44 +1100 Subject: [PATCH 29/37] Add missing prop --- app/Helpers/Invoice/InvoiceSum.php | 2 -- app/Utils/HtmlEngine.php | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/Helpers/Invoice/InvoiceSum.php b/app/Helpers/Invoice/InvoiceSum.php index de498a2dfa91..fd1472c18e7f 100644 --- a/app/Helpers/Invoice/InvoiceSum.php +++ b/app/Helpers/Invoice/InvoiceSum.php @@ -316,10 +316,8 @@ class InvoiceSum $this->tax_map = collect(); $keys = $this->invoice_items->getGroupedTaxes()->pluck('key')->unique(); -nlog($keys); $values = $this->invoice_items->getGroupedTaxes(); -nlog($values); foreach ($keys as $key) { $tax_name = $values->filter(function ($value, $k) use ($key) { diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index db365f1e8dff..8316b6a7e4a5 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -397,7 +397,8 @@ class HtmlEngine $data['$credit.date'] = ['value' => $this->translateDate($this->entity->date, $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.credit_date')]; $data['$balance'] = ['value' => Number::formatMoney($this->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.balance')]; $data['$credit.balance'] = ['value' => Number::formatMoney($this->entity_calc->getBalance(), $this->client) ?: ' ', 'label' => ctrans('texts.credit_balance')]; - + $data['$client.credit_balance'] = &$data['$credit.balance']; + $data['$invoice.balance'] = &$data['$balance']; $data['$taxes'] = ['value' => Number::formatMoney($this->entity_calc->getItemTotalTaxes(), $this->client) ?: ' ', 'label' => ctrans('texts.taxes')]; $data['$invoice.taxes'] = &$data['$taxes']; From e22d3effc62cafed2841fa30416586c2c65db497 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 29 Feb 2024 12:31:59 +1100 Subject: [PATCH 30/37] improve parseFloat --- app/Utils/Number.php | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/app/Utils/Number.php b/app/Utils/Number.php index ca7a8ad59eca..6a10998d3538 100644 --- a/app/Utils/Number.php +++ b/app/Utils/Number.php @@ -143,6 +143,51 @@ class Number // return (float) $s; } + /* + //next iteration of float parsing + public static function parseFloatv2($value) + { + + if(!$value) { + return 0; + } + + //remove everything except for numbers, decimals, commas and hyphens + $value = preg_replace('/[^0-9.,-]+/', '', $value); + + $decimal = strpos($value, '.'); + $comma = strpos($value, ','); + + //check the 3rd last character + if(!in_array(substr($value, -3, 1), [".", ","])) { + + if($comma && (substr($value, -3, 1) != ".")) { + $value .= ".00"; + } elseif($decimal && (substr($value, -3, 1) != ",")) { + $value .= ",00"; + } + + } + + $decimal = strpos($value, '.'); + $comma = strpos($value, ','); + + if($comma === false) { //no comma must be a decimal number already + return (float) $value; + } + + if($decimal < $comma) { //decimal before a comma = euro + $value = str_replace(['.',','], ['','.'], $value); + return (float) $value; + } + + //comma first = traditional thousan separator + $value = str_replace(',', '', $value); + + return (float)$value; + + } + */ public static function parseStringFloat($value) { $value = preg_replace('/[^0-9-.]+/', '', $value); From c4807be9dfb1c3ca4fcabf906e57d8d4478af71e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 29 Feb 2024 13:06:11 +1100 Subject: [PATCH 31/37] Updates for parseformat --- app/Models/Document.php | 3 ++- app/Utils/Number.php | 11 ++++----- tests/Unit/NumberTest.php | 47 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/app/Models/Document.php b/app/Models/Document.php index 8b2a45f7a508..53be87341a4d 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -211,6 +211,7 @@ class Document extends BaseModel public function link() { $entity_id = $this->encodePrimaryKey($this->documentable_id); + $link = ''; match($this->documentable_type) { 'App\Models\Vendor' => $link = "vendors/{$entity_id}", @@ -222,7 +223,7 @@ class Document extends BaseModel 'App\Models\Payment' => $link = "payments/{$entity_id}/edit", 'App\Models\Task' => $link = "tasks/{$entity_id}/edit", 'App\Models\Client' => $link = "clients/{$entity_id}", - default => $link = '' + default => $link = '', }; return $link; diff --git a/app/Utils/Number.php b/app/Utils/Number.php index 6a10998d3538..53e030093c2c 100644 --- a/app/Utils/Number.php +++ b/app/Utils/Number.php @@ -93,7 +93,7 @@ class Number * @param string $value The formatted number to be converted back to float * @return float The formatted value */ - public static function parseFloat($value) + public static function parseFloat2($value) { if(!$value) return 0; @@ -104,7 +104,7 @@ class Number $decimal = strpos($value, '.'); $comma = strpos($value, ','); - if(!$comma) //no comma must be a decimal number already + if($comma === false) //no comma must be a decimal number already return (float) $value; if($decimal < $comma){ //decimal before a comma = euro @@ -143,9 +143,9 @@ class Number // return (float) $s; } - /* + //next iteration of float parsing - public static function parseFloatv2($value) + public static function parseFloat($value) { if(!$value) { @@ -187,7 +187,8 @@ class Number return (float)$value; } - */ + + public static function parseStringFloat($value) { $value = preg_replace('/[^0-9-.]+/', '', $value); diff --git a/tests/Unit/NumberTest.php b/tests/Unit/NumberTest.php index feecae8abf7d..538a48e338e4 100644 --- a/tests/Unit/NumberTest.php +++ b/tests/Unit/NumberTest.php @@ -20,6 +20,53 @@ use Tests\TestCase; */ class NumberTest extends TestCase { + + public function testRangeOfNumberFormats() + { + + + $floatvals = [ + "22000.76" =>"22 000,76", + "22000.76" =>"22.000,76", + "22000.76" =>"22,000.76", + "22000" =>"22 000", + "22000" =>"22,000", + "22000" =>"22.000", + "22000.76" =>"22000.76", + "22000.76" =>"22000,76", + "1022000.76" =>"1.022.000,76", + "1022000.76" =>"1,022,000.76", + "1000000" =>"1,000,000", + "1000000" =>"1.000.000", + "1022000.76" =>"1022000.76", + "1022000.76" =>"1022000,76", + "1022000" =>"1022000", + "0.76" =>"0.76", + "0.76" =>"0,76", + "0" =>"0.00", + "0" =>"0,00", + "1" =>"1.00", + "1" =>"1,00", + "423545" =>"423545 €", + "423545" =>"423,545 €", + "423545" =>"423.545 €", + "1" =>"1,00 €", + "1.02" =>"€ 1.02", + "1000.02" =>"1'000,02 EUR", + "1000.02" =>"1 000.02$", + "1000.02" =>"1,000.02$", + "1000.02" =>"1.000,02 EURO" + ]; + + + foreach($floatvals as $key => $value) { + + $this->assertEquals($key, Number::parseFloat($value)); + + } + + } + public function testNegativeFloatParse() { From 3772d2a16529a8b8657d9ed1757c9e0ae83f56d8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 29 Feb 2024 14:02:32 +1100 Subject: [PATCH 32/37] Updates for smtp --- app/Http/Controllers/SmtpController.php | 22 +++++++++---- app/Http/Requests/Smtp/CheckSmtpRequest.php | 36 ++++++++++++++++++--- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/SmtpController.php b/app/Http/Controllers/SmtpController.php index b28275e62fff..bf03012f017b 100644 --- a/app/Http/Controllers/SmtpController.php +++ b/app/Http/Controllers/SmtpController.php @@ -30,16 +30,24 @@ class SmtpController extends BaseController $user = auth()->user(); $company = $user->company(); + $smtp_host = $request->input('smtp_host', $company->smtp_host); + $smtp_port = $request->input('smtp_port', $company->smtp_port); + $smtp_username = $request->input('smtp_username', $company->smtp_username); + $smtp_password = $request->input('smtp_password', $company->smtp_password); + $smtp_encryption = $request->input('smtp_encryption', $company->smtp_encryption ?? 'tls'); + $smtp_local_domain = $request->input('smtp_local_domain', strlen($company->smtp_local_domain) > 2 ? $company->smtp_local_domain : null); + $smtp_verify_peer = $request->input('verify_peer', $company->smtp_verify_peer ?? true); + config([ 'mail.mailers.smtp' => [ 'transport' => 'smtp', - 'host' => $request->input('smtp_host', $company->smtp_host), - 'port' => $request->input('smtp_port', $company->smtp_port), - 'username' => $request->input('smtp_username', $company->smtp_username), - 'password' => $request->input('smtp_password', $company->smtp_password), - 'encryption' => $request->input('smtp_encryption', $company->smtp_encryption ?? 'tls'), - 'local_domain' => $request->input('smtp_local_domain', strlen($company->smtp_local_domain) > 2 ? $company->smtp_local_domain : null), - 'verify_peer' => $request->input('verify_peer', $company->smtp_verify_peer ?? true), + 'host' => $smtp_host, + 'port' => $smtp_port, + 'username' => $smtp_username, + 'password' => $smtp_password, + 'encryption' => $smtp_encryption, + 'local_domain' => $smtp_local_domain, + 'verify_peer' => $smtp_verify_peer, 'timeout' => 5, ], ]); diff --git a/app/Http/Requests/Smtp/CheckSmtpRequest.php b/app/Http/Requests/Smtp/CheckSmtpRequest.php index d4a4c22914af..186a68271425 100644 --- a/app/Http/Requests/Smtp/CheckSmtpRequest.php +++ b/app/Http/Requests/Smtp/CheckSmtpRequest.php @@ -36,18 +36,46 @@ class CheckSmtpRequest extends Request public function rules() { return [ + 'smtp_host' => 'sometimes|nullable|string|min:3', + 'smtp_port' => 'sometimes|nullable|integer', + 'smtp_username' => 'sometimes|nullable|string|min:3', + 'smtp_password' => 'sometimes|nullable|string|min:3', ]; } public function prepareForValidation() { + + /** @var \App\Models\User $user */ + $user = auth()->user(); + $company = $user->company(); + $input = $this->input(); - if(isset($input['smtp_username']) && $input['smtp_username'] == '********') - unset($input['smtp_username']); + if(isset($input['smtp_username']) && $input['smtp_username'] == '********'){ + // unset($input['smtp_username']); + $input['smtp_username'] = $company->smtp_username; + } + + if(isset($input['smtp_password'])&& $input['smtp_password'] == '********'){ + // unset($input['smtp_password']); + $input['smtp_password'] = $company->smtp_password; + } + + if(isset($input['smtp_host']) && strlen($input['smtp_host']) >=3){ + + } + else { + $input['smtp_host'] = $company->smtp_host; + } + + + if(isset($input['smtp_port']) && strlen($input['smtp_port']) >= 3) { + + } else { + $input['smtp_port'] = $company->smtp_port; + } - if(isset($input['smtp_password'])&& $input['smtp_password'] == '********') - unset($input['smtp_password']); $this->replace($input); } From 631527e8e3a0bfd688a721aa1a13fda1be2716fe Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 29 Feb 2024 19:58:59 +1100 Subject: [PATCH 33/37] Fixes for redirect after stripe connect --- app/Http/Controllers/StripeConnectController.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/StripeConnectController.php b/app/Http/Controllers/StripeConnectController.php index a8de13091bf4..e28eb61d858f 100644 --- a/app/Http/Controllers/StripeConnectController.php +++ b/app/Http/Controllers/StripeConnectController.php @@ -54,6 +54,8 @@ class StripeConnectController extends BaseController $redirect_uri = config('ninja.app_url').'/stripe/completed'; $endpoint = "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={$stripe_client_id}&redirect_uri={$redirect_uri}&scope=read_write&state={$token}"; + \Illuminate\Support\Facades\Cache::pull($token); + return redirect($endpoint); } @@ -148,7 +150,8 @@ class StripeConnectController extends BaseController } //response here - return view('auth.connect.completed', ['url' => $redirect_uri]); + // return view('auth.connect.completed', ['url' => $redirect_uri]); + return redirect($redirect_uri); } } From d9184bb0ab4c738f8e39736c6e9ad4547d3111ee Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 29 Feb 2024 21:34:29 +1100 Subject: [PATCH 34/37] Fixes for stripe connect --- app/Http/Controllers/StripeConnectController.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/StripeConnectController.php b/app/Http/Controllers/StripeConnectController.php index e28eb61d858f..3aa9639a48c0 100644 --- a/app/Http/Controllers/StripeConnectController.php +++ b/app/Http/Controllers/StripeConnectController.php @@ -66,6 +66,8 @@ class StripeConnectController extends BaseController if ($request->has('error') && $request->error == 'access_denied') { return view('auth.connect.access_denied'); } + + $response = false; try { /** @class \stdClass $response @@ -90,6 +92,11 @@ class StripeConnectController extends BaseController nlog($response); } catch (\Exception $e) { + + + } + + if(!$response) { return view('auth.connect.access_denied'); } @@ -146,12 +153,12 @@ class StripeConnectController extends BaseController if(isset($request->getTokenContent()['is_react']) && $request->getTokenContent()['is_react']) { $redirect_uri = config('ninja.react_url').'/#/settings/online_payments'; } else { - $redirect_uri = config('ninja.app_url').'/stripe/completed'; + $redirect_uri = config('ninja.app_url'); } //response here - // return view('auth.connect.completed', ['url' => $redirect_uri]); - return redirect($redirect_uri); + return view('auth.connect.completed', ['url' => $redirect_uri]); + // return redirect($redirect_uri); } } From 52f1c0bcfe77a7aa75734df689deb309df8e1c58 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 1 Mar 2024 08:58:56 +1100 Subject: [PATCH 35/37] Updates for client document filters --- app/Filters/DocumentFilters.php | 6 +++++- app/Http/Controllers/ClientController.php | 5 ++++- app/Models/Document.php | 18 +++++++++--------- lang/en/texts.php | 1 + lang/fr_CA/texts.php | 4 ++++ 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/app/Filters/DocumentFilters.php b/app/Filters/DocumentFilters.php index 15856a2e762d..f649ccca1767 100644 --- a/app/Filters/DocumentFilters.php +++ b/app/Filters/DocumentFilters.php @@ -57,7 +57,11 @@ class DocumentFilters extends QueryFilters \App\Models\Credit::class, \App\Models\Expense::class, \App\Models\Payment::class, - \App\Models\Task::class], function ($q2) use ($client_id) { + \App\Models\Task::class, + \App\Models\RecurringExpense::class, + \App\Models\RecurringInvoice::class, + \App\Models\Project::class, + ], function ($q2) use ($client_id) { $q2->where('client_id', $this->decodePrimaryKey($client_id)); })->orWhereHasMorph('documentable', [\App\Models\Client::class], function ($q3) use ($client_id) { $q3->where('id', $this->decodePrimaryKey($client_id)); diff --git a/app/Http/Controllers/ClientController.php b/app/Http/Controllers/ClientController.php index 5f43373501e3..d6f8a8a8fd2d 100644 --- a/app/Http/Controllers/ClientController.php +++ b/app/Http/Controllers/ClientController.php @@ -49,6 +49,9 @@ use App\Http\Requests\Client\ClientDocumentsRequest; use App\Http\Requests\Client\ReactivateClientEmailRequest; use App\Models\Expense; use App\Models\Payment; +use App\Models\Project; +use App\Models\RecurringExpense; +use App\Models\RecurringInvoice; use App\Models\Task; use App\Transformers\DocumentTransformer; @@ -421,7 +424,7 @@ class ClientController extends BaseController $documents = Document::query() ->company() - ->whereHasMorph('documentable', [Invoice::class, Quote::class, Credit::class, Expense::class, Payment::class, Task::class], function ($query) use ($client) { + ->whereHasMorph('documentable', [Invoice::class, Quote::class, Credit::class, Expense::class, Payment::class, Task::class, RecurringInvoice::class, RecurringExpense::class, Project::class], function ($query) use ($client) { $query->where('client_id', $client->id); }) ->orWhereHasMorph('documentable', [Client::class], function ($query) use ($client) { diff --git a/app/Models/Document.php b/app/Models/Document.php index 53be87341a4d..2c8948070ae0 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -214,15 +214,15 @@ class Document extends BaseModel $link = ''; match($this->documentable_type) { - 'App\Models\Vendor' => $link = "vendors/{$entity_id}", - 'App\Models\Project' => $link = "projects/{$entity_id}", - 'invoices' => $link = "invoices/{$entity_id}/edit", - 'App\Models\Quote' => $link = "quotes/{$entity_id}/edit", - 'App\Models\Credit' => $link = "credits/{$entity_id}/edit", - 'App\Models\Expense' => $link = "expenses/{$entity_id}/edit", - 'App\Models\Payment' => $link = "payments/{$entity_id}/edit", - 'App\Models\Task' => $link = "tasks/{$entity_id}/edit", - 'App\Models\Client' => $link = "clients/{$entity_id}", + 'App\Models\Vendor' => $link = "/vendors/{$entity_id}", + 'App\Models\Project' => $link = "/projects/{$entity_id}", + 'invoices' => $link = "/invoices/{$entity_id}/edit", + 'App\Models\Quote' => $link = "/quotes/{$entity_id}/edit", + 'App\Models\Credit' => $link = "/credits/{$entity_id}/edit", + 'App\Models\Expense' => $link = "/expenses/{$entity_id}/edit", + 'App\Models\Payment' => $link = "/payments/{$entity_id}/edit", + 'App\Models\Task' => $link = "/tasks/{$entity_id}/edit", + 'App\Models\Client' => $link = "/clients/{$entity_id}", default => $link = '', }; diff --git a/lang/en/texts.php b/lang/en/texts.php index 217ce04ed9f1..507dfdc76a88 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5241,6 +5241,7 @@ $lang = array( 'test_email_sent' => 'Successfully sent email', 'gateway_type' => 'Gateway Type', 'save_template_body' => 'Would you like to save this import mapping as a template for future use?', + 'save_as_template' => 'Save Template Mapping' ); return $lang; diff --git a/lang/fr_CA/texts.php b/lang/fr_CA/texts.php index 1020490d5777..497ed891308a 100644 --- a/lang/fr_CA/texts.php +++ b/lang/fr_CA/texts.php @@ -5234,6 +5234,10 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'user_sales' => 'Ventes de l\'utilisateur', 'iframe_url' => 'URL de l\'iFrame', 'user_unsubscribed' => 'Utilisateur désabonné des courriels :link', + 'use_available_payments' => 'Utilisez les paiements disponibles', + 'test_email_sent' => 'Le courriel a été envoyé', + 'gateway_type' => 'Type de passerelle', + 'save_template_body' => 'Souhaitez-vous enregistrer cette correspondance d\'importation en tant que modèle pour une utilisation future ?', ); return $lang; From 6a127e3c5b0b94a8fb3656521681d7ac49905235 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 1 Mar 2024 10:34:05 +1100 Subject: [PATCH 36/37] Updates for document links --- app/Models/Document.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Models/Document.php b/app/Models/Document.php index 2c8948070ae0..896d3015a2de 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -223,6 +223,8 @@ class Document extends BaseModel 'App\Models\Payment' => $link = "/payments/{$entity_id}/edit", 'App\Models\Task' => $link = "/tasks/{$entity_id}/edit", 'App\Models\Client' => $link = "/clients/{$entity_id}", + 'App\Models\RecurringExpense' => $link = "/recurring_expenses/{$entity_id}/edit", + 'App\Models\RecurringInvoice' => $link = "/recurring_invoices/{$entity_id}/edit", default => $link = '', }; From 82117f83619ccde4fe86570f382443536417b680 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 2 Mar 2024 10:14:36 +1100 Subject: [PATCH 37/37] v5.8.32 --- VERSION.txt | 2 +- app/Helpers/Invoice/InvoiceSum.php | 6 +++--- config/ninja.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 1dcfa2b73936..577d1e25e9a9 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.8.31 \ No newline at end of file +5.8.32 \ No newline at end of file diff --git a/app/Helpers/Invoice/InvoiceSum.php b/app/Helpers/Invoice/InvoiceSum.php index fd1472c18e7f..d6379356cd11 100644 --- a/app/Helpers/Invoice/InvoiceSum.php +++ b/app/Helpers/Invoice/InvoiceSum.php @@ -122,7 +122,7 @@ class InvoiceSum private function calculateInvoiceTaxes(): self { - if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 2) { + if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) >= 2) { $tax = $this->taxer($this->total, $this->invoice->tax_rate1); $tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name1, $this->invoice->tax_rate1); @@ -130,7 +130,7 @@ class InvoiceSum $this->total_tax_map[] = ['name' => $this->invoice->tax_name1.' '.floatval($this->invoice->tax_rate1).'%', 'total' => $tax]; } - if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) > 2) { + if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) >= 2) { $tax = $this->taxer($this->total, $this->invoice->tax_rate2); $tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name2, $this->invoice->tax_rate2); @@ -138,7 +138,7 @@ class InvoiceSum $this->total_tax_map[] = ['name' => $this->invoice->tax_name2.' '.floatval($this->invoice->tax_rate2).'%', 'total' => $tax]; } - if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) > 2) { + if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) >= 2) { $tax = $this->taxer($this->total, $this->invoice->tax_rate3); $tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name3, $this->invoice->tax_rate3); diff --git a/config/ninja.php b/config/ninja.php index d9174d53721e..660b465a201b 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.31'), - 'app_tag' => env('APP_TAG', '5.8.31'), + 'app_version' => env('APP_VERSION', '5.8.32'), + 'app_tag' => env('APP_TAG', '5.8.32'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false),