Merge pull request #7149 from turbo124/v5-stable

v5.3.49
This commit is contained in:
David Bomba 2022-01-21 10:16:05 +11:00 committed by GitHub
commit 0e89efa331
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 174881 additions and 168853 deletions

View File

@ -5,6 +5,7 @@
![v5-develop phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-develop) ![v5-develop phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-develop)
![v5-stable phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-stable) ![v5-stable phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-stable)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/d16c78aad8574466bf83232b513ef4fb)](https://www.codacy.com/gh/turbo124/invoiceninja/dashboard?utm_source=github.com&utm_medium=referral&utm_content=turbo124/invoiceninja&utm_campaign=Badge_Grade) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/d16c78aad8574466bf83232b513ef4fb)](https://www.codacy.com/gh/turbo124/invoiceninja/dashboard?utm_source=github.com&utm_medium=referral&utm_content=turbo124/invoiceninja&utm_campaign=Badge_Grade)
<a href="https://cla-assistant.io/invoiceninja/invoiceninja"><img src="https://cla-assistant.io/readme/badge/invoiceninja/invoiceninja" alt="CLA assistant" /></a>
# Invoice Ninja 5 # Invoice Ninja 5

View File

@ -1 +1 @@
5.3.48 5.3.49

View File

@ -190,9 +190,7 @@ abstract class QueryFilters
return $this->builder; return $this->builder;
} }
$this->builder->where('client_id', $this->decodePrimaryKey($client_id)); return $this->builder->where('client_id', $this->decodePrimaryKey($client_id));
return $this->builder;
} }

View File

@ -0,0 +1,76 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers;
use App\Http\Requests\Chart\ShowChartRequest;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class ChartController extends BaseController
{
public function __construct()
{
parent::__construct();
}
/**
* @OA\Post(
* path="/api/v1/charts",
* operationId="getCharts",
* tags={"charts"},
* summary="Get chart data",
* description="Get chart data",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\Parameter(
* name="rows",
* in="query",
* description="The number of activities to return",
* example="50",
* required=false,
* @OA\Schema(
* type="number",
* format="integer",
* ),
* ),
* @OA\Response(
* response=200,
* description="json dataset of chart data",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
* @param Request $request
* @return Response|mixed
*/
public function index(ShowChartRequest $request)
{
return response()->json([],200);
}
}

View File

@ -119,6 +119,9 @@ class PaymentController extends Controller
} else { } else {
$payment = PaymentFactory::create($payment_hash->fee_invoice->company_id, $payment_hash->fee_invoice->user_id); $payment = PaymentFactory::create($payment_hash->fee_invoice->company_id, $payment_hash->fee_invoice->user_id);
$payment->client_id = $payment_hash->fee_invoice->client_id; $payment->client_id = $payment_hash->fee_invoice->client_id;
$payment->saveQuietly();
$payment->currency_id = $payment->client->getSetting('currency_id');
$payment->saveQuietly(); $payment->saveQuietly();
$payment_hash->payment_id = $payment->id; $payment_hash->payment_id = $payment->id;

View File

@ -357,6 +357,57 @@ class DesignController extends BaseController
$design->fill($request->all()); $design->fill($request->all());
$design->save(); $design->save();
/*
This is required as the base template does not know to inject the table elements
*/
$properties = ['includes','header','body','footer'];
$d = $design->design;
$old_header = '<div class="repeating-header" id="header"></div>';
$new_header = '<table style="min-width: 100%">
<thead>
<tr>
<td>
<div class="repeating-header-space">&nbsp;</div>
</td>
</tr>
</thead>
<tbody>
<tr>
<td>';
$old_footer = '<div class="repeating-footer" id="footer">';
$new_footer = '</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
<div class="repeating-footer-space">&nbsp;</div>
</td>
</tr>
</tfoot>
</table>
<div class="repeating-header" id="header"></div>
<div class="repeating-footer" id="footer">';
foreach($properties as $property){
$d->{$property} = str_replace($old_header, $new_header, $d->{$property});
$d->{$property} = str_replace($old_footer, $new_footer, $d->{$property});
}
$design->design = $d;
$design->save();
/*
This is required as the base template does not know to inject the table elements
*/
return $this->itemResponse($design->fresh()); return $this->itemResponse($design->fresh());
} }

View File

@ -0,0 +1,34 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\Chart;
use App\Http\Requests\Request;
use App\Models\Activity;
class ShowChartRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
public function rules()
{
return [
];
}
}

View File

@ -82,10 +82,10 @@ class BaseTransformer
if ( $contacts->count() >= 1 ) { if ( $contacts->count() >= 1 ) {
return $contacts->first()->client_id; return $contacts->first()->client_id;
nlog("found via contact"); nlog("found via contact");
} }
} }
nlog("did not find client"); nlog("did not find client");
return null; return null;
} }

View File

@ -240,6 +240,9 @@ class PaymentEmailEngine extends BaseEmailEngine
$data['$invoices'] = ['value' => $this->formatInvoices(), 'label' => ctrans('texts.invoices')]; $data['$invoices'] = ['value' => $this->formatInvoices(), 'label' => ctrans('texts.invoices')];
$data['$invoice_references'] = ['value' => $this->formatInvoiceReferences(), 'label' => ctrans('texts.invoices')]; $data['$invoice_references'] = ['value' => $this->formatInvoiceReferences(), 'label' => ctrans('texts.invoices')];
$data['$invoice'] = ['value' => $this->formatInvoice(), 'label' => ctrans('texts.invoices')]; $data['$invoice'] = ['value' => $this->formatInvoice(), 'label' => ctrans('texts.invoices')];
$data['$invoice.po_number'] = ['value' => $this->formatPoNumber(), 'label' => ctrans('texts.po_number')];
$data['$poNumber'] = &$data['$invoice.po_number'];
return $data; return $data;
} }
@ -253,6 +256,16 @@ class PaymentEmailEngine extends BaseEmailEngine
return $invoice; return $invoice;
} }
private function formatPoNumber()
{
$invoice = '';
if($this->payment->invoices()->exists())
$invoice = ctrans('texts.po_number_short') . implode(",", $this->payment->invoices->pluck('po_number')->toArray());
return $invoice;
}
private function formatInvoices() private function formatInvoices()
{ {
$invoice_list = '<br><br>'; $invoice_list = '<br><br>';

View File

@ -109,7 +109,7 @@ class TemplateEmail extends Mailable
'settings' => $settings, 'settings' => $settings,
'company' => $company, 'company' => $company,
'whitelabel' => $this->client->user->account->isPaid() ? true : false, 'whitelabel' => $this->client->user->account->isPaid() ? true : false,
'logo' => $this->company->present()->logo(), 'logo' => $this->company->present()->logo($settings),
]) ])
->withSwiftMessage(function ($message) use($company){ ->withSwiftMessage(function ($message) use($company){
$message->getHeaders()->addTextHeader('Tag', $company->company_key); $message->getHeaders()->addTextHeader('Tag', $company->company_key);

View File

@ -114,7 +114,8 @@ class Gateway extends StaticModel
GatewayType::BANCONTACT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], GatewayType::BANCONTACT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']]]; GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']]];
case 39: case 39:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout
break; break;

View File

@ -38,6 +38,7 @@ class GatewayType extends StaticModel
const ACSS = 19; const ACSS = 19;
const BECS = 20; const BECS = 20;
const INSTANT_BANK_PAY = 21; const INSTANT_BANK_PAY = 21;
const FPX = 22;
public function gateway() public function gateway()
{ {
@ -92,6 +93,8 @@ class GatewayType extends StaticModel
return ctrans('texts.payment_type_direct_debit'); return ctrans('texts.payment_type_direct_debit');
case self::INSTANT_BANK_PAY: case self::INSTANT_BANK_PAY:
return ctrans('texts.payment_type_instant_bank_pay'); return ctrans('texts.payment_type_instant_bank_pay');
case self::FPX:
return ctrans('texts.fpx');
default: default:
return 'Undefined.'; return 'Undefined.';
break; break;

View File

@ -54,6 +54,7 @@ class PaymentType extends StaticModel
const BECS = 43; const BECS = 43;
const ACSS = 44; const ACSS = 44;
const INSTANT_BANK_PAY = 45; const INSTANT_BANK_PAY = 45;
const FPX = 46;
public static function parseCardType($cardName) public static function parseCardType($cardName)
{ {

View File

@ -0,0 +1,141 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\PaymentDrivers\Stripe;
use App\Exceptions\PaymentFailed;
use App\Jobs\Mail\PaymentFailureMailer;
use App\Jobs\Util\SystemLogger;
use App\Models\GatewayType;
use App\Models\Payment;
use App\Models\PaymentType;
use App\Models\SystemLog;
use App\PaymentDrivers\StripePaymentDriver;
class FPX
{
/** @var StripePaymentDriver */
public StripePaymentDriver $stripe;
public function __construct(StripePaymentDriver $stripe)
{
$this->stripe = $stripe;
}
public function authorizeView($data)
{
return render('gateways.stripe.fpx.authorize', $data);
}
public function paymentView(array $data)
{
$data['gateway'] = $this->stripe;
$data['return_url'] = $this->buildReturnUrl();
$data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency());
$data['client'] = $this->stripe->client;
$data['customer'] = $this->stripe->findOrCreateCustomer()->id;
$data['country'] = $this->stripe->client->country->iso_3166_2;
$intent = \Stripe\PaymentIntent::create([
'amount' => $data['stripe_amount'],
'currency' => 'eur',
'payment_method_types' => ['fpx'],
'customer' => $this->stripe->findOrCreateCustomer(),
'description' => $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')),
]);
$data['pi_client_secret'] = $intent->client_secret;
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]);
$this->stripe->payment_hash->save();
return render('gateways.stripe.fpx.pay', $data);
}
private function buildReturnUrl(): string
{
return route('client.payments.response', [
'company_gateway_id' => $this->stripe->company_gateway->id,
'payment_hash' => $this->stripe->payment_hash->hash,
'payment_method_id' => GatewayType::FPX,
]);
}
public function paymentResponse($request)
{
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $request->all());
$this->stripe->payment_hash->save();
if ($request->redirect_status == 'succeeded') {
return $this->processSuccessfulPayment($request->payment_intent);
}
return $this->processUnsuccessfulPayment();
}
public function processSuccessfulPayment(string $payment_intent)
{
/* @todo: https://github.com/invoiceninja/invoiceninja/pull/3789/files#r436175798 */
$this->stripe->init();
$data = [
'payment_method' => $payment_intent,
'payment_type' => PaymentType::FPX,
'amount' => $this->stripe->convertFromStripeAmount($this->stripe->payment_hash->data->stripe_amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
'transaction_reference' => $payment_intent,
'gateway_type_id' => GatewayType::FPX,
];
$this->stripe->createPayment($data, Payment::STATUS_PENDING);
SystemLogger::dispatch(
['response' => $this->stripe->payment_hash->data, 'data' => $data],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_STRIPE,
$this->stripe->client,
$this->stripe->client->company,
);
return redirect()->route('client.payments.index');
}
public function processUnsuccessfulPayment()
{
$server_response = $this->stripe->payment_hash->data;
PaymentFailureMailer::dispatch(
$this->stripe->client,
$server_response,
$this->stripe->client->company,
$this->stripe->convertFromStripeAmount($this->stripe->payment_hash->data->stripe_amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency())
);
$message = [
'server_response' => $server_response,
'data' => $this->stripe->payment_hash->data,
];
SystemLogger::dispatch(
$message,
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_STRIPE,
$this->stripe->client,
$this->stripe->client->company,
);
throw new PaymentFailed('Failed to process the payment.', 500);
}
}

View File

@ -41,6 +41,7 @@ use App\PaymentDrivers\Stripe\Bancontact;
use App\PaymentDrivers\Stripe\BECS; use App\PaymentDrivers\Stripe\BECS;
use App\PaymentDrivers\Stripe\ACSS; use App\PaymentDrivers\Stripe\ACSS;
use App\PaymentDrivers\Stripe\BrowserPay; use App\PaymentDrivers\Stripe\BrowserPay;
use App\PaymentDrivers\Stripe\FPX;
use App\PaymentDrivers\Stripe\UpdatePaymentMethods; use App\PaymentDrivers\Stripe\UpdatePaymentMethods;
use App\PaymentDrivers\Stripe\Utilities; use App\PaymentDrivers\Stripe\Utilities;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
@ -198,6 +199,13 @@ class StripePaymentDriver extends BaseDriver
&& in_array($this->client->country->iso_3166_3, ["AUT"])) && in_array($this->client->country->iso_3166_3, ["AUT"]))
$types[] = GatewayType::EPS; $types[] = GatewayType::EPS;
if ($this->client
&& $this->client->currency()
&& ($this->client->currency()->code == 'MYR')
&& isset($this->client->country)
&& in_array($this->client->country->iso_3166_3, ["MYS"]))
$types[] = GatewayType::FPX;
if ($this->client if ($this->client
&& $this->client->currency() && $this->client->currency()
&& ($this->client->currency()->code == 'EUR') && ($this->client->currency()->code == 'EUR')
@ -266,6 +274,8 @@ class StripePaymentDriver extends BaseDriver
return 'gateways.stripe.becs'; return 'gateways.stripe.becs';
case GatewayType::ACSS: case GatewayType::ACSS:
return 'gateways.stripe.acss'; return 'gateways.stripe.acss';
case GatewayType::FPX:
return 'gateways.stripe.fpx';
default: default:
break; break;
} }

View File

@ -106,6 +106,7 @@ class PaymentRepository extends BaseRepository {
$payment->fill($data); $payment->fill($data);
$payment->is_manual = true; $payment->is_manual = true;
$payment->status_id = Payment::STATUS_COMPLETED; $payment->status_id = Payment::STATUS_COMPLETED;
$payment->save(); $payment->save();
/*Save documents*/ /*Save documents*/
@ -207,6 +208,7 @@ class PaymentRepository extends BaseRepository {
$payment->exchange_rate = $exchange_rate->exchangeRate($client_currency, $company_currency, Carbon::parse($payment->date)); $payment->exchange_rate = $exchange_rate->exchangeRate($client_currency, $company_currency, Carbon::parse($payment->date));
// $payment->exchange_currency_id = $client_currency; // $payment->exchange_currency_id = $client_currency;
$payment->exchange_currency_id = $company_currency; $payment->exchange_currency_id = $company_currency;
$payment->currency_id = $client_currency;
} }

View File

@ -0,0 +1,80 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\Chart;
use Illuminate\Support\Facades\DB;
/**
* Class ChartQueries.
*/
trait ChartQueries
{
public function getRevenueQuery($start_date, $end_date)
{
return DB::select( DB::raw("
SELECT
sum(invoices.paid_to_date) as paid_to_date,
JSON_EXTRACT( settings, '$.currency_id' ) AS currency_id
FROM clients
JOIN invoices
on invoices.client_id = clients.id
WHERE invoices.status_id IN (3,4)
AND invoices.company_id = :company_id
AND invoices.amount > 0
AND clients.is_deleted = 0
AND invoices.is_deleted = 0
AND (invoices.date BETWEEN :start_date AND :end_date)
GROUP BY currency_id
"), ['company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date] );
}
public function getOutstandingQuery($start_date, $end_date)
{
return DB::select( DB::raw("
SELECT
sum(invoices.balance) as balance,
JSON_EXTRACT( settings, '$.currency_id' ) AS currency_id
FROM clients
JOIN invoices
on invoices.client_id = clients.id
WHERE invoices.status_id IN (2,3)
AND invoices.company_id = :company_id
AND invoices.balance > 0
AND clients.is_deleted = 0
AND invoices.is_deleted = 0
AND (invoices.due_date BETWEEN :start_date AND :end_date)
GROUP BY currency_id
"), ['company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date] );
}
public function getExpenseQuery($start_date, $end_date)
{
return DB::select( DB::raw("
SELECT sum(expenses.amount) as amount,
expenses.currency_id as currency_id
FROM expenses
WHERE expenses.is_deleted = 0
AND expenses.company_id = :company_id
AND (expenses.date BETWEEN :start_date AND :end_date)
GROUP BY currency_id
"), ['company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date] );
}
}

View File

@ -0,0 +1,197 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\Chart;
use App\Models\Client;
use App\Models\Company;
use App\Models\Expense;
use App\Models\Payment;
use App\Services\Chart\ChartQueries;
use Illuminate\Support\Facades\Cache;
class ChartService
{
use ChartQueries;
public Company $company;
public function __construct(Company $company)
{
$this->company = $company;
}
/**
* Returns an array of currencies that have
* transacted with a company
*/
public function getCurrencyCodes() :array
{
// $currencies = Payment::withTrashed()
// ->where('company_id', $this->company->id)
// ->where('is_deleted', 0)
// ->distinct()
// ->get(['currency_id']);
/* Get all the distinct client currencies */
$currencies = Client::withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->distinct()
->pluck('settings->currency_id as id');
/* Push the company currency on also */
$currencies->push((int)$this->company->settings->currency_id);
/* Add our expense currencies*/
$expense_currencies = Expense::withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->distinct()
->get(['currency_id']);
/* Merge and filter by unique */
$currencies = $currencies->merge($expense_currencies)->unique();
$cache_currencies = Cache::get('currencies');
$filtered_currencies = $cache_currencies->whereIn('id', $currencies)->all();
$final_currencies = [];
foreach($filtered_currencies as $c_currency)
{
$final_currencies[$c_currency['id']] = $c_currency['code'];
}
return $final_currencies;
}
public function totals($start_date, $end_date)
{
$data = [];
$data['revenue'] = $this->getRevenue($start_date, $end_date);
$data['outstanding'] = $this->getOutstanding($start_date, $end_date);
$data['expenses'] = $this->getExpenses($start_date, $end_date);
return $data;
}
public function oustanding($start_date, $end_date)
{
$company_currency = (int) $this->company->settings->currency_id;
$results = \DB::select( \DB::raw("
SELECT
sum(invoices.balance) as balance,
JSON_EXTRACT( settings, '$.currency_id' ) AS currency_id
FROM clients
JOIN invoices
on invoices.client_id = clients.id
WHERE invoices.status_id IN (2,3)
AND invoices.company_id = :company_id
AND invoices.balance > 0
AND clients.is_deleted = 0
AND invoices.is_deleted = 0
AND (invoices.due_date BETWEEN :start_date AND :end_date)
GROUP BY currency_id
"), ['company_id' => $this->company->id, 'start_date' => $start_date, 'end_date' => $end_date] );
//return $results;
//the output here will most likely contain a currency_id = null value - we need to merge this value with the company currency
}
private function getRevenue($start_date, $end_date)
{
$revenue = $this->getRevenueQuery($start_date, $end_date);
$revenue = $this->parseTotals($revenue);
$revenue = $this->addCountryCodes($revenue);
return $revenue;
}
private function getOutstanding($start_date, $end_date)
{
$outstanding = $this->getOutstandingQuery($start_date, $end_date);
$outstanding = $this->parseTotals($outstanding);
$outstanding = $this->addCountryCodes($outstanding);
return $outstanding;
}
private function getExpenses($start_date, $end_date)
{
$expenses = $this->getExpenseQuery($start_date, $end_date);
$expenses = $this->parseTotals($expenses);
$expenses = $this->addCountryCodes($expenses);
return $expenses;
}
private function parseTotals($data_set)
{
/* Find the key where the company currency amount lives*/
$c_key = array_search($this->company->id , array_column($data_set, 'currency_id'));
if(!$c_key)
return $data_set;
/* Find the key where null currency_id lives */
$key = array_search(null , array_column($data_set, 'currency_id'));
if(!$key)
return $data_set;
$null_currency_amount = $data_set[$key]['amount'];
unset($data_set[$key]);
$data_set[$c_key]['amount'] += $null_currency_amount;
return $data_set;
}
private function addCountryCodes($data_set)
{
$currencies = Cache::get('currencies');
foreach($data_set as $key => $value)
{
$data_set[$key]['code'] = $this->getCode($currencies, $value);
}
return $data_set;
}
private function getCode($currencies, $currency_id)
{
$currency = $currencies->filter(function ($item) use($currency_id) {
return $item->id == $currency_id;
})->first();
if($currency)
return $currency->code;
return '';
}
}

View File

@ -12,6 +12,7 @@
namespace App\Services\Credit; namespace App\Services\Credit;
use App\Factory\PaymentFactory; use App\Factory\PaymentFactory;
use App\Jobs\Entity\CreateEntityPdf;
use App\Jobs\Util\UnlinkFile; use App\Jobs\Util\UnlinkFile;
use App\Models\Credit; use App\Models\Credit;
use App\Models\Payment; use App\Models\Payment;
@ -98,6 +99,8 @@ class CreditService
if($this->credit->balance > 0) if($this->credit->balance > 0)
return $this; return $this;
$this->markSent();
$payment_repo = new PaymentRepository(new CreditRepository()); $payment_repo = new PaymentRepository(new CreditRepository());
//set credit balance to zero //set credit balance to zero
@ -116,6 +119,7 @@ class CreditService
$payment->status_id = Payment::STATUS_COMPLETED; $payment->status_id = Payment::STATUS_COMPLETED;
$payment->type_id = PaymentType::CREDIT; $payment->type_id = PaymentType::CREDIT;
$payment->is_manual = true; $payment->is_manual = true;
$payment->currency_id = $this->credit->client->getSetting('currency_id');
$payment->date = now(); $payment->date = now();
$payment->saveQuietly(); $payment->saveQuietly();
@ -132,6 +136,7 @@ class CreditService
->client ->client
->service() ->service()
->updatePaidToDate($adjustment) ->updatePaidToDate($adjustment)
->setStatus(Credit::STATUS_APPLIED)
->save(); ->save();
event('eloquent.created: App\Models\Payment', $payment); event('eloquent.created: App\Models\Payment', $payment);
@ -176,6 +181,38 @@ class CreditService
return $this; return $this;
} }
/**
* Sometimes we need to refresh the
* PDF when it is updated etc.
* @return InvoiceService
*/
public function touchPdf($force = false)
{
try {
if($force){
$this->credit->invitations->each(function ($invitation) {
CreateEntityPdf::dispatchNow($invitation);
});
return $this;
}
$this->credit->invitations->each(function ($invitation) {
CreateEntityPdf::dispatch($invitation);
});
}
catch(\Exception $e){
nlog("failed creating invoices in Touch PDF");
}
return $this;
}
public function fillDefaults() public function fillDefaults()
{ {
$settings = $this->credit->client->getMergedSettings(); $settings = $this->credit->client->getMergedSettings();

View File

@ -42,7 +42,7 @@ class MarkSent
->setStatus(Credit::STATUS_SENT) ->setStatus(Credit::STATUS_SENT)
->applyNumber() ->applyNumber()
->adjustBalance($this->credit->amount) ->adjustBalance($this->credit->amount)
->deletePdf() ->touchPdf()
->save(); ->save();
event(new CreditWasMarkedSent($this->credit, $this->credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); event(new CreditWasMarkedSent($this->credit, $this->credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));

View File

@ -286,12 +286,33 @@ class Design extends BaseDesign
return []; return [];
} }
$thead = [
['element' => 'th', 'content' => '$item_label', 'properties' => ['data-ref' => 'delivery_note-item_label']],
['element' => 'th', 'content' => '$description_label', 'properties' => ['data-ref' => 'delivery_note-description_label']],
['element' => 'th', 'content' => '$product.quantity_label', 'properties' => ['data-ref' => 'delivery_note-product.quantity_label']],
];
$items = $this->transformLineItems($this->entity->line_items, $this->type);
$this->processNewLines($items);
$product_customs = [false, false, false, false];
foreach ($items as $row) {
for ($i = 0; $i < count($product_customs); $i++) {
if (!empty($row['delivery_note.delivery_note' . ($i + 1)])) {
$product_customs[$i] = true;
}
}
}
for ($i = 0; $i < count($product_customs); $i++) {
if ($product_customs[$i]) {
array_push($thead, ['element' => 'th', 'content' => '$product.product' . ($i + 1) . '_label', 'properties' => ['data-ref' => 'delivery_note-product.product' . ($i + 1) . '_label']]);
}
}
return [ return [
['element' => 'thead', 'elements' => [ ['element' => 'thead', 'elements' => $thead],
['element' => 'th', 'content' => '$item_label', 'properties' => ['data-ref' => 'delivery_note-item_label']],
['element' => 'th', 'content' => '$description_label', 'properties' => ['data-ref' => 'delivery_note-description_label']],
['element' => 'th', 'content' => '$product.quantity_label', 'properties' => ['data-ref' => 'delivery_note-product.quantity_label']],
]],
['element' => 'tbody', 'elements' => $this->buildTableBody(self::DELIVERY_NOTE)], ['element' => 'tbody', 'elements' => $this->buildTableBody(self::DELIVERY_NOTE)],
]; ];
} }
@ -528,6 +549,16 @@ class Design extends BaseDesign
} }
if ($type == self::DELIVERY_NOTE) { if ($type == self::DELIVERY_NOTE) {
$product_customs = [false, false, false, false];
foreach ($items as $row) {
for ($i = 0; $i < count($product_customs); $i++) {
if (!empty($row['delivery_note.delivery_note' . ($i + 1)])) {
$product_customs[$i] = true;
}
}
}
foreach ($items as $row) { foreach ($items as $row) {
$element = ['element' => 'tr', 'elements' => []]; $element = ['element' => 'tr', 'elements' => []];
@ -535,6 +566,12 @@ class Design extends BaseDesign
$element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.notes'], 'properties' => ['data-ref' => 'delivery_note_table.notes-td']]; $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.notes'], 'properties' => ['data-ref' => 'delivery_note_table.notes-td']];
$element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.quantity'], 'properties' => ['data-ref' => 'delivery_note_table.quantity-td']]; $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.quantity'], 'properties' => ['data-ref' => 'delivery_note_table.quantity-td']];
for ($i = 0; $i < count($product_customs); $i++) {
if ($product_customs[$i]) {
$element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.delivery_note' . ($i + 1)], 'properties' => ['data-ref' => 'delivery_note_table.product' . ($i + 1) . '-td']];
}
}
$elements[] = $element; $elements[] = $element;
} }

View File

@ -322,6 +322,7 @@ class HtmlEngine
$data['$client.address'] = &$data['$client_address']; $data['$client.address'] = &$data['$client_address'];
$data['$client.postal_code'] = ['value' => $this->client->postal_code ?: '&nbsp;', 'label' => ctrans('texts.postal_code')]; $data['$client.postal_code'] = ['value' => $this->client->postal_code ?: '&nbsp;', 'label' => ctrans('texts.postal_code')];
$data['$client.city'] = ['value' => $this->client->city ?: '&nbsp;', 'label' => ctrans('texts.city')]; $data['$client.city'] = ['value' => $this->client->city ?: '&nbsp;', 'label' => ctrans('texts.city')];
$data['$client.state'] = ['value' => $this->client->state ?: '&nbsp;', 'label' => ctrans('texts.state')];
$data['$client.id_number'] = &$data['$id_number']; $data['$client.id_number'] = &$data['$id_number'];
$data['$client.vat_number'] = &$data['$vat_number']; $data['$client.vat_number'] = &$data['$vat_number'];
$data['$client.website'] = &$data['$website']; $data['$client.website'] = &$data['$website'];
@ -333,6 +334,22 @@ class HtmlEngine
$data['$client.country'] = &$data['$country']; $data['$client.country'] = &$data['$country'];
$data['$client.email'] = &$data['$email']; $data['$client.email'] = &$data['$email'];
$data['$client.billing_address'] = &$data['$client_address'];
$data['$client.billing_address1'] = &$data['$client.address1'];
$data['$client.billing_address2'] = &$data['$client.address2'];
$data['$client.billing_city'] = &$data['$client.city'];
$data['$client.billing_state'] = &$data['$client.state'];
$data['$client.billing_postal_code'] = &$data['$client.postal_code'];
$data['$client.billing_country'] = &$data['$client.country'];
$data['$client.shipping_address'] = ['value' => $this->client->present()->shipping_address() ?: '&nbsp;', 'label' => ctrans('texts.shipping_address')];
$data['$client.shipping_address1'] = ['value' => $this->client->shipping_address1 ?: '&nbsp;', 'label' => ctrans('texts.shipping_address1')];
$data['$client.shipping_address2'] = ['value' => $this->client->shipping_address2 ?: '&nbsp;', 'label' => ctrans('texts.shipping_address2')];
$data['$client.shipping_city'] = ['value' => $this->client->shipping_city ?: '&nbsp;', 'label' => ctrans('texts.shipping_city')];
$data['$client.shipping_state'] = ['value' => $this->client->shipping_state ?: '&nbsp;', 'label' => ctrans('texts.shipping_state')];
$data['$client.shipping_postal_code'] = ['value' => $this->client->shipping_postal_code ?: '&nbsp;', 'label' => ctrans('texts.shipping_postal_code')];
$data['$client.shipping_country'] = ['value' => isset($this->client->shipping_country->name) ? ctrans('texts.country_' . $this->client->shipping_country->name) : '', 'label' => ctrans('texts.shipping_country')];
$data['$client.currency'] = ['value' => $this->client->currency()->code, 'label' => '']; $data['$client.currency'] = ['value' => $this->client->currency()->code, 'label' => ''];
$data['$client.balance'] = ['value' => Number::formatMoney($this->client->balance, $this->client), 'label' => ctrans('texts.account_balance')]; $data['$client.balance'] = ['value' => Number::formatMoney($this->client->balance, $this->client), 'label' => ctrans('texts.account_balance')];

View File

@ -532,42 +532,46 @@ trait GeneratesCounter
switch ($reset_counter_frequency) { switch ($reset_counter_frequency) {
case RecurringInvoice::FREQUENCY_DAILY: case RecurringInvoice::FREQUENCY_DAILY:
now()->addDay(); $new_reset_date = $reset_date->addDay();
break; break;
case RecurringInvoice::FREQUENCY_WEEKLY: case RecurringInvoice::FREQUENCY_WEEKLY:
now()->addWeek(); $new_reset_date = $reset_date->addWeek();
break; break;
case RecurringInvoice::FREQUENCY_TWO_WEEKS: case RecurringInvoice::FREQUENCY_TWO_WEEKS:
now()->addWeeks(2); $new_reset_date = $reset_date->addWeeks(2);
break; break;
case RecurringInvoice::FREQUENCY_FOUR_WEEKS: case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
now()->addWeeks(4); $new_reset_date = $reset_date->addWeeks(4);
break; break;
case RecurringInvoice::FREQUENCY_MONTHLY: case RecurringInvoice::FREQUENCY_MONTHLY:
now()->addMonth(); $new_reset_date = $reset_date->addMonth();
break; break;
case RecurringInvoice::FREQUENCY_TWO_MONTHS: case RecurringInvoice::FREQUENCY_TWO_MONTHS:
now()->addMonths(2); $new_reset_date = $reset_date->addMonths(2);
break; break;
case RecurringInvoice::FREQUENCY_THREE_MONTHS: case RecurringInvoice::FREQUENCY_THREE_MONTHS:
now()->addMonths(3); $new_reset_date = $reset_date->addMonths(3);
break; break;
case RecurringInvoice::FREQUENCY_FOUR_MONTHS: case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
now()->addMonths(4); $new_reset_date = $reset_date->addMonths(4);
break; break;
case RecurringInvoice::FREQUENCY_SIX_MONTHS: case RecurringInvoice::FREQUENCY_SIX_MONTHS:
now()->addMonths(6); $new_reset_date = $reset_date->addMonths(6);
break; break;
case RecurringInvoice::FREQUENCY_ANNUALLY: case RecurringInvoice::FREQUENCY_ANNUALLY:
now()->addYear(); $new_reset_date = $reset_date->addYear();
break; break;
case RecurringInvoice::FREQUENCY_TWO_YEARS: case RecurringInvoice::FREQUENCY_TWO_YEARS:
now()->addYears(2); $new_reset_date = $reset_date->addYears(2);
break;
default:
$new_reset_date = $reset_date->addYear();
break; break;
} }
$settings = $client->company->settings; $settings = $client->company->settings;
$settings->reset_counter_date = $reset_date->format('Y-m-d'); $settings->reset_counter_date = $new_reset_date->format('Y-m-d');
$settings->invoice_number_counter = 1; $settings->invoice_number_counter = 1;
$settings->quote_number_counter = 1; $settings->quote_number_counter = 1;
$settings->credit_number_counter = 1; $settings->credit_number_counter = 1;

689
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,5 @@
<?php <?php
use App\Utils\Ninja;
return [ return [
/* /*
@ -96,7 +94,7 @@ return [
'prefix_indexes' => true, 'prefix_indexes' => true,
'strict' => env('DB_STRICT', false), 'strict' => env('DB_STRICT', false),
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC', 'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
// 'options' => Ninja::isHosted() ? config('ninja.db_options') : [], 'options' => [],
// 'options' => [ // 'options' => [
// PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false, // PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
// PDO::MYSQL_ATTR_SSL_KEY => env("DB_CLIENT_KEY", ''), // PDO::MYSQL_ATTR_SSL_KEY => env("DB_CLIENT_KEY", ''),
@ -118,7 +116,7 @@ return [
'prefix_indexes' => true, 'prefix_indexes' => true,
'strict' => env('DB_STRICT', false), 'strict' => env('DB_STRICT', false),
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC', 'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
// 'options' => Ninja::isHosted() ? config('ninja.db_options') : [], 'options' => [],
], ],
'db-ninja-02' => [ 'db-ninja-02' => [
@ -134,7 +132,7 @@ return [
'prefix_indexes' => true, 'prefix_indexes' => true,
'strict' => env('DB_STRICT', false), 'strict' => env('DB_STRICT', false),
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC', 'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
// 'options' => Ninja::isHosted() ? config('ninja.db_options') : [], 'options' => [],
], ],
'db-ninja-02a' => [ 'db-ninja-02a' => [
@ -150,7 +148,7 @@ return [
'prefix_indexes' => true, 'prefix_indexes' => true,
'strict' => env('DB_STRICT', false), 'strict' => env('DB_STRICT', false),
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC', 'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
// 'options' => Ninja::isHosted() ? config('ninja.db_options') : [], 'options' => [],
], ],
], ],

View File

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true), 'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.3.48', 'app_version' => '5.3.49',
'app_tag' => '5.3.48', 'app_tag' => '5.3.49',
'minimum_client_version' => '5.0.16', 'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1', 'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''), 'api_secret' => env('API_SECRET', ''),
@ -59,12 +59,12 @@ return [
'default' => env('DB_CONNECTION', 'mysql'), 'default' => env('DB_CONNECTION', 'mysql'),
], ],
'db_options' => [ // 'db_options' => [
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false, // PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
PDO::MYSQL_ATTR_SSL_KEY => env("DB_CLIENT_KEY", ''), // PDO::MYSQL_ATTR_SSL_KEY => env("DB_CLIENT_KEY", ''),
PDO::MYSQL_ATTR_SSL_CERT => env("DB_CLIENT_CERT", ''), // PDO::MYSQL_ATTR_SSL_CERT => env("DB_CLIENT_CERT", ''),
PDO::MYSQL_ATTR_SSL_CA => env("DB_CA_CERT", ''), // PDO::MYSQL_ATTR_SSL_CA => env("DB_CA_CERT", ''),
], // ],
'i18n' => [ 'i18n' => [
'timezone_id' => env('DEFAULT_TIMEZONE', 1), 'timezone_id' => env('DEFAULT_TIMEZONE', 1),

View File

@ -0,0 +1,34 @@
<?php
use App\Models\Language;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddEstonianLanguage extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$estonia = ['id' => 35, 'name' => 'Estonian', 'locale' => 'et'];
Language::unguard();
Language::create($estonia);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -0,0 +1,52 @@
<?php
use App\Models\Currency;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddKydCurrency extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$currencies = [
['id' => 112, 'name' => 'Cayman Island Dollar', 'code' => 'KYD', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
];
foreach ($currencies as $currency) {
$record = Currency::whereCode($currency['code'])->first();
if ($record) {
$record->name = $currency['name'];
$record->symbol = $currency['symbol'];
$record->precision = $currency['precision'];
$record->thousand_separator = $currency['thousand_separator'];
$record->decimal_separator = $currency['decimal_separator'];
if (isset($currency['swap_currency_symbol'])) {
$record->swap_currency_symbol = $currency['swap_currency_symbol'];
}
$record->save();
} else {
Currency::create($currency);
}
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -30,11 +30,11 @@ const RESOURCES = {
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98", "assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f", "assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1", "assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"/": "ce9704a467c22d129715ff0c8be6d2c2", "/": "854475c3df4e34c07a33807e1806745b",
"version.json": "58bf33a34913cc76f45bb675b8348805", "version.json": "b685694a4de24b3b41bd9b5e756ba0fc",
"favicon.ico": "51636d3a390451561744c42188ccd628", "favicon.ico": "51636d3a390451561744c42188ccd628",
"favicon.png": "dca91c54388f52eded692718d5a98b8b", "favicon.png": "dca91c54388f52eded692718d5a98b8b",
"main.dart.js": "64f0368dd9c2bd830002aa4303c0daee", "main.dart.js": "7dfd02ea7b43f28d6b087971fe418e99",
"canvaskit/profiling/canvaskit.js": "3783918f48ef691e230156c251169480", "canvaskit/profiling/canvaskit.js": "3783918f48ef691e230156c251169480",
"canvaskit/profiling/canvaskit.wasm": "6d1b0fc1ec88c3110db88caa3393c580", "canvaskit/profiling/canvaskit.wasm": "6d1b0fc1ec88c3110db88caa3393c580",
"canvaskit/canvaskit.js": "62b9906717d7215a6ff4cc24efbd1b5c", "canvaskit/canvaskit.js": "62b9906717d7215a6ff4cc24efbd1b5c",

View File

@ -0,0 +1,2 @@
/*! For license information please see stripe-fpx.js.LICENSE.txt */
(()=>{var e,t,n,r;function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var i=null!==(e=null===(t=document.querySelector('meta[name="stripe-publishable-key"]'))||void 0===t?void 0:t.content)&&void 0!==e?e:"",c=null!==(n=null===(r=document.querySelector('meta[name="stripe-account-id"]'))||void 0===r?void 0:r.content)&&void 0!==n?n:"";new function e(t,n){var r=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),o(this,"setupStripe",(function(){r.stripe=Stripe(r.key),r.stripeConnect&&(r.stripe.stripeAccount=c);var e=r.stripe.elements();return r.fpx=e.create("fpxBank",{style:{base:{padding:"10px 12px",color:"#32325d",fontSize:"16px"}},accountHolderType:"individual"}),r.fpx.mount("#fpx-bank-element"),r})),o(this,"handle",(function(){document.getElementById("pay-now").addEventListener("click",(function(e){document.getElementById("pay-now").disabled=!0,document.querySelector("#pay-now > svg").classList.remove("hidden"),document.querySelector("#pay-now > span").classList.add("hidden"),r.stripe.confirmFpxPayment(document.querySelector("meta[name=pi-client-secret").content,{payment_method:{fpx:r.fpx},return_url:document.querySelector('meta[name="return-url"]').content})}))})),this.key=t,this.errors=document.getElementById("errors"),this.stripeConnect=n}(i,c).setupStripe().handle()})();

View File

@ -0,0 +1,9 @@
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/

81562
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

80149
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

80588
public/main.html.dart.js vendored

File diff suppressed because one or more lines are too long

81427
public/main.next.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -37,6 +37,7 @@
"/js/clients/payments/stripe-ideal.js": "/js/clients/payments/stripe-ideal.js?id=73ce56676f9252b0cecf", "/js/clients/payments/stripe-ideal.js": "/js/clients/payments/stripe-ideal.js?id=73ce56676f9252b0cecf",
"/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js?id=f3a14f78bec8209c30ba", "/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js?id=f3a14f78bec8209c30ba",
"/js/clients/payments/stripe-browserpay.js": "/js/clients/payments/stripe-browserpay.js?id=71e49866d66a6d85b88a", "/js/clients/payments/stripe-browserpay.js": "/js/clients/payments/stripe-browserpay.js?id=71e49866d66a6d85b88a",
"/js/clients/payments/stripe-fpx.js": "/js/clients/payments/stripe-fpx.js?id=915712157bc0634b9b21",
"/css/app.css": "/css/app.css?id=cab8a6526b0f9f71842d", "/css/app.css": "/css/app.css?id=cab8a6526b0f9f71842d",
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad" "/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad"
} }

View File

@ -1 +1 @@
{"app_name":"invoiceninja_flutter","version":"5.0.72","build_number":"72","package_name":"invoiceninja_flutter"} {"app_name":"invoiceninja_flutter","version":"5.0.73","build_number":"73","package_name":"invoiceninja_flutter"}

View File

@ -0,0 +1,65 @@
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
class ProcessFPXPay {
constructor(key, stripeConnect) {
this.key = key;
this.errors = document.getElementById('errors');
this.stripeConnect = stripeConnect;
}
setupStripe = () => {
this.stripe = Stripe(this.key);
if(this.stripeConnect)
this.stripe.stripeAccount = stripeConnect;
let elements = this.stripe.elements();
let style = {
base: {
// Add your base input styles here. For example:
padding: '10px 12px',
color: '#32325d',
fontSize: '16px',
},
};
this.fpx = elements.create('fpxBank', {style: style, accountHolderType: 'individual',});
this.fpx.mount("#fpx-bank-element");
return this;
};
handle = () => {
document.getElementById('pay-now').addEventListener('click', (e) => {
document.getElementById('pay-now').disabled = true;
document.querySelector('#pay-now > svg').classList.remove('hidden');
document.querySelector('#pay-now > span').classList.add('hidden');
this.stripe.confirmFpxPayment(
document.querySelector('meta[name=pi-client-secret').content,
{
payment_method: {
fpx: this.fpx,
},
return_url: document.querySelector(
'meta[name="return-url"]'
).content,
}
);
});
};
}
const publishableKey = document.querySelector(
'meta[name="stripe-publishable-key"]'
)?.content ?? '';
const stripeConnect =
document.querySelector('meta[name="stripe-account-id"]')?.content ?? '';
new ProcessFPXPay(publishableKey, stripeConnect).setupStripe().handle();

View File

@ -90,7 +90,7 @@ $LANG = array(
'upload' => 'Hochladen', 'upload' => 'Hochladen',
'import' => 'Importieren', 'import' => 'Importieren',
'download' => 'Herunterladen', 'download' => 'Herunterladen',
'cancel' => 'Abbrechen', 'cancel' => 'Stornieren',
'close' => 'Schließen', 'close' => 'Schließen',
'provide_email' => 'Bitte geben Sie eine gültige E-Mail-Adresse an', 'provide_email' => 'Bitte geben Sie eine gültige E-Mail-Adresse an',
'powered_by' => 'Unterstützt durch', 'powered_by' => 'Unterstützt durch',
@ -2367,7 +2367,7 @@ Sobald Sie die Beträge erhalten haben, kommen Sie bitte wieder zurück zu diese
'default_documents' => 'Standard-Dokumente', 'default_documents' => 'Standard-Dokumente',
'send_email_to_client' => 'E-Mail an den Kunden senden', 'send_email_to_client' => 'E-Mail an den Kunden senden',
'refund_subject' => 'Erstattung wurde durchgeführt', 'refund_subject' => 'Erstattung wurde durchgeführt',
'refund_body' => 'Sie haben eine Rückerstattung über :Betrag für Rechnung :Rechnungsnummer erhalten.', 'refund_body' => 'Sie haben eine Rückerstattung über :amount für Rechnung :invoice_number erhalten.',
'currency_us_dollar' => 'US Dollar', 'currency_us_dollar' => 'US Dollar',
'currency_british_pound' => 'Britische Pfund', 'currency_british_pound' => 'Britische Pfund',
@ -3937,7 +3937,7 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
'next_step' => 'Nächster Schritt', 'next_step' => 'Nächster Schritt',
'notification_credit_sent_subject' => 'Rechnung :invoice wurde an Kunde gesendet.', 'notification_credit_sent_subject' => 'Rechnung :invoice wurde an Kunde gesendet.',
'notification_credit_viewed_subject' => 'Guthaben :invoice wurde angesehen von :client', 'notification_credit_viewed_subject' => 'Guthaben :invoice wurde angesehen von :client',
'notification_credit_sent' => 'Der folgende Kunde :Kunde hat eine Gutschrift :Rechnung über :Betrag erhalten.', 'notification_credit_sent' => 'Der folgende Kunde :client hat eine Gutschrift :invoice über :amount erhalten.',
'notification_credit_viewed' => 'Der folgende Kunde :client hat Kredit :credit für :amount angeschaut.', 'notification_credit_viewed' => 'Der folgende Kunde :client hat Kredit :credit für :amount angeschaut.',
'reset_password_text' => 'Bitte geben Sie ihre E-Mail-Adresse an, um das Passwort zurücksetzen zu können.', 'reset_password_text' => 'Bitte geben Sie ihre E-Mail-Adresse an, um das Passwort zurücksetzen zu können.',
'password_reset' => 'Passwort zurücksetzten', 'password_reset' => 'Passwort zurücksetzten',
@ -3961,8 +3961,8 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
'new_signup_text' => 'Ein neuer Benutzer wurde von :user - :email von der IP: :ip erstellt', 'new_signup_text' => 'Ein neuer Benutzer wurde von :user - :email von der IP: :ip erstellt',
'notification_payment_paid_subject' => 'Neue Zahlung von :client', 'notification_payment_paid_subject' => 'Neue Zahlung von :client',
'notification_partial_payment_paid_subject' => 'Neue Anzahlung von :client', 'notification_partial_payment_paid_subject' => 'Neue Anzahlung von :client',
'notification_payment_paid' => 'Eine Zahlung von :Betrag wurde von Kunde :Kunde auf :Rechnung geleistet', 'notification_payment_paid' => 'Eine Zahlung von :amount wurde von Kunde :client auf :invoice geleistet',
'notification_partial_payment_paid' => 'Eine Teilzahlung in Höhe von :Betrag wurde vom Kunden :Kunde auf :Rechnung geleistet', 'notification_partial_payment_paid' => 'Eine Teilzahlung in Höhe von :amount wurde vom Kunden :client auf :invoice geleistet',
'notification_bot' => 'Benachrichtigungs-Bot', 'notification_bot' => 'Benachrichtigungs-Bot',
'invoice_number_placeholder' => 'Rechnung # :invoice', 'invoice_number_placeholder' => 'Rechnung # :invoice',
'entity_number_placeholder' => ':entity # :entity_number', 'entity_number_placeholder' => ':entity # :entity_number',
@ -3978,12 +3978,12 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
'enter_your_personal_address' => 'Bitte geben Sie Ihre Rechnungsadresse an', 'enter_your_personal_address' => 'Bitte geben Sie Ihre Rechnungsadresse an',
'enter_your_shipping_address' => 'Bitte geben Sie Ihr Lieferadresse an', 'enter_your_shipping_address' => 'Bitte geben Sie Ihr Lieferadresse an',
'list_of_invoices' => 'Liste der Rechnungen', 'list_of_invoices' => 'Liste der Rechnungen',
'with_selected' => 'With selected', 'with_selected' => 'Breite ausgewählt',
'invoice_still_unpaid' => 'Diese Rechnung wurde noch nicht beglichen. Klicken um zu vervollständigen.', 'invoice_still_unpaid' => 'Diese Rechnung wurde noch nicht beglichen. Klicken um zu vervollständigen.',
'list_of_recurring_invoices' => 'Liste der wiederkehrende Rechnungen', 'list_of_recurring_invoices' => 'Liste der wiederkehrende Rechnungen',
'details_of_recurring_invoice' => 'Details über wiederkehrende Rechnung', 'details_of_recurring_invoice' => 'Details über wiederkehrende Rechnung',
'cancellation' => 'Storno', 'cancellation' => 'Storno',
'about_cancellation' => 'Wenn Sie die wiederkehrende Rechnung stoppen möchten, klicken Sie bitte auf den Button zur Anforderung der Stornierung.', 'about_cancellation' => 'Wenn Sie die wiederkehrende Rechnung stoppen möchten, klicken Sie bitte auf , um die Stornierung zu beantragen.',
'cancellation_warning' => 'Achtung! Sie beantragen die Stornierung dieses Dienstes. Ihr Dienst kann ohne weitere Mitteilung an Sie storniert werden.', 'cancellation_warning' => 'Achtung! Sie beantragen die Stornierung dieses Dienstes. Ihr Dienst kann ohne weitere Mitteilung an Sie storniert werden.',
'cancellation_pending' => 'Kündigung in Bearbeitung! Wir melden uns bei Ihnen...', 'cancellation_pending' => 'Kündigung in Bearbeitung! Wir melden uns bei Ihnen...',
'list_of_payments' => 'Liste der Zahlungen', 'list_of_payments' => 'Liste der Zahlungen',
@ -4080,7 +4080,7 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
'notification_invoice_reminder1_sent_subject' => 'Die erste Erinnerung für Rechnung :invoice wurde an Kunde :client gesendet', 'notification_invoice_reminder1_sent_subject' => 'Die erste Erinnerung für Rechnung :invoice wurde an Kunde :client gesendet',
'notification_invoice_reminder2_sent_subject' => 'Die 2. Erinnerung für Rechnung :invoice wurde an Kunde :client gesendet', 'notification_invoice_reminder2_sent_subject' => 'Die 2. Erinnerung für Rechnung :invoice wurde an Kunde :client gesendet',
'notification_invoice_reminder3_sent_subject' => 'Die 3. Erinnerung für Rechnung :invoice wurde an Kunde :client gesendet', 'notification_invoice_reminder3_sent_subject' => 'Die 3. Erinnerung für Rechnung :invoice wurde an Kunde :client gesendet',
'notification_invoice_reminder_endless_sent_subject' => 'Endlose Erinnerung für Rechnung :Rechnung wurde an :Kunde gesendet', 'notification_invoice_reminder_endless_sent_subject' => 'Endlose Erinnerung für Rechnung :invoice wurde an :client gesendet',
'assigned_user' => 'Zugewiesener Benutzer', 'assigned_user' => 'Zugewiesener Benutzer',
'setup_steps_notice' => 'Um mit dem nächsten Schritt fortzufahren, stellen Sie sicher, dass Sie jeden Abschnitt testen.', 'setup_steps_notice' => 'Um mit dem nächsten Schritt fortzufahren, stellen Sie sicher, dass Sie jeden Abschnitt testen.',
'setup_phantomjs_note' => 'Anmerkung zu Phantom JS. Mehr...', 'setup_phantomjs_note' => 'Anmerkung zu Phantom JS. Mehr...',
@ -4096,77 +4096,77 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
'new_card' => 'Neue Kreditkarte', 'new_card' => 'Neue Kreditkarte',
'new_bank_account' => 'Bankverbindung hinzufügen', 'new_bank_account' => 'Bankverbindung hinzufügen',
'company_limit_reached' => 'Maximal 10 Firmen pro Account.', 'company_limit_reached' => 'Maximal 10 Firmen pro Account.',
'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices', 'credits_applied_validation' => 'Die Gesamtsumme der Gutschriften kann nicht MEHR sein als die Gesamtsumme der Rechnungen',
'credit_number_taken' => 'Gutschriftsnummer bereits vergeben.', 'credit_number_taken' => 'Gutschriftsnummer bereits vergeben.',
'credit_not_found' => 'Gutschrift nicht gefunden', 'credit_not_found' => 'Gutschrift nicht gefunden',
'invoices_dont_match_client' => 'Die ausgewählten Rechnungen stammen von mehr als einem Kunden', 'invoices_dont_match_client' => 'Die ausgewählten Rechnungen stammen von mehr als einem Kunden',
'duplicate_credits_submitted' => 'Duplicate credits submitted.', 'duplicate_credits_submitted' => 'Doppelte Zahlung eingereicht',
'duplicate_invoices_submitted' => 'Duplicate invoices submitted.', 'duplicate_invoices_submitted' => 'Doppelte Rechnung',
'credit_with_no_invoice' => 'You must have an invoice set when using a credit in a payment', 'credit_with_no_invoice' => 'Bei der Verwendung eines Kredits in einer Zahlung muss eine Rechnung eingestellt sein.',
'client_id_required' => 'Kundennummer wird benötigt.', 'client_id_required' => 'Kundennummer wird benötigt.',
'expense_number_taken' => 'Expense number already taken', 'expense_number_taken' => 'Bereits vergebene Ausgabennummer',
'invoice_number_taken' => 'Diese Rechnungsnummer wurde bereits verwendet.', 'invoice_number_taken' => 'Diese Rechnungsnummer wurde bereits verwendet.',
'payment_id_required' => 'Zahlungs-ID notwendig.', 'payment_id_required' => 'Zahlungs-ID notwendig.',
'unable_to_retrieve_payment' => 'Unable to retrieve specified payment', 'unable_to_retrieve_payment' => 'Die angegebene Zahlung kann nicht abgerufen werden',
'invoice_not_related_to_payment' => 'Rechnungsnr. :invoice ist nicht mit dieser Zahlung verknüpft', 'invoice_not_related_to_payment' => 'Rechnungsnr. :invoice ist nicht mit dieser Zahlung verknüpft',
'credit_not_related_to_payment' => 'Guthabennr. :credit ist nicht mit dieser Zahlung verknüpft', 'credit_not_related_to_payment' => 'Guthabennr. :credit ist nicht mit dieser Zahlung verknüpft',
'max_refundable_invoice' => 'Attempting to refund more than allowed for invoice id :invoice, maximum refundable amount is :amount', 'max_refundable_invoice' => 'Es wurde versucht, mehr zu erstatten, als für die Rechnungs-ID :invoice zulässig ist, der maximal erstattungsfähige Betrag ist :amount',
'refund_without_invoices' => 'Attempting to refund a payment with invoices attached, please specify valid invoice/s to be refunded.', 'refund_without_invoices' => 'Wenn Sie versuchen, eine Zahlung mit beigefügten Rechnungen zu erstatten, geben Sie bitte gültige Rechnungen an, die erstattet werden sollen.',
'refund_without_credits' => 'Attempting to refund a payment with credits attached, please specify valid credits/s to be refunded.', 'refund_without_credits' => 'Wenn Sie versuchen, eine Zahlung mit beigefügten Guthaben zu erstatten, geben Sie bitte gültige Guthaben an, die erstattet werden sollen.',
'max_refundable_credit' => 'Attempting to refund more than allowed for credit :credit, maximum refundable amount is :amount', 'max_refundable_credit' => 'Versuch, mehr zu erstatten, als für die Gutschrift zugelassen ist :credit, maximal erstattungsfähiger Betrag ist :amount',
'project_client_do_not_match' => 'Project client does not match entity client', 'project_client_do_not_match' => 'Projektkunde stimmt nicht mit Entitätskunde überein',
'quote_number_taken' => 'Angebotsnummer bereits in Verwendung', 'quote_number_taken' => 'Angebotsnummer bereits in Verwendung',
'recurring_invoice_number_taken' => 'Recurring Invoice number :number already taken', 'recurring_invoice_number_taken' => 'Wiederkehrende Rechnungsnummer :number bereits vergeben',
'user_not_associated_with_account' => 'User not associated with this account', 'user_not_associated_with_account' => 'Kein mit diesem Konto verbundener Benutzer',
'amounts_do_not_balance' => 'Amounts do not balance correctly.', 'amounts_do_not_balance' => 'Die Beträge sind nicht korrekt ausgeglichen.',
'insufficient_applied_amount_remaining' => 'Insufficient applied amount remaining to cover payment.', 'insufficient_applied_amount_remaining' => 'Der angewandte Betrag reicht nicht aus, um die Zahlung zu decken.',
'insufficient_credit_balance' => 'Insufficient balance on credit.', 'insufficient_credit_balance' => 'Unzureichendes Guthaben auf dem Kredit.',
'one_or_more_invoices_paid' => 'One or more of these invoices have been paid', 'one_or_more_invoices_paid' => 'Eine oder mehrere dieser Rechnungen wurden bereits bezahlt',
'invoice_cannot_be_refunded' => 'Rechnung :number kann nicht erstattet werden', 'invoice_cannot_be_refunded' => 'Rechnung :number kann nicht erstattet werden',
'attempted_refund_failed' => 'Attempting to refund :amount only :refundable_amount available for refund', 'attempted_refund_failed' => 'Erstattungsversuch :amount nur :refundable_amount für Erstattung verfügbar',
'user_not_associated_with_this_account' => 'This user is unable to be attached to this company. Perhaps they have already registered a user on another account?', 'user_not_associated_with_this_account' => 'Dieser Benutzer kann nicht mit diesem Unternehmen verbunden werden. Vielleicht hat er bereits einen Benutzer für ein anderes Konto registriert?',
'migration_completed' => 'Umstellung abgeschlossen', 'migration_completed' => 'Umstellung abgeschlossen',
'migration_completed_description' => 'Die Umstellung wurde erfolgreich abgeschlossen. Bitte prüfen Sie trotzdem Ihre Daten nach dem Login.', 'migration_completed_description' => 'Die Umstellung wurde erfolgreich abgeschlossen. Bitte prüfen Sie trotzdem Ihre Daten nach dem Login.',
'api_404' => '404 | Hier gibt es nichts zu sehen!', 'api_404' => '404 | Hier gibt es nichts zu sehen!',
'large_account_update_parameter' => 'Cannot load a large account without a updated_at parameter', 'large_account_update_parameter' => 'Kann ein großes Konto ohne den Parameter updated_at nicht laden',
'no_backup_exists' => 'No backup exists for this activity', 'no_backup_exists' => 'Für diese Aktivität ist keine Sicherung vorhanden',
'company_user_not_found' => 'Company User record not found', 'company_user_not_found' => 'Firma Benutzerdatensatz nicht gefunden',
'no_credits_found' => 'Kein Guthaben gefunden.', 'no_credits_found' => 'Kein Guthaben gefunden.',
'action_unavailable' => 'The requested action :action is not available.', 'action_unavailable' => 'Die angeforderte Aktion :action ist nicht verfügbar.',
'no_documents_found' => 'Keine Dokumente gefunden.', 'no_documents_found' => 'Keine Dokumente gefunden.',
'no_group_settings_found' => 'No group settings found', 'no_group_settings_found' => 'Keine Gruppeneinstellungen gefunden',
'access_denied' => 'Insufficient privileges to access/modify this resource', 'access_denied' => 'Unzureichende Berechtigungen für den Zugriff/die Änderung dieser Ressource',
'invoice_cannot_be_marked_paid' => 'Rechnung kann nicht als "bezahlt" gekennzeichnet werden.', 'invoice_cannot_be_marked_paid' => 'Rechnung kann nicht als "bezahlt" gekennzeichnet werden.',
'invoice_license_or_environment' => 'Invalid license, or invalid environment :environment', 'invoice_license_or_environment' => 'Ungültige Lizenz, oder ungültige Umgebung :environment',
'route_not_available' => 'Route not available', 'route_not_available' => 'Pfad nicht verfügbar',
'invalid_design_object' => 'Invalid custom design object', 'invalid_design_object' => 'Ungültiges benutzerdefiniertes Entwurfsobjekt',
'quote_not_found' => 'Angebot/e nicht gefunden', 'quote_not_found' => 'Angebot/e nicht gefunden',
'quote_unapprovable' => 'Unable to approve this quote as it has expired.', 'quote_unapprovable' => 'Dieses Angebot kann nicht genehmigt werden, da es abgelaufen ist.',
'scheduler_has_run' => 'Aufgabenplaner lief', 'scheduler_has_run' => 'Aufgabenplaner lief',
'scheduler_has_never_run' => 'Aufgabenplaner lief noch nie', 'scheduler_has_never_run' => 'Aufgabenplaner lief noch nie',
'self_update_not_available' => 'Integrierter Updater auf diesem System nicht verfügbar.', 'self_update_not_available' => 'Integrierter Updater auf diesem System nicht verfügbar.',
'user_detached' => 'Nutzer wurden vom Unternehmen entkoppelt', 'user_detached' => 'Nutzer wurden vom Unternehmen entkoppelt',
'create_webhook_failure' => 'Webhook konnte nicht erstellt werden', 'create_webhook_failure' => 'Webhook konnte nicht erstellt werden',
'payment_message_extended' => 'Vielen Dank für deine Zahlung von :amount für die Rechnung :invoice', 'payment_message_extended' => 'Vielen Dank für deine Zahlung von :amount für die Rechnung :invoice',
'online_payments_minimum_note' => 'Note: Online payments are supported only if amount is bigger than $1 or currency equivalent.', 'online_payments_minimum_note' => 'Hinweis: Online-Zahlungen werden nur unterstützt, wenn der Betrag größer als 1€ oder der entsprechende Währungsbetrag ist.',
'payment_token_not_found' => 'Payment token not found, please try again. If an issue still persist, try with another payment method', 'payment_token_not_found' => 'Zahlungstoken nicht gefunden, bitte versuchen Sie es erneut. Wenn das Problem weiterhin besteht, versuchen Sie es mit einer anderen Zahlungsmethode',
'vendor_address1' => 'Vendor Street', 'vendor_address1' => 'Stre Lieferant',
'vendor_address2' => 'Vendor Apt/Suite', 'vendor_address2' => 'Lieferant Apt/Suite',
'partially_unapplied' => 'Partially Unapplied', 'partially_unapplied' => 'Teilweise unangewandt',
'select_a_gmail_user' => 'Please select a user authenticated with Gmail', 'select_a_gmail_user' => 'Bitte wählen Sie einen mit Gmail authentifizierten Benutzer',
'list_long_press' => 'List Long Press', 'list_long_press' => 'Liste Langes Drücken',
'show_actions' => 'Zeige Aktionen', 'show_actions' => 'Zeige Aktionen',
'start_multiselect' => 'Mehrfachauswahl', 'start_multiselect' => 'Mehrfachauswahl',
'email_sent_to_confirm_email' => 'Eine E-Mail wurde versandt um Ihre E-Mail-Adresse zu bestätigen.', 'email_sent_to_confirm_email' => 'Eine E-Mail wurde versandt um Ihre E-Mail-Adresse zu bestätigen.',
'converted_paid_to_date' => 'Converted Paid to Date', 'converted_paid_to_date' => 'Umgewandelt Bezahlt bis Datum',
'converted_credit_balance' => 'Converted Credit Balance', 'converted_credit_balance' => 'Umgerechneter Guthabenbetrag',
'converted_total' => 'Converted Total', 'converted_total' => 'Umgerechnet Total',
'reply_to_name' => 'Name der Antwortadresse', 'reply_to_name' => 'Name der Antwortadresse',
'payment_status_-2' => 'Teilweise nicht angewendet', 'payment_status_-2' => 'Teilweise nicht angewendet',
'color_theme' => 'Farbthema', 'color_theme' => 'Farbthema',
'start_migration' => 'Beginne mit der Migration.', 'start_migration' => 'Beginne mit der Migration.',
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact', 'recurring_cancellation_request' => 'Antrag auf Stornierung wiederkehrender Rechnungen von :contact',
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice', 'recurring_cancellation_request_body' => ':contact vom Kunden :client bittet um Stornierung der wiederkehrenden Rechnung :invoice',
'hello' => 'Hallo', 'hello' => 'Hallo',
'group_documents' => 'Gruppendokumente', 'group_documents' => 'Gruppendokumente',
'quote_approval_confirmation_label' => 'Sind Sie sicher, dass Sie diesem Angebot / Kostenvoranschlag zustimmen möchten?', 'quote_approval_confirmation_label' => 'Sind Sie sicher, dass Sie diesem Angebot / Kostenvoranschlag zustimmen möchten?',
@ -4189,10 +4189,10 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
'zoho' => 'Zoho', 'zoho' => 'Zoho',
'accounting' => 'Buchhaltung', 'accounting' => 'Buchhaltung',
'required_files_missing' => 'Bitte geben Sie alle CSV-Dateien an.', 'required_files_missing' => 'Bitte geben Sie alle CSV-Dateien an.',
'migration_auth_label' => 'Let\'s continue by authenticating.', 'migration_auth_label' => 'Authentifizierung fortsetzen.',
'api_secret' => 'API-Secret', 'api_secret' => 'API-Secret',
'migration_api_secret_notice' => 'You can find API_SECRET in the .env file or Invoice Ninja v5. If property is missing, leave field blank.', 'migration_api_secret_notice' => 'Sie finden API_SECRET in der .env-Datei oder in Invoice Ninja v5. Wenn die Eigenschaft fehlt, lassen Sie das Feld leer.',
'billing_coupon_notice' => 'Your discount will be applied on the checkout.', 'billing_coupon_notice' => 'Ihr Rabatt wird an der Kasse abgezogen.',
'use_last_email' => 'Vorherige E-Mail benutzen', 'use_last_email' => 'Vorherige E-Mail benutzen',
'activate_company' => 'Unternehmen aktivieren', 'activate_company' => 'Unternehmen aktivieren',
'activate_company_help' => 'Aktivieren sie Email, wiederkehrende Rechnungen und Benachrichtigungen', 'activate_company_help' => 'Aktivieren sie Email, wiederkehrende Rechnungen und Benachrichtigungen',
@ -4214,7 +4214,7 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
'disconnect_google' => 'Google-Konto entfernen', 'disconnect_google' => 'Google-Konto entfernen',
'disable_two_factor' => 'Zwei-Faktor-Authentifizierung deaktivieren', 'disable_two_factor' => 'Zwei-Faktor-Authentifizierung deaktivieren',
'invoice_task_datelog' => 'Aufgabenzeiterfassung in Rechnung stellen', 'invoice_task_datelog' => 'Aufgabenzeiterfassung in Rechnung stellen',
'invoice_task_datelog_help' => 'Add date details to the invoice line items', 'invoice_task_datelog_help' => 'Datumsdetails zu den Rechnungsposten hinzufügen',
'promo_code' => 'Gutscheincode', 'promo_code' => 'Gutscheincode',
'recurring_invoice_issued_to' => 'Wiederkehrende Rechnung ausgestellt an', 'recurring_invoice_issued_to' => 'Wiederkehrende Rechnung ausgestellt an',
'subscription' => 'Abonnement', 'subscription' => 'Abonnement',
@ -4222,140 +4222,330 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
'deleted_subscription' => 'Abonnement gelöscht', 'deleted_subscription' => 'Abonnement gelöscht',
'removed_subscription' => 'Abonnement entfernt', 'removed_subscription' => 'Abonnement entfernt',
'restored_subscription' => 'Abonnement wiederhergestellt', 'restored_subscription' => 'Abonnement wiederhergestellt',
'search_subscription' => 'Search 1 Subscription', 'search_subscription' => 'Suchen Sie 1 Abonnement',
'search_subscriptions' => ':count Abonnements durchsuchen', 'search_subscriptions' => ':count Abonnements durchsuchen',
'subdomain_is_not_available' => 'Subdomain ist nicht verfügbar', 'subdomain_is_not_available' => 'Subdomain ist nicht verfügbar',
'connect_gmail' => 'Mit Gmail verbinden', 'connect_gmail' => 'Mit Gmail verbinden',
'disconnect_gmail' => 'von Gmail trennen', 'disconnect_gmail' => 'von Gmail trennen',
'connected_gmail' => 'Mit Gmail erfolgreich verbunden', 'connected_gmail' => 'Mit Gmail erfolgreich verbunden',
'disconnected_gmail' => 'Von Gmail erfolgreich getrennt', 'disconnected_gmail' => 'Von Gmail erfolgreich getrennt',
'update_fail_help' => 'Changes to the codebase may be blocking the update, you can run this command to discard the changes:', 'update_fail_help' => 'Änderungen an der Codebasis können das Update blockieren, Sie können diesen Befehl ausführen, um die Änderungen zu verwerfen:',
'client_id_number' => 'Kundennummer', 'client_id_number' => 'Kundennummer',
'count_minutes' => ':count Minuten', 'count_minutes' => ':count Minuten',
'password_timeout' => 'Passwort Timeout', 'password_timeout' => 'Passwort Timeout',
'shared_invoice_credit_counter' => 'Shared Invoice/Credit Counter', 'shared_invoice_credit_counter' => 'gemeinsamer Rechnungs- / Kreditzähler',
'activity_80' => ':user hat Abonnement :subscription erstellt', 'activity_80' => ':user hat Abonnement :subscription erstellt',
'activity_81' => ':user hat Abonnement :subscription geändert', 'activity_81' => ':user hat Abonnement :subscription geändert',
'activity_82' => ':user hat Abonnement :subscription archiviert', 'activity_82' => ':user hat Abonnement :subscription archiviert',
'activity_83' => ':user hat Abonnement :subscription gelöscht', 'activity_83' => ':user hat Abonnement :subscription gelöscht',
'activity_84' => ':user hat Abonnement :subscription wiederhergestellt', 'activity_84' => ':user hat Abonnement :subscription wiederhergestellt',
'amount_greater_than_balance_v5' => 'The amount is greater than the invoice balance. You cannot overpay an invoice.', 'amount_greater_than_balance_v5' => 'Der Betrag ist größer als der Rechnungsbetrag, Eine Überzahlung ist nicht erlaubt.',
'click_to_continue' => 'Weiter', 'click_to_continue' => 'Weiter',
'notification_invoice_created_body' => 'The following invoice :invoice was created for client :client for :amount.', 'notification_invoice_created_body' => 'Die Rechnung :invoice für den Kunden :client mit dem Betrag :amount wurde erstellt',
'notification_invoice_created_subject' => 'Invoice :invoice was created for :client', 'notification_invoice_created_subject' => 'Rechnung :invoice für :client erstellt',
'notification_quote_created_body' => 'The following quote :invoice was created for client :client for :amount.', 'notification_quote_created_body' => 'Das folgende Angebot :invoice wurde für den Kunde :client für :amount erstellt.',
'notification_quote_created_subject' => 'Quote :invoice was created for :client', 'notification_quote_created_subject' => 'Angebot :invoice für den Kunden :client wurde erstellt',
'notification_credit_created_body' => 'The following credit :invoice was created for client :client for :amount.', 'notification_credit_created_body' => 'Die Gutschrift :invoice für den Kunden :client über :amount wurde erstellt.',
'notification_credit_created_subject' => 'Credit :invoice was created for :client', 'notification_credit_created_subject' => 'Gutschrift :invoice für :client erstellt',
'max_companies' => 'Maximum companies migrated', 'max_companies' => 'Maximale Anzahl an Firmen erreicht',
'max_companies_desc' => 'You have reached your maximum number of companies. Delete existing companies to migrate new ones.', 'max_companies_desc' => 'Sie haben Ihre maximale Anzahl an Unternehmen erreicht. Löschen Sie vorhandene Unternehmen, um neue Firmen einzufügen.',
'migration_already_completed' => 'Company already migrated', 'migration_already_completed' => 'Firma existiert bereits',
'migration_already_completed_desc' => 'Looks like you already migrated <b> :company_name </b>to the V5 version of the Invoice Ninja. In case you want to start over, you can force migrate to wipe existing data.', 'migration_already_completed_desc' => 'Anscheinend haben Sie :company_name bereits auf die V5-Version von Invoice Ninja migriert. Falls Sie von vorne beginnen möchten, können Sie die Migration erzwingen, um vorhandene Daten zu löschen.',
'payment_method_cannot_be_authorized_first' => 'This payment method can be can saved for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.', 'payment_method_cannot_be_authorized_first' => 'Diese Zahlungsmethode kann für die zukünftige Verwendung gespeichert werden, sobald Sie Ihre erste Transaktion abgeschlossen haben. Vergessen Sie nicht, während des Zahlungsvorgangs "Kreditkartendaten hinterlegen" zu aktivieren.',
'new_account' => 'New account', 'new_account' => 'Neues Konto',
'activity_100' => ':user created recurring invoice :recurring_invoice', 'activity_100' => ':user hat die wiederkehrende Rechnung :recurring_invoice erstellt.',
'activity_101' => ':user updated recurring invoice :recurring_invoice', 'activity_101' => ':user hat die wiederkehrende Rechnung :recurring_invoice aktuallisiert',
'activity_102' => ':user archived recurring invoice :recurring_invoice', 'activity_102' => ':user hat die wiederkehrende Rechnung :recurring_invoice archiviert',
'activity_103' => ':user deleted recurring invoice :recurring_invoice', 'activity_103' => ':user hat die wiederkehrende Rechnung :recurring_invoice gelöscht.',
'activity_104' => ':user restored recurring invoice :recurring_invoice', 'activity_104' => ':user hat die wiederkehrende Rechnung :recurring_invoice wiederhergestellt.',
'new_login_detected' => 'New login detected for your account.', 'new_login_detected' => 'Neue Anmeldung für Ihr Konto erkannt.',
'new_login_description' => 'You recently logged in to your Invoice Ninja account from a new location or device:<br><br><b>IP:</b> :ip<br><b>Time:</b> :time<br><b>Email:</b> :email', 'new_login_description' => 'Sie haben sich kürzlich von einem neuen Standort oder Gerät aus bei Ihrem Invoice Ninja-Konto angemeldet: <br><br><b>IP:</b> :ip<br><b>Time: </b>:time<br> Email: <b> :email',
'download_backup_subject' => 'Your company backup is ready for download', 'download_backup_subject' => 'Ihr Firmen-Backup steht zum Download bereit',
'contact_details' => 'Contact Details', 'contact_details' => 'Kontakt Informationen',
'download_backup_subject' => 'Your company backup is ready for download', 'download_backup_subject' => 'Ihr Firmen-Backup steht zum Download bereit',
'account_passwordless_login' => 'Account passwordless login', 'account_passwordless_login' => 'Passwortfreies einloggen',
'user_duplicate_error' => 'Cannot add the same user to the same company', 'user_duplicate_error' => 'Derselbe Benutzer kann nicht derselben Firma hinzugefügt werden',
'user_cross_linked_error' => 'User exists but cannot be crossed linked to multiple accounts', 'user_cross_linked_error' => 'Der Benutzer ist vorhanden, kann aber nicht mit mehreren Konten verknüpft werden',
'ach_verification_notification_label' => 'ACH verification', 'ach_verification_notification_label' => 'ACH-Verifizierung',
'ach_verification_notification' => 'Connecting bank accounts require verification. Payment gateway will automatically send two small deposits for this purpose. These deposits take 1-2 business days to appear on the customer\'s online statement.', 'ach_verification_notification' => 'Für die Verbindung von Bankkonten ist eine Überprüfung erforderlich. Das Zahlungsgateway sendet zu diesem Zweck automatisch zwei kleine Einzahlungen. Es dauert 1-2 Werktage, bis diese Einzahlungen auf dem Online-Kontoauszug des Kunden erscheinen.',
'login_link_requested_label' => 'Anmeldelink angefordert', 'login_link_requested_label' => 'Anmeldelink angefordert',
'login_link_requested' => 'There was a request to login using link. If you did not request this, it\'s safe to ignore it.', 'login_link_requested' => 'Es gab eine Aufforderung, sich über einen Link anzumelden. Wenn Sie dies nicht angefordert haben, können Sie es ignorieren.',
'invoices_backup_subject' => 'Your invoices are ready for download', 'invoices_backup_subject' => 'Ihre Rechnungen stehen zum Download bereit',
'migration_failed_label' => 'Update der Datenbank-Struktur fehlgeschlagen', 'migration_failed_label' => 'Update der Datenbank-Struktur fehlgeschlagen',
'migration_failed' => 'Looks like something went wrong with the migration for the following company:', 'migration_failed' => 'Es sieht so aus, als wäre bei der Migration für das folgende Unternehmen etwas schief gelaufen:',
'client_email_company_contact_label' => 'If you have any questions please contact us, we\'re here to help!', 'client_email_company_contact_label' => 'Wenn Sie Fragen haben, wenden Sie sich bitte an uns, wir sind gerne für Sie da!',
'quote_was_approved_label' => 'Quote was approved', 'quote_was_approved_label' => 'Das Angebot wurde genehmigt',
'quote_was_approved' => 'We would like to inform you that quote was approved.', 'quote_was_approved' => 'Wir möchten Ihnen mitteilen, dass das Angebot angenommen wurde.',
'company_import_failure_subject' => 'Error importing :company', 'company_import_failure_subject' => 'Fehler beim Importieren von :company',
'company_import_failure_body' => 'There was an error importing the company data, the error message was:', 'company_import_failure_body' => 'Beim Importieren der Unternehmensdaten ist ein Fehler aufgetreten, die Fehlermeldung lautete:',
'recurring_invoice_due_date' => 'Fälligkeitsdatum', 'recurring_invoice_due_date' => 'Fälligkeitsdatum',
'amount_cents' => 'Amount in pennies,pence or cents', 'amount_cents' => 'Betrag in Pfennigen, Pence oder Cents',
'default_payment_method_label' => 'Standard Zahlungsart', 'default_payment_method_label' => 'Standard Zahlungsart',
'default_payment_method' => 'Machen Sie dies zu Ihrer bevorzugten Zahlungsmethode', 'default_payment_method' => 'Machen Sie dies zu Ihrer bevorzugten Zahlungsmethode',
'already_default_payment_method' => 'This is your preferred way of paying.', 'already_default_payment_method' => 'Dies ist die von Ihnen bevorzugte Art der Bezahlung.',
'auto_bill_disabled' => 'Auto Bill Disabled', 'auto_bill_disabled' => 'Automatische Bezahlung Deaktiviert',
'select_payment_method' => 'Wählen Sie eine Zahlungsmethode:', 'select_payment_method' => 'Wählen Sie eine Zahlungsmethode:',
'login_without_password' => 'Ohne Passwort anmelden', 'login_without_password' => 'Ohne Passwort anmelden',
'email_sent' => 'Benachrichtigen, wenn eine Rechnung <strong>versendet</strong> wurde', 'email_sent' => 'Benachrichtigen, wenn eine Rechnung <strong>versendet</strong> wurde',
'one_time_purchases' => 'Einmalzahlungen', 'one_time_purchases' => 'Einmalzahlungen',
'recurring_purchases' => 'Wiederkehrende Käufe', 'recurring_purchases' => 'Wiederkehrende Käufe',
'you_might_be_interested_in_following' => 'You might be interested in the following', 'you_might_be_interested_in_following' => 'Das könnte Sie interessieren',
'quotes_with_status_sent_can_be_approved' => 'Only quotes with "Sent" status can be approved.', 'quotes_with_status_sent_can_be_approved' => 'Nur Angebote mit dem Status "Gesendet" können genehmigt werden.',
'no_quotes_available_for_download' => 'No quotes available for download.', 'no_quotes_available_for_download' => 'Keine Angebote zum Herunterladen verfügbar.',
'copyright' => 'Copyright', 'copyright' => 'Copyright',
'user_created_user' => ':user created :created_user at :time', 'user_created_user' => ':user erstellt :created_user zu :time',
'company_deleted' => 'Company deleted', 'company_deleted' => 'Unternehmen gelöscht',
'company_deleted_body' => 'Company [ :company ] was deleted by :user', 'company_deleted_body' => 'Firma [ : company ] wurde von :user gelöscht',
'back_to' => 'Back to :url', 'back_to' => 'Zurück zu :url',
'stripe_connect_migration_title' => 'Connect your Stripe Account', 'stripe_connect_migration_title' => 'Verbinden Sie Ihr Stripe-Konto',
'stripe_connect_migration_desc' => 'Invoice Ninja v5 uses Stripe Connect to link your Stripe account to Invoice Ninja. This provides an additional layer of security for your account. Now that you data has migrated, you will need to Authorize Stripe to accept payments in v5.<br><br>To do this, navigate to Settings > Online Payments > Configure Gateways. Click on Stripe Connect and then under Settings click Setup Gateway. This will take you to Stripe to authorize Invoice Ninja and on your return your account will be successfully linked!', 'stripe_connect_migration_desc' => 'Verbinden Sie Ihre Rechnung Ninja v5 verwendet Stripe Connect, um Ihr Stripe-Konto mit Invoice Ninja zu verbinden. Dies bietet eine zusätzliche Sicherheitsebene für Ihr Konto. Nachdem Ihre Daten migriert wurden, müssen Sie Stripe autorisieren, Zahlungen in v5 zu akzeptieren. <br><br>Navigieren Sie dazu zu Einstellungen > Online-Zahlungen > Gateways konfigurieren. Klicken Sie auf Stripe Connect und dann unter Einstellungen auf Gateway einrichten. Dadurch werden Sie zu Stripe weitergeleitet, um Invoice Ninja zu autorisieren, und bei Ihrer Rückkehr wird Ihr Konto erfolgreich verknüpft sein!',
'email_quota_exceeded_subject' => 'Account email quota exceeded.', 'email_quota_exceeded_subject' => 'E-Mail-Kontingent überschritten.',
'email_quota_exceeded_body' => 'In a 24 hour period you have sent :quota emails. <br> We have paused your outbound emails.<br><br> Your email quota will reset at 23:00 UTC.', 'email_quota_exceeded_body' => 'In einem Zeitraum von 24 Stunden haben Sie : quota emails gesendet.<br> Wir haben Ihre ausgehenden E-Mails pausiert.<br><br> Ihr E-Mail-Kontingent wird um 23:00 UTC zurückgesetzt.',
'auto_bill_option' => 'Opt in or out of having this invoice automatically charged.', 'auto_bill_option' => 'Aktivieren oder deaktivieren Sie die automatische Belastung dieser Rechnung.',
'lang_Arabic' => 'Arabisch', 'lang_Arabic' => 'Arabisch',
'lang_Persian' => 'Persisch', 'lang_Persian' => 'Persisch',
'lang_Latvian' => 'Lettisch', 'lang_Latvian' => 'Lettisch',
'expiry_date' => 'Ablaufdatum', 'expiry_date' => 'Ablaufdatum',
'cardholder_name' => 'Name des Karteninhabers', 'cardholder_name' => 'Name des Karteninhabers',
'recurring_quote_number_taken' => 'Recurring Quote number :number already taken', 'recurring_quote_number_taken' => 'Wiederkehrende Angebotsnummer :number already taken',
'account_type' => 'Kontotyp', 'account_type' => 'Kontotyp',
'locality' => 'Standort', 'locality' => 'Standort',
'checking' => 'Checking', 'checking' => 'Checking',
'savings' => 'Savings', 'savings' => 'Ersparnisse',
'unable_to_verify_payment_method' => 'Unable to verify payment method.', 'unable_to_verify_payment_method' => 'Die Zahlungsmethode kann nicht verifiziert werden.',
'generic_gateway_error' => 'Gateway configuration error. Please check your credentials.', 'generic_gateway_error' => 'Gateway-Konfigurationsfehler. Bitte überprüfen Sie Ihre Anmeldedaten.',
'my_documents' => 'Meine Dokumente', 'my_documents' => 'Meine Dokumente',
'payment_method_cannot_be_preauthorized' => 'This payment method cannot be preauthorized.', 'payment_method_cannot_be_preauthorized' => 'Diese Zahlungsmethode kann nicht vorautorisiert werden.',
'kbc_cbc' => 'KBC/CBC', 'kbc_cbc' => 'KBC/CBC',
'bancontact' => 'Bancontact', 'bancontact' => 'Bancontact',
'sepa_mandat' => 'Indem Sie Ihre IBAN angeben und diese Zahlung bestätigen, ermächtigen Sie :company und Stripe, unseren Zahlungsdienstleister, Anweisungen an Ihre Bank zu senden, um Ihr Konto zu belasten, und Ihre Bank, Ihr Konto gemäß diesen Anweisungen zu belasten. Sie haben Anspruch auf eine Rückerstattung von Ihrer Bank gemäß den Bestimmungen und Bedingungen Ihrer Vereinbarung mit Ihrer Bank. Sie können innerhalb von 8 Wochen, beginnend mit dem Tag der Abbuchung, die Erstattung des Betrages von Ihrem Konto verlangen.', 'sepa_mandat' => 'Indem Sie Ihre IBAN angeben und diese Zahlung bestätigen, ermächtigen Sie :company und Stripe, unseren Zahlungsdienstleister, Anweisungen an Ihre Bank zu senden, um Ihr Konto zu belasten, und Ihre Bank, Ihr Konto gemäß diesen Anweisungen zu belasten. Sie haben Anspruch auf eine Rückerstattung von Ihrer Bank gemäß den Bestimmungen und Bedingungen Ihrer Vereinbarung mit Ihrer Bank. Sie können innerhalb von 8 Wochen, beginnend mit dem Tag der Abbuchung, die Erstattung des Betrages von Ihrem Konto verlangen.',
'ideal' => 'iDEAL', 'ideal' => 'iDEAL',
'bank_account_holder' => 'Kontoinhaber', 'bank_account_holder' => 'Kontoinhaber',
'aio_checkout' => 'All-in-one checkout', 'aio_checkout' => 'All-in-one Checkout',
'przelewy24' => 'Przelewy24', 'przelewy24' => 'Przelewy24',
'przelewy24_accept' => 'I declare that I have familiarized myself with the regulations and information obligation of the Przelewy24 service.', 'przelewy24_accept' => 'Ich erkläre, dass ich mich mit den Regelungen und Informationspflichten des Przelewy24-Dienstes vertraut gemacht habe.',
'giropay' => 'GiroPay', 'giropay' => 'GiroPay',
'giropay_law' => 'Mit der Eingabe Ihrer Kundendaten (z. B. Name, Bankleitzahl und Kontonummer) erklären Sie (der Kunde), dass die Angabe dieser Daten freiwillig erfolgt.', 'giropay_law' => 'Mit der Eingabe Ihrer Kundendaten (z. B. Name, Bankleitzahl und Kontonummer) erklären Sie (der Kunde), dass die Angabe dieser Daten freiwillig erfolgt.',
'eps' => 'EPS', 'eps' => 'EPS',
'becs' => 'BECS Direct Debit', 'becs' => 'BECS-Lastschriftverfahren',
'becs_mandate' => 'Mit der Angabe Ihrer Kontodaten erklären Sie sich mit dieser <a class="underline" href="https://stripe.com/au-becs-dd-service-agreement/legal">Lastschriftanforderung und der Dienstleistungsvereinbarung zur Lastschriftanforderung</a> einverstanden und ermächtigen Stripe Payments Australia Pty Ltd ACN 160 180 343 Direct Debit User ID number 507156 ("Stripe"), Ihr Konto über das Bulk Electronic Clearing System (BECS) im Namen von :company (dem "Händler") mit allen Beträgen zu belasten, die Ihnen vom Händler separat mitgeteilt werden. Sie bestätigen, dass Sie entweder Kontoinhaber oder Zeichnungsberechtigter für das oben genannte Konto sind.', 'becs_mandate' => 'Mit der Angabe Ihrer Kontodaten erklären Sie sich mit dieser <a class="underline" href="https://stripe.com/au-becs-dd-service-agreement/legal">Lastschriftanforderung und der Dienstleistungsvereinbarung zur Lastschriftanforderung</a> einverstanden und ermächtigen Stripe Payments Australia Pty Ltd ACN 160 180 343 Direct Debit User ID number 507156 ("Stripe"), Ihr Konto über das Bulk Electronic Clearing System (BECS) im Namen von :company (dem "Händler") mit allen Beträgen zu belasten, die Ihnen vom Händler separat mitgeteilt werden. Sie bestätigen, dass Sie entweder Kontoinhaber oder Zeichnungsberechtigter für das oben genannte Konto sind.',
'you_need_to_accept_the_terms_before_proceeding' => 'Sie müssen die Bedingungen akzeptieren, um fortzufahren.', 'you_need_to_accept_the_terms_before_proceeding' => 'Sie müssen die Bedingungen akzeptieren, um fortzufahren.',
'direct_debit' => 'Direct Debit', 'direct_debit' => 'Lastschriftverfahren',
'clone_to_expense' => 'Clone to expense', 'clone_to_expense' => 'Klonen zu Ausgabe',
'checkout' => 'Kasse', 'checkout' => 'Kasse',
'acss' => 'Pre-authorized debit payments', 'acss' => 'Vorautorisierte Lastschriftzahlungen',
'invalid_amount' => 'Invalid amount. Number/Decimal values only.', 'invalid_amount' => 'Ungültiger Betrag. Nur Zahlen/Dezimalwerte.',
'client_payment_failure_body' => 'Payment for Invoice :invoice for amount :amount failed.', 'client_payment_failure_body' => 'Zahlung für Rechnung :invoice for amount :amount fehlgeschlagen.',
'browser_pay' => 'Google Pay, Apple Pay, Microsoft Pay', 'browser_pay' => 'Google Pay, Apple Pay, Microsoft Pay',
'no_available_methods' => 'We can\'t find any credit cards on your device. <a href="https://invoiceninja.github.io/docs/payments#apple-pay-google-pay-microsoft-pay" target="_blank" class="underline">Read more about this.</a>', 'no_available_methods' => 'Wir können keine Kreditkarten auf Ihrem Gerät finden. <a href="https://invoiceninja.github.io/docs/payments#apple-pay-google-pay-microsoft-pay" target="_blank" class="underline">Lesen Sie mehr darüber.</a>',
'gocardless_mandate_not_ready' => 'Payment mandate is not ready. Please try again later.', 'gocardless_mandate_not_ready' => 'Das Zahlungsmandat ist nicht fertig. Bitte versuchen Sie es später noch einmal.',
'payment_type_instant_bank_pay' => 'Instant Bank Pay', 'payment_type_instant_bank_pay' => 'Sofortige Banküberweisung',
'payment_type_iDEAL' => 'iDEAL', 'payment_type_iDEAL' => 'iDEAL',
'payment_type_Przelewy24' => 'Przelewy24', 'payment_type_Przelewy24' => 'Przelewy24',
'payment_type_Mollie Bank Transfer' => 'Überweisung', 'payment_type_Mollie Bank Transfer' => 'Mollie Banküberweisung',
'payment_type_KBC/CBC' => 'KBC/CBC', 'payment_type_KBC/CBC' => 'KBC/CBC',
'payment_type_Instant Bank Pay' => 'Instant Bank Pay', 'payment_type_Instant Bank Pay' => 'Sofortige Banküberweisung',
'payment_type_Hosted Page' => 'Hosted Page', 'payment_type_Hosted Page' => 'Gehostete Seite',
'payment_type_GiroPay' => 'GiroPay', 'payment_type_GiroPay' => 'GiroPay',
'payment_type_EPS' => 'EPS', 'payment_type_EPS' => 'EPS',
'payment_type_Direct Debit' => 'Direct Debit', 'payment_type_Direct Debit' => 'Lastschriftverfahren',
'payment_type_Bancontact' => 'Bancontact', 'payment_type_Bancontact' => 'Bancontact',
'payment_type_BECS' => 'BECS', 'payment_type_BECS' => 'BECS',
'payment_type_ACSS' => 'ACSS', 'payment_type_ACSS' => 'ACSS',
'gross_line_total' => 'Gross line total', 'gross_line_total' => 'Bruttozeile gesamt',
'lang_Slovak' => 'Slovak', 'lang_Slovak' => 'Slowakisch',
'normal' => 'Normal',
'large' => 'Groß',
'extra_large' => 'Extra Groß',
'show_pdf_preview' => 'PDF-Vorschau anzeigen',
'show_pdf_preview_help' => 'PDF-Vorschau bei der Bearbeitung von Rechnungen anzeigen',
'print_pdf' => 'PDF drucken',
'remind_me' => 'Erinnere mich',
'instant_bank_pay' => 'Instant Bank Pay',
'click_selected' => 'Ausgewähltes anklicken',
'hide_preview' => 'Vorschau ausblenden',
'edit_record' => 'Datensatz bearbeiten',
'credit_is_more_than_invoice' => 'Der Kreditbetrag kann nicht höher sein als der Rechnungsbetrag',
'please_set_a_password' => 'Bitte legen Sie ein Kontopasswort fest',
'recommend_desktop' => 'Wir empfehlen, die Desktop-Anwendung zu verwenden, um die beste Leistung zu erzielen.',
'recommend_mobile' => 'Wir empfehlen die Verwendung der mobilen App, um die beste Leistung zu erzielen.',
'disconnected_gateway' => 'Gateway erfolgreich getrennt',
'disconnect' => 'Trennen',
'add_to_invoices' => 'Zu Rechnungen hinzufügen',
'bulk_download' => 'Herunterladen',
'persist_data_help' => 'Daten lokal speichern, damit die Anwendung schneller starten kann. (Deaktivierung kann die Leistung bei großen Konten verbessern)',
'persist_ui' => 'Benutzeroberfläche beibehalten',
'persist_ui_help' => 'UI-Status lokal speichern, damit die Anwendung an der letzten Position startet (Deaktivierung kann die Leistung verbessern)',
'client_postal_code' => 'Postleitzahl des Kunden',
'client_vat_number' => 'Umsatzsteuer-Identifikationsnummer des Kunden',
'has_tasks' => 'Has Tasks',
'registration' => 'Registration',
'unauthorized_stripe_warning' => 'Bitte autorisieren Sie Stripe zur Annahme von Online-Zahlungen.',
'fpx' => 'FPX',
'update_all_records' => 'Alle Datensätze aktualisieren',
'set_default_company' => 'Standardunternehmen festlegen',
'updated_company' => 'Unternehmen wurde erfolgreich aktualisiert',
'kbc' => 'KBC',
'why_are_you_leaving' => 'Helfen Sie uns, uns zu verbessern, indem Sie uns sagen, warum (optional)',
'webhook_success' => 'Webhook Success',
'error_cross_client_tasks' => 'Die Aufgaben müssen alle zum selben Kunden gehören',
'error_cross_client_expenses' => 'Die Ausgaben müssen alle zu demselben Kunden gehören',
'app' => 'App',
'for_best_performance' => 'Für die beste Leistung laden Sie die App herunter :app',
'bulk_email_invoice' => 'Email Invoice',
'bulk_email_quote' => 'Email Quote',
'bulk_email_credit' => 'Email Credit',
'removed_recurring_expense' => 'Erfolgreich wiederkehrende Ausgaben entfernt',
'search_recurring_expense' => 'Wiederkehrende Ausgaben suchen',
'search_recurring_expenses' => 'Wiederkehrende Ausgaben suchen',
'last_sent_date' => 'Datum des letzten Versands',
'include_drafts' => 'Entwürfe einschließen',
'include_drafts_help' => 'Entwürfe von Aufzeichnungen in Berichte einbeziehen',
'is_invoiced' => 'Ist in Rechnung gestellt',
'change_plan' => 'Change Plan',
'persist_data' => 'Daten aufbewahren',
'customer_count' => 'Kundenzahl',
'verify_customers' => 'Kunden überprüfen',
'google_analytics_tracking_id' => 'Google Analytics Tracking ID',
'decimal_comma' => 'Decimal Comma',
'use_comma_as_decimal_place' => 'Komma als Dezimalstelle in Formularen verwenden',
'select_method' => 'Select Method',
'select_platform' => 'Select Platform',
'use_web_app_to_connect_gmail' => 'Bitte verwenden Sie die Web-App, um sich mit Gmail zu verbinden',
'expense_tax_help' => 'Postensteuersätze sind deaktiviert',
'enable_markdown' => 'Enable Markdown',
'enable_markdown_help' => 'Konvertierung von Markdown in HTML in der PDF-Datei',
'add_second_contact' => 'Zweiten Kontakt hinzufügen',
'previous_page' => 'Vorherige Seite',
'next_page' => 'Nächste Seite',
'export_colors' => 'Farben exportieren',
'import_colors' => 'Farben importieren',
'clear_all' => 'Alle löschen',
'contrast' => 'Kontrast',
'custom_colors' => 'Eigene Farben',
'colors' => 'Farben',
'sidebar_active_background_color' => 'Aktive Hintergrundfarbe der Seitenleiste',
'sidebar_active_font_color' => 'Schriftfarbe der aktiven Seitenleiste',
'sidebar_inactive_background_color' => 'Seitenleiste Inaktive Hintergrundfarbe',
'sidebar_inactive_font_color' => 'Seitenleiste Inaktiv Schriftfarbe',
'table_alternate_row_background_color' => 'Tabelle alternierende Zeilenhintergrundfarbe',
'invoice_header_background_color' => 'Hintergrundfarbe der Rechnungskopfzeile',
'invoice_header_font_color' => 'Schriftfarbe der Rechnungskopfzeile',
'review_app' => 'App bewerten',
'check_status' => 'Status prüfen',
'free_trial' => 'Kostenlose Testversion',
'free_trial_help' => 'Alle Konten erhalten eine zweiwöchige Testversion des Pro-Tarifs. Nach Ablauf der Testversion wechselt Ihr Konto automatisch in den kostenlosen Tarif.',
'free_trial_ends_in_days' => 'Die Testversion des Pro-Plans endet in :count Tagen, klicken Sie zum Upgrade.',
'free_trial_ends_today' => 'Heute ist der letzte Tag des Pro-Tarifs, klicken Sie zum Upgrade.',
'change_email' => 'E-Mail ändern',
'client_portal_domain_hint' => 'Konfigurieren Sie optional eine separate Kunden-Portal-Domäne',
'tasks_shown_in_portal' => 'Im Portal angezeigte Aufgaben',
'uninvoiced' => 'Nicht in Rechnung gestellt',
'subdomain_guide' => 'Die Subdomain wird im Kundenportal verwendet, um Links zu personalisieren, die Ihrer Marke entsprechen. z.B. https://your-brand.invoicing.co',
'send_time' => 'Sendezeit',
'import_settings' => 'Einstellungen importieren',
'json_file_missing' => 'Bitte stellen Sie die JSON-Datei zur Verfügung',
'json_option_missing' => 'Bitte wählen Sie, um die Einstellungen und/oder Daten zu importieren',
'json' => 'JSON',
'no_payment_types_enabled' => 'Keine Zahlungsarten aktiviert',
'wait_for_data' => 'Bitte warten Sie, bis die Daten vollständig geladen sind',
'net_total' => 'Netto Gesamt',
'has_taxes' => 'enthält Steuern',
'import_customers' => 'Kunden importieren',
'imported_customers' => 'Successfully started importing customers',
'login_success' => 'Erfolgreiche Anmeldung',
'login_failure' => 'Anmeldung fehlgeschlagen',
'exported_data' => 'Sobald die Datei fertig ist, erhalten Sie eine E-Mail mit einem Download-Link.',
'include_deleted_clients' => 'Gelöschte Kunden einbeziehen',
'include_deleted_clients_help' => 'Datensätze von gelöschten Kunden laden',
'step_1_sign_in' => 'Schritt 1: Registrieren',
'step_2_authorize' => 'Schritt 2: autorisieren',
'account_id' => 'Account ID',
'migration_not_yet_completed' => 'Die Migration ist noch nicht abgeschlossen',
'show_task_end_date' => 'Ende der Aufgabe anzeigen',
'show_task_end_date_help' => 'Aktivieren Sie die Angabe des Enddatums der Aufgabe',
'gateway_setup' => 'Gateway-Einstellungen',
'preview_sidebar' => 'Vorschau der Seitenleiste',
'years_data_shown' => 'Years Data Shown',
'ended_all_sessions' => 'alle Sitzungen erfolgreich beendet',
'end_all_sessions' => 'Alle Sitzungen beenden',
'count_session' => '1 Session',
'count_sessions' => ':count Sessions',
'invoice_created' => 'Rechnung erstellt',
'quote_created' => 'Angebot erstellt',
'credit_created' => 'Gutschrift erstellt',
'enterprise' => 'Enterprise',
'invoice_item' => 'Invoice Item',
'quote_item' => 'Quote Item',
'order' => 'Order',
'search_kanban' => 'Search Kanban',
'search_kanbans' => 'Search Kanban',
'move_top' => 'Nach oben bewegen',
'move_up' => 'Nach unten bewegen',
'move_down' => 'Move Down',
'move_bottom' => 'Move Bottom',
'body_variable_missing' => 'Fehler: das benutzerdefinierte E-Mail Template muss die :body Variable beinhalten',
'add_body_variable_message' => 'bitte stelle sicher das die :body Variable eingefügt ist',
'view_date_formats' => 'View Date Formats',
'is_viewed' => 'Is Viewed',
'letter' => 'Letter',
'legal' => 'Legal',
'page_layout' => 'Seiten Layout',
'portrait' => 'Hochformat',
'landscape' => 'Querformat',
'owner_upgrade_to_paid_plan' => 'Der Kontoinhaber kann auf einen kostenpflichtigen Plan upgraden, um die erweiterten erweiterten Einstellungen zu aktivieren',
'upgrade_to_paid_plan' => 'Führen Sie ein Upgrade auf einen kostenpflichtigen Plan durch, um die erweiterten Einstellungen zu aktivieren',
'invoice_payment_terms' => 'Zahlungsbedingungen für Rechnungen',
'quote_valid_until' => 'Angebot gültig bis',
'no_headers' => 'No Headers',
'add_header' => 'Add Header',
'remove_header' => 'Remove Header',
'return_url' => 'Return URL',
'rest_method' => 'REST Method',
'header_key' => 'Header Key',
'header_value' => 'Header Value',
'recurring_products' => 'Recurring Products',
'promo_discount' => 'Promo Discount',
'allow_cancellation' => 'Allow Cancellation',
'per_seat_enabled' => 'Per Seat Enabled',
'max_seats_limit' => 'Max Seats Limit',
'trial_enabled' => 'Trial Enabled',
'trial_duration' => 'Trial Duration',
'allow_query_overrides' => 'Allow Query Overrides',
'allow_plan_changes' => 'Allow Plan Changes',
'plan_map' => 'Plan Map',
'refund_period' => 'Refund Period',
'webhook_configuration' => 'Webhook Configuration',
'purchase_page' => 'Purchase Page',
'email_bounced' => 'E-Mail zurückgesendet',
'email_spam_complaint' => 'Spam Complaint',
'email_delivery' => 'E-Mail-Zustellung',
'webhook_response' => 'Webhook Response',
'pdf_response' => 'PDF Response',
'authentication_failure' => 'Authentifizierungsfehler',
'pdf_failed' => 'PDF fehgeschlagen',
'pdf_success' => 'PDF erfolgreich',
'modified' => 'geändert',
'html_mode' => 'HTML Modus',
'html_mode_help' => 'Vorschau von Aktualisierungen schneller, aber weniger genau',
'status_color_theme' => 'Status Farbschema',
'load_color_theme' => 'lade Farbschema',
'lang_Estonian' => 'Estonian',
'marked_credit_as_paid' => 'Guthaben erfolgreich als bezahlt markiert',
'marked_credits_as_paid' => 'Erfolgreich Kredite als bezahlt markiert',
'wait_for_loading' => 'Daten werden geladen - bitte warten Sie, bis der Vorgang abgeschlossen ist',
'wait_for_saving' => 'Datenspeicherung - bitte warten Sie, bis der Vorgang abgeschlossen ist',
'html_preview_warning' => 'Hinweis: Die hier vorgenommenen Änderungen werden nur in der Vorschau angezeigt, sie müssen in den obigen Registerkarten angewendet werden, um gespeichert zu werden.',
'remaining' => 'Verbleibende',
'invoice_paid' => 'Rechnung bezahlt',
'activity_120' => ':user erstellte wiederkehrende Ausgabe :recurring_expense',
'activity_121' => ':user aktualisiert wiederkehrende Ausgabe :recurring_expense',
'activity_122' => ':user archivierte wiederkehrende Ausgabe :recurring_expense',
'activity_123' => ':user löschte wiederkehrende Ausgabe :recurring_expense',
'activity_124' => ':user stellte wiederkehrende Ausgabe :recurring_expense wieder her',
'fpx' => "FPX",
); );

View File

@ -4530,6 +4530,20 @@ $LANG = array(
'html_mode_help' => 'Preview updates faster but is less accurate', 'html_mode_help' => 'Preview updates faster but is less accurate',
'status_color_theme' => 'Status Color Theme', 'status_color_theme' => 'Status Color Theme',
'load_color_theme' => 'Load Color Theme', 'load_color_theme' => 'Load Color Theme',
'lang_Estonian' => 'Estonian',
'marked_credit_as_paid' => 'Successfully marked credit as paid',
'marked_credits_as_paid' => 'Successfully marked credits as paid',
'wait_for_loading' => 'Data loading - please wait for it to complete',
'wait_for_saving' => 'Data saving - please wait for it to complete',
'html_preview_warning' => 'Note: changes made here are only previewed, they must be applied in the tabs above to be saved',
'remaining' => 'Remaining',
'invoice_paid' => 'Invoice Paid',
'activity_120' => ':user created recurring expense :recurring_expense',
'activity_121' => ':user updated recurring expense :recurring_expense',
'activity_122' => ':user archived recurring expense :recurring_expense',
'activity_123' => ':user deleted recurring expense :recurring_expense',
'activity_124' => ':user restored recurring expense :recurring_expense',
'fpx' => "FPX",
); );

View File

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'These credentials do not match our records.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
];

View File

@ -0,0 +1,13 @@
<?php
$lang = [
'client_dashboard' => 'Message to be displayed on clients dashboard',
'client_currency' => 'The client currency.',
'client_language' => 'The client language.',
'client_payment_terms' => 'The client payment terms.',
'client_paid_invoice' => 'Message to be displayed on a clients paid invoice screen',
'client_unpaid_invoice' => 'Message to be displayed on a clients unpaid invoice screen',
'client_unapproved_quote' => 'Message to be displayed on a clients unapproved quote screen',
];
return $lang;

View File

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '« Previous',
'next' => 'Next »',
];

View File

@ -0,0 +1,23 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => 'Passwords must be at least six characters and match the confirmation.',
'reset' => 'Your password has been reset!',
'sent' => 'We have e-mailed your password reset link!',
'token' => 'This password reset token is invalid.',
'user' => "We can't find a user with that e-mail address.",
'throttled' => "You have requested password reset recently, please check your email.",
];

7
resources/lang/et/t.php Normal file
View File

@ -0,0 +1,7 @@
<?php
$lang = [
'client_settings' => 'Client Settings',
];
return $lang;

4535
resources/lang/et/texts.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,146 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'The :attribute must be accepted.',
'active_url' => 'The :attribute is not a valid URL.',
'after' => 'The :attribute must be a date after :date.',
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
'alpha' => 'The :attribute may only contain letters.',
'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.',
'alpha_num' => 'The :attribute may only contain letters and numbers.',
'array' => 'The :attribute must be an array.',
'before' => 'The :attribute must be a date before :date.',
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
'between' => [
'numeric' => 'The :attribute must be between :min and :max.',
'file' => 'The :attribute must be between :min and :max kilobytes.',
'string' => 'The :attribute must be between :min and :max characters.',
'array' => 'The :attribute must have between :min and :max items.',
],
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'date' => 'The :attribute is not a valid date.',
'date_format' => 'The :attribute does not match the format :format.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
'digits_between' => 'The :attribute must be between :min and :max digits.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'email' => 'The :attribute must be a valid email address.',
'exists' => 'The selected :attribute is invalid.',
'file' => 'The :attribute must be a file.',
'filled' => 'The :attribute field must have a value.',
'gt' => [
'numeric' => 'The :attribute must be greater than :value.',
'file' => 'The :attribute must be greater than :value kilobytes.',
'string' => 'The :attribute must be greater than :value characters.',
'array' => 'The :attribute must have more than :value items.',
],
'gte' => [
'numeric' => 'The :attribute must be greater than or equal :value.',
'file' => 'The :attribute must be greater than or equal :value kilobytes.',
'string' => 'The :attribute must be greater than or equal :value characters.',
'array' => 'The :attribute must have :value items or more.',
],
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field does not exist in :other.',
'integer' => 'The :attribute must be an integer.',
'ip' => 'The :attribute must be a valid IP address.',
'ipv4' => 'The :attribute must be a valid IPv4 address.',
'ipv6' => 'The :attribute must be a valid IPv6 address.',
'json' => 'The :attribute must be a valid JSON string.',
'lt' => [
'numeric' => 'The :attribute must be less than :value.',
'file' => 'The :attribute must be less than :value kilobytes.',
'string' => 'The :attribute must be less than :value characters.',
'array' => 'The :attribute must have less than :value items.',
],
'lte' => [
'numeric' => 'The :attribute must be less than or equal :value.',
'file' => 'The :attribute must be less than or equal :value kilobytes.',
'string' => 'The :attribute must be less than or equal :value characters.',
'array' => 'The :attribute must not have more than :value items.',
],
'max' => [
'numeric' => 'The :attribute may not be greater than :max.',
'file' => 'The :attribute may not be greater than :max kilobytes.',
'string' => 'The :attribute may not be greater than :max characters.',
'array' => 'The :attribute may not have more than :max items.',
],
'mimes' => 'The :attribute must be a file of type: :values.',
'mimetypes' => 'The :attribute must be a file of type: :values.',
'min' => [
'numeric' => 'The :attribute must be at least :min.',
'file' => 'The :attribute must be at least :min kilobytes.',
'string' => 'The :attribute must be at least :min characters.',
'array' => 'The :attribute must have at least :min items.',
],
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute format is invalid.',
'numeric' => 'The :attribute must be a number.',
'present' => 'The :attribute field must be present.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values is present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute and :other must match.',
'size' => [
'numeric' => 'The :attribute must be :size.',
'file' => 'The :attribute must be :size kilobytes.',
'string' => 'The :attribute must be :size characters.',
'array' => 'The :attribute must contain :size items.',
],
'string' => 'The :attribute must be a string.',
'timezone' => 'The :attribute must be a valid zone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute format is invalid.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [],
];

View File

@ -1,6 +1,6 @@
<label class="flex items-center cursor-pointer"> <label class="flex items-center cursor-pointer">
<input type="checkbox" class="form-checkbox mr-2" <input type="checkbox" class="form-checkbox mr-2"
wire:change="updateAutoBilling" {{ $invoice->auto_bill_enabled || $invoice->auto_bill === 'optout' ? 'checked' : '' }}> wire:change="updateAutoBilling" {{ $invoice->auto_bill_enabled ? 'checked' : '' }}>
<span class="text-sm leading-5 font-medium text-gray-900"> <span class="text-sm leading-5 font-medium text-gray-900">
{{ $invoice->auto_bill_enabled || $invoice->auto_bill === 'optout' ? ctrans('texts.auto_bill_enabled') : ctrans('texts.auto_bill_disabled') }} {{ $invoice->auto_bill_enabled || $invoice->auto_bill === 'optout' ? ctrans('texts.auto_bill_enabled') : ctrans('texts.auto_bill_disabled') }}

View File

@ -0,0 +1,7 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.bank_account'), 'card_title' => ctrans('texts.bank_account')])
@section('gateway_content')
@component('portal.ninja2020.components.general.card-element-single', ['title' => ctrans('texts.bank_account'), 'show_title' => false])
{{ __('texts.sofort_authorize_label') }}
@endcomponent
@endsection

View File

@ -0,0 +1,8 @@
<div id="stripe--payment-container">
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.name')])
<label for="fpx-bank-element"></label>
<div class="border p-4 rounded">
<div id="fpx-bank-element"></div>
</div>
@endcomponent
</div>

View File

@ -0,0 +1,28 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'FPX', 'card_title' => 'FPX'])
@section('gateway_head')
<meta name="stripe-publishable-key" content="{{ $gateway->getPublishableKey() }}">
<meta name="stripe-account-id" content="{{ $gateway->company_gateway->getConfigField('account_id') }}">
<meta name="return-url" content="{{ $return_url }}">
<meta name="amount" content="{{ $stripe_amount }}">
<meta name="country" content="{{ $country }}">
<meta name="customer" content="{{ $customer }}">
<meta name="pi-client-secret" content="{{ $pi_client_secret }}">
@endsection
@section('gateway_content')
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@include('portal.ninja2020.gateways.includes.payment_details')
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
{{ ctrans('texts.fpx') }} ({{ ctrans('texts.bank_transfer') }})
@endcomponent
@include('portal.ninja2020.gateways.stripe.fpx.fpx')
@include('portal.ninja2020.gateways.includes.pay_now')
@endsection
@push('footer')
<script src="https://js.stripe.com/v3/"></script>
<script src="{{ asset('js/clients/payments/stripe-fpx.js') }}"></script>
@endpush

View File

@ -0,0 +1,125 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace Tests\Unit\Chart;
use App\Services\Chart\ChartService;
use App\Utils\Ninja;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
*/
class ChartCurrencyTest extends TestCase
{
use MockAccountData;
public function setUp() :void
{
parent::setUp();
$this->makeTestData();
}
// public function testClientServiceDataSetBuild()
// {
// $haystack = [
// [
// 'currency_id' => null,
// 'amount' => 10
// ],
// [
// 'currency_id' => 1,
// 'amount' => 11
// ],
// [
// 'currency_id' => 2,
// 'amount' => 12
// ],
// [
// 'currency_id' => 3,
// 'amount' => 13
// ],
// ];
// $cs = new ChartService($this->company);
// nlog($cs->totals(now()->subYears(10), now()));
// $this->assertTrue(is_array($cs->totals(now()->subYears(10), now())));
// }
// /* coalesces the company currency with the null currencies */
// public function testFindNullValueinArray()
// {
// $haystack = [
// [
// 'currency_id' => null,
// 'amount' => 10
// ],
// [
// 'currency_id' => 1,
// 'amount' => 11
// ],
// [
// 'currency_id' => 2,
// 'amount' => 12
// ],
// [
// 'currency_id' => 3,
// 'amount' => 13
// ],
// ];
// $company_currency_id = 1;
// $c_key = array_search($company_currency_id , array_column($haystack, 'currency_id'));
// $this->assertNotEquals($c_key, 2);
// $this->assertEquals($c_key, 1);
// $key = array_search(null , array_column($haystack, 'currency_id'));
// $this->assertNotEquals($key, 39);
// $this->assertEquals($key, 0);
// $null_currency_amount = $haystack[$key]['amount'];
// unset($haystack[$key]);
// $haystack[$c_key]['amount'] += $null_currency_amount;
// $this->assertEquals($haystack[$c_key]['amount'], 21);
// }
public function testCollectionMerging()
{
$currencies = collect([1,2,3,4,5,6]);
$expense_currencies = collect([4,5,6,7,8]);
$currencies = $currencies->merge($expense_currencies);
$this->assertEquals($currencies->count(), 11);
$currencies = $currencies->unique();
$this->assertEquals($currencies->count(), 8);
}
}

2
webpack.mix.js vendored
View File

@ -150,6 +150,8 @@ mix.js("resources/js/app.js", "public/js")
"resources/js/clients/payments/stripe-browserpay.js", "resources/js/clients/payments/stripe-browserpay.js",
"public/js/clients/payments/stripe-browserpay.js" "public/js/clients/payments/stripe-browserpay.js"
) )
.js("resources/js/clients/payments/stripe-fpx.js",
"public/js/clients/payments/stripe-fpx.js")
mix.copyDirectory('node_modules/card-js/card-js.min.css', 'public/css/card-js.min.css'); mix.copyDirectory('node_modules/card-js/card-js.min.css', 'public/css/card-js.min.css');