mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-07 13:14:30 -04:00
commit
0e89efa331
@ -5,6 +5,7 @@
|
||||

|
||||

|
||||
[](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
|
||||
|
||||
|
@ -1 +1 @@
|
||||
5.3.48
|
||||
5.3.49
|
@ -190,9 +190,7 @@ abstract class QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
$this->builder->where('client_id', $this->decodePrimaryKey($client_id));
|
||||
|
||||
return $this->builder;
|
||||
return $this->builder->where('client_id', $this->decodePrimaryKey($client_id));
|
||||
|
||||
}
|
||||
|
||||
|
76
app/Http/Controllers/ChartController.php
Normal file
76
app/Http/Controllers/ChartController.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -119,6 +119,9 @@ class PaymentController extends Controller
|
||||
} else {
|
||||
$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->saveQuietly();
|
||||
$payment->currency_id = $payment->client->getSetting('currency_id');
|
||||
$payment->saveQuietly();
|
||||
|
||||
$payment_hash->payment_id = $payment->id;
|
||||
|
@ -357,6 +357,57 @@ class DesignController extends BaseController
|
||||
$design->fill($request->all());
|
||||
$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"> </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"> </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());
|
||||
}
|
||||
|
||||
|
34
app/Http/Requests/Chart/ShowChartRequest.php
Normal file
34
app/Http/Requests/Chart/ShowChartRequest.php
Normal 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 [
|
||||
];
|
||||
}
|
||||
}
|
@ -240,6 +240,9 @@ class PaymentEmailEngine extends BaseEmailEngine
|
||||
$data['$invoices'] = ['value' => $this->formatInvoices(), '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.po_number'] = ['value' => $this->formatPoNumber(), 'label' => ctrans('texts.po_number')];
|
||||
$data['$poNumber'] = &$data['$invoice.po_number'];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@ -253,6 +256,16 @@ class PaymentEmailEngine extends BaseEmailEngine
|
||||
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()
|
||||
{
|
||||
$invoice_list = '<br><br>';
|
||||
|
@ -109,7 +109,7 @@ class TemplateEmail extends Mailable
|
||||
'settings' => $settings,
|
||||
'company' => $company,
|
||||
'whitelabel' => $this->client->user->account->isPaid() ? true : false,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'logo' => $this->company->present()->logo($settings),
|
||||
])
|
||||
->withSwiftMessage(function ($message) use($company){
|
||||
$message->getHeaders()->addTextHeader('Tag', $company->company_key);
|
||||
|
@ -114,7 +114,8 @@ class Gateway extends StaticModel
|
||||
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::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:
|
||||
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout
|
||||
break;
|
||||
|
@ -38,6 +38,7 @@ class GatewayType extends StaticModel
|
||||
const ACSS = 19;
|
||||
const BECS = 20;
|
||||
const INSTANT_BANK_PAY = 21;
|
||||
const FPX = 22;
|
||||
|
||||
public function gateway()
|
||||
{
|
||||
@ -92,6 +93,8 @@ class GatewayType extends StaticModel
|
||||
return ctrans('texts.payment_type_direct_debit');
|
||||
case self::INSTANT_BANK_PAY:
|
||||
return ctrans('texts.payment_type_instant_bank_pay');
|
||||
case self::FPX:
|
||||
return ctrans('texts.fpx');
|
||||
default:
|
||||
return 'Undefined.';
|
||||
break;
|
||||
|
@ -54,6 +54,7 @@ class PaymentType extends StaticModel
|
||||
const BECS = 43;
|
||||
const ACSS = 44;
|
||||
const INSTANT_BANK_PAY = 45;
|
||||
const FPX = 46;
|
||||
|
||||
public static function parseCardType($cardName)
|
||||
{
|
||||
|
141
app/PaymentDrivers/Stripe/FPX.php
Normal file
141
app/PaymentDrivers/Stripe/FPX.php
Normal 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);
|
||||
}
|
||||
}
|
@ -41,6 +41,7 @@ use App\PaymentDrivers\Stripe\Bancontact;
|
||||
use App\PaymentDrivers\Stripe\BECS;
|
||||
use App\PaymentDrivers\Stripe\ACSS;
|
||||
use App\PaymentDrivers\Stripe\BrowserPay;
|
||||
use App\PaymentDrivers\Stripe\FPX;
|
||||
use App\PaymentDrivers\Stripe\UpdatePaymentMethods;
|
||||
use App\PaymentDrivers\Stripe\Utilities;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
@ -198,6 +199,13 @@ class StripePaymentDriver extends BaseDriver
|
||||
&& in_array($this->client->country->iso_3166_3, ["AUT"]))
|
||||
$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
|
||||
&& $this->client->currency()
|
||||
&& ($this->client->currency()->code == 'EUR')
|
||||
@ -266,6 +274,8 @@ class StripePaymentDriver extends BaseDriver
|
||||
return 'gateways.stripe.becs';
|
||||
case GatewayType::ACSS:
|
||||
return 'gateways.stripe.acss';
|
||||
case GatewayType::FPX:
|
||||
return 'gateways.stripe.fpx';
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -106,6 +106,7 @@ class PaymentRepository extends BaseRepository {
|
||||
$payment->fill($data);
|
||||
$payment->is_manual = true;
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
|
||||
$payment->save();
|
||||
|
||||
/*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_currency_id = $client_currency;
|
||||
$payment->exchange_currency_id = $company_currency;
|
||||
$payment->currency_id = $client_currency;
|
||||
|
||||
}
|
||||
|
||||
|
80
app/Services/Chart/ChartQueries.php
Normal file
80
app/Services/Chart/ChartQueries.php
Normal 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] );
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
197
app/Services/Chart/ChartService.php
Normal file
197
app/Services/Chart/ChartService.php
Normal 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 '';
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
namespace App\Services\Credit;
|
||||
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Jobs\Entity\CreateEntityPdf;
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Payment;
|
||||
@ -98,6 +99,8 @@ class CreditService
|
||||
if($this->credit->balance > 0)
|
||||
return $this;
|
||||
|
||||
$this->markSent();
|
||||
|
||||
$payment_repo = new PaymentRepository(new CreditRepository());
|
||||
|
||||
//set credit balance to zero
|
||||
@ -116,6 +119,7 @@ class CreditService
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->type_id = PaymentType::CREDIT;
|
||||
$payment->is_manual = true;
|
||||
$payment->currency_id = $this->credit->client->getSetting('currency_id');
|
||||
$payment->date = now();
|
||||
|
||||
$payment->saveQuietly();
|
||||
@ -132,6 +136,7 @@ class CreditService
|
||||
->client
|
||||
->service()
|
||||
->updatePaidToDate($adjustment)
|
||||
->setStatus(Credit::STATUS_APPLIED)
|
||||
->save();
|
||||
|
||||
event('eloquent.created: App\Models\Payment', $payment);
|
||||
@ -176,6 +181,38 @@ class CreditService
|
||||
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()
|
||||
{
|
||||
$settings = $this->credit->client->getMergedSettings();
|
||||
|
@ -42,7 +42,7 @@ class MarkSent
|
||||
->setStatus(Credit::STATUS_SENT)
|
||||
->applyNumber()
|
||||
->adjustBalance($this->credit->amount)
|
||||
->deletePdf()
|
||||
->touchPdf()
|
||||
->save();
|
||||
|
||||
event(new CreditWasMarkedSent($this->credit, $this->credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
@ -286,12 +286,33 @@ class Design extends BaseDesign
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
['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']],
|
||||
]],
|
||||
];
|
||||
|
||||
$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 [
|
||||
['element' => 'thead', 'elements' => $thead],
|
||||
['element' => 'tbody', 'elements' => $this->buildTableBody(self::DELIVERY_NOTE)],
|
||||
];
|
||||
}
|
||||
@ -528,6 +549,16 @@ class Design extends BaseDesign
|
||||
}
|
||||
|
||||
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) {
|
||||
$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.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;
|
||||
}
|
||||
|
||||
|
@ -322,6 +322,7 @@ class HtmlEngine
|
||||
$data['$client.address'] = &$data['$client_address'];
|
||||
$data['$client.postal_code'] = ['value' => $this->client->postal_code ?: ' ', 'label' => ctrans('texts.postal_code')];
|
||||
$data['$client.city'] = ['value' => $this->client->city ?: ' ', 'label' => ctrans('texts.city')];
|
||||
$data['$client.state'] = ['value' => $this->client->state ?: ' ', 'label' => ctrans('texts.state')];
|
||||
$data['$client.id_number'] = &$data['$id_number'];
|
||||
$data['$client.vat_number'] = &$data['$vat_number'];
|
||||
$data['$client.website'] = &$data['$website'];
|
||||
@ -333,6 +334,22 @@ class HtmlEngine
|
||||
$data['$client.country'] = &$data['$country'];
|
||||
$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() ?: ' ', 'label' => ctrans('texts.shipping_address')];
|
||||
$data['$client.shipping_address1'] = ['value' => $this->client->shipping_address1 ?: ' ', 'label' => ctrans('texts.shipping_address1')];
|
||||
$data['$client.shipping_address2'] = ['value' => $this->client->shipping_address2 ?: ' ', 'label' => ctrans('texts.shipping_address2')];
|
||||
$data['$client.shipping_city'] = ['value' => $this->client->shipping_city ?: ' ', 'label' => ctrans('texts.shipping_city')];
|
||||
$data['$client.shipping_state'] = ['value' => $this->client->shipping_state ?: ' ', 'label' => ctrans('texts.shipping_state')];
|
||||
$data['$client.shipping_postal_code'] = ['value' => $this->client->shipping_postal_code ?: ' ', '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.balance'] = ['value' => Number::formatMoney($this->client->balance, $this->client), 'label' => ctrans('texts.account_balance')];
|
||||
|
@ -532,42 +532,46 @@ trait GeneratesCounter
|
||||
|
||||
switch ($reset_counter_frequency) {
|
||||
case RecurringInvoice::FREQUENCY_DAILY:
|
||||
now()->addDay();
|
||||
$new_reset_date = $reset_date->addDay();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_WEEKLY:
|
||||
now()->addWeek();
|
||||
$new_reset_date = $reset_date->addWeek();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
|
||||
now()->addWeeks(2);
|
||||
$new_reset_date = $reset_date->addWeeks(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
|
||||
now()->addWeeks(4);
|
||||
$new_reset_date = $reset_date->addWeeks(4);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_MONTHLY:
|
||||
now()->addMonth();
|
||||
$new_reset_date = $reset_date->addMonth();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
|
||||
now()->addMonths(2);
|
||||
$new_reset_date = $reset_date->addMonths(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
|
||||
now()->addMonths(3);
|
||||
$new_reset_date = $reset_date->addMonths(3);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
|
||||
now()->addMonths(4);
|
||||
$new_reset_date = $reset_date->addMonths(4);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
|
||||
now()->addMonths(6);
|
||||
$new_reset_date = $reset_date->addMonths(6);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_ANNUALLY:
|
||||
now()->addYear();
|
||||
$new_reset_date = $reset_date->addYear();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_YEARS:
|
||||
now()->addYears(2);
|
||||
$new_reset_date = $reset_date->addYears(2);
|
||||
break;
|
||||
|
||||
default:
|
||||
$new_reset_date = $reset_date->addYear();
|
||||
break;
|
||||
}
|
||||
|
||||
$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->quote_number_counter = 1;
|
||||
$settings->credit_number_counter = 1;
|
||||
|
689
composer.lock
generated
689
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,5 @@
|
||||
<?php
|
||||
|
||||
use App\Utils\Ninja;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
@ -96,7 +94,7 @@ return [
|
||||
'prefix_indexes' => true,
|
||||
'strict' => env('DB_STRICT', false),
|
||||
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
|
||||
// 'options' => Ninja::isHosted() ? config('ninja.db_options') : [],
|
||||
'options' => [],
|
||||
// 'options' => [
|
||||
// PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
|
||||
// PDO::MYSQL_ATTR_SSL_KEY => env("DB_CLIENT_KEY", ''),
|
||||
@ -118,7 +116,7 @@ return [
|
||||
'prefix_indexes' => true,
|
||||
'strict' => env('DB_STRICT', false),
|
||||
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
|
||||
// 'options' => Ninja::isHosted() ? config('ninja.db_options') : [],
|
||||
'options' => [],
|
||||
],
|
||||
|
||||
'db-ninja-02' => [
|
||||
@ -134,7 +132,7 @@ return [
|
||||
'prefix_indexes' => true,
|
||||
'strict' => env('DB_STRICT', false),
|
||||
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
|
||||
// 'options' => Ninja::isHosted() ? config('ninja.db_options') : [],
|
||||
'options' => [],
|
||||
],
|
||||
|
||||
'db-ninja-02a' => [
|
||||
@ -150,7 +148,7 @@ return [
|
||||
'prefix_indexes' => true,
|
||||
'strict' => env('DB_STRICT', false),
|
||||
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
|
||||
// 'options' => Ninja::isHosted() ? config('ninja.db_options') : [],
|
||||
'options' => [],
|
||||
],
|
||||
],
|
||||
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.3.48',
|
||||
'app_tag' => '5.3.48',
|
||||
'app_version' => '5.3.49',
|
||||
'app_tag' => '5.3.49',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
@ -59,12 +59,12 @@ return [
|
||||
'default' => env('DB_CONNECTION', 'mysql'),
|
||||
],
|
||||
|
||||
'db_options' => [
|
||||
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
|
||||
PDO::MYSQL_ATTR_SSL_KEY => env("DB_CLIENT_KEY", ''),
|
||||
PDO::MYSQL_ATTR_SSL_CERT => env("DB_CLIENT_CERT", ''),
|
||||
PDO::MYSQL_ATTR_SSL_CA => env("DB_CA_CERT", ''),
|
||||
],
|
||||
// 'db_options' => [
|
||||
// PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
|
||||
// PDO::MYSQL_ATTR_SSL_KEY => env("DB_CLIENT_KEY", ''),
|
||||
// PDO::MYSQL_ATTR_SSL_CERT => env("DB_CLIENT_CERT", ''),
|
||||
// PDO::MYSQL_ATTR_SSL_CA => env("DB_CA_CERT", ''),
|
||||
// ],
|
||||
|
||||
'i18n' => [
|
||||
'timezone_id' => env('DEFAULT_TIMEZONE', 1),
|
||||
|
@ -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()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
52
database/migrations/2022_01_19_232436_add_kyd_currency.php
Normal file
52
database/migrations/2022_01_19_232436_add_kyd_currency.php
Normal 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()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
6
public/flutter_service_worker.js
vendored
6
public/flutter_service_worker.js
vendored
@ -30,11 +30,11 @@ const RESOURCES = {
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98",
|
||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
|
||||
"/": "ce9704a467c22d129715ff0c8be6d2c2",
|
||||
"version.json": "58bf33a34913cc76f45bb675b8348805",
|
||||
"/": "854475c3df4e34c07a33807e1806745b",
|
||||
"version.json": "b685694a4de24b3b41bd9b5e756ba0fc",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"main.dart.js": "64f0368dd9c2bd830002aa4303c0daee",
|
||||
"main.dart.js": "7dfd02ea7b43f28d6b087971fe418e99",
|
||||
"canvaskit/profiling/canvaskit.js": "3783918f48ef691e230156c251169480",
|
||||
"canvaskit/profiling/canvaskit.wasm": "6d1b0fc1ec88c3110db88caa3393c580",
|
||||
"canvaskit/canvaskit.js": "62b9906717d7215a6ff4cc24efbd1b5c",
|
||||
|
2
public/js/clients/payments/stripe-fpx.js
vendored
Normal file
2
public/js/clients/payments/stripe-fpx.js
vendored
Normal 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()})();
|
9
public/js/clients/payments/stripe-fpx.js.LICENSE.txt
Normal file
9
public/js/clients/payments/stripe-fpx.js.LICENSE.txt
Normal 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
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
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
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
81427
public/main.next.dart.js
vendored
File diff suppressed because one or more lines are too long
12910
public/main.profile.dart.js
vendored
12910
public/main.profile.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -37,6 +37,7 @@
|
||||
"/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-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/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad"
|
||||
}
|
||||
|
@ -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"}
|
65
resources/js/clients/payments/stripe-fpx.js
vendored
Normal file
65
resources/js/clients/payments/stripe-fpx.js
vendored
Normal 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();
|
@ -90,7 +90,7 @@ $LANG = array(
|
||||
'upload' => 'Hochladen',
|
||||
'import' => 'Importieren',
|
||||
'download' => 'Herunterladen',
|
||||
'cancel' => 'Abbrechen',
|
||||
'cancel' => 'Stornieren',
|
||||
'close' => 'Schließen',
|
||||
'provide_email' => 'Bitte geben Sie eine gültige E-Mail-Adresse an',
|
||||
'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',
|
||||
'send_email_to_client' => 'E-Mail an den Kunden senden',
|
||||
'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_british_pound' => 'Britische Pfund',
|
||||
@ -3937,7 +3937,7 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
|
||||
'next_step' => 'Nächster Schritt',
|
||||
'notification_credit_sent_subject' => 'Rechnung :invoice wurde an Kunde gesendet.',
|
||||
'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.',
|
||||
'reset_password_text' => 'Bitte geben Sie ihre E-Mail-Adresse an, um das Passwort zurücksetzen zu können.',
|
||||
'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',
|
||||
'notification_payment_paid_subject' => 'Neue Zahlung 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_partial_payment_paid' => 'Eine Teilzahlung in Höhe von :Betrag wurde vom Kunden :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 :amount wurde vom Kunden :client auf :invoice geleistet',
|
||||
'notification_bot' => 'Benachrichtigungs-Bot',
|
||||
'invoice_number_placeholder' => 'Rechnung # :invoice',
|
||||
'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_shipping_address' => 'Bitte geben Sie Ihr Lieferadresse an',
|
||||
'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.',
|
||||
'list_of_recurring_invoices' => 'Liste der wiederkehrende Rechnungen',
|
||||
'details_of_recurring_invoice' => 'Details über wiederkehrende Rechnung',
|
||||
'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_pending' => 'Kündigung in Bearbeitung! Wir melden uns bei Ihnen...',
|
||||
'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_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_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',
|
||||
'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...',
|
||||
@ -4096,77 +4096,77 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
|
||||
'new_card' => 'Neue Kreditkarte',
|
||||
'new_bank_account' => 'Bankverbindung hinzufügen',
|
||||
'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_not_found' => 'Gutschrift nicht gefunden',
|
||||
'invoices_dont_match_client' => 'Die ausgewählten Rechnungen stammen von mehr als einem Kunden',
|
||||
'duplicate_credits_submitted' => 'Duplicate credits submitted.',
|
||||
'duplicate_invoices_submitted' => 'Duplicate invoices submitted.',
|
||||
'credit_with_no_invoice' => 'You must have an invoice set when using a credit in a payment',
|
||||
'duplicate_credits_submitted' => 'Doppelte Zahlung eingereicht',
|
||||
'duplicate_invoices_submitted' => 'Doppelte Rechnung',
|
||||
'credit_with_no_invoice' => 'Bei der Verwendung eines Kredits in einer Zahlung muss eine Rechnung eingestellt sein.',
|
||||
'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.',
|
||||
'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',
|
||||
'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',
|
||||
'refund_without_invoices' => 'Attempting to refund a payment with invoices attached, please specify valid invoice/s to be refunded.',
|
||||
'refund_without_credits' => 'Attempting to refund a payment with credits attached, please specify valid credits/s to be refunded.',
|
||||
'max_refundable_credit' => 'Attempting to refund more than allowed for credit :credit, maximum refundable amount is :amount',
|
||||
'project_client_do_not_match' => 'Project client does not match entity client',
|
||||
'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' => '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' => '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' => 'Versuch, mehr zu erstatten, als für die Gutschrift zugelassen ist :credit, maximal erstattungsfähiger Betrag ist :amount',
|
||||
'project_client_do_not_match' => 'Projektkunde stimmt nicht mit Entitätskunde überein',
|
||||
'quote_number_taken' => 'Angebotsnummer bereits in Verwendung',
|
||||
'recurring_invoice_number_taken' => 'Recurring Invoice number :number already taken',
|
||||
'user_not_associated_with_account' => 'User not associated with this account',
|
||||
'amounts_do_not_balance' => 'Amounts do not balance correctly.',
|
||||
'insufficient_applied_amount_remaining' => 'Insufficient applied amount remaining to cover payment.',
|
||||
'insufficient_credit_balance' => 'Insufficient balance on credit.',
|
||||
'one_or_more_invoices_paid' => 'One or more of these invoices have been paid',
|
||||
'recurring_invoice_number_taken' => 'Wiederkehrende Rechnungsnummer :number bereits vergeben',
|
||||
'user_not_associated_with_account' => 'Kein mit diesem Konto verbundener Benutzer',
|
||||
'amounts_do_not_balance' => 'Die Beträge sind nicht korrekt ausgeglichen.',
|
||||
'insufficient_applied_amount_remaining' => 'Der angewandte Betrag reicht nicht aus, um die Zahlung zu decken.',
|
||||
'insufficient_credit_balance' => 'Unzureichendes Guthaben auf dem Kredit.',
|
||||
'one_or_more_invoices_paid' => 'Eine oder mehrere dieser Rechnungen wurden bereits bezahlt',
|
||||
'invoice_cannot_be_refunded' => 'Rechnung :number kann nicht erstattet werden',
|
||||
'attempted_refund_failed' => 'Attempting to refund :amount only :refundable_amount available for refund',
|
||||
'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?',
|
||||
'attempted_refund_failed' => 'Erstattungsversuch :amount nur :refundable_amount für Erstattung verfügbar',
|
||||
'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_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!',
|
||||
'large_account_update_parameter' => 'Cannot load a large account without a updated_at parameter',
|
||||
'no_backup_exists' => 'No backup exists for this activity',
|
||||
'company_user_not_found' => 'Company User record not found',
|
||||
'large_account_update_parameter' => 'Kann ein großes Konto ohne den Parameter updated_at nicht laden',
|
||||
'no_backup_exists' => 'Für diese Aktivität ist keine Sicherung vorhanden',
|
||||
'company_user_not_found' => 'Firma Benutzerdatensatz nicht 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_group_settings_found' => 'No group settings found',
|
||||
'access_denied' => 'Insufficient privileges to access/modify this resource',
|
||||
'no_group_settings_found' => 'Keine Gruppeneinstellungen gefunden',
|
||||
'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_license_or_environment' => 'Invalid license, or invalid environment :environment',
|
||||
'route_not_available' => 'Route not available',
|
||||
'invalid_design_object' => 'Invalid custom design object',
|
||||
'invoice_license_or_environment' => 'Ungültige Lizenz, oder ungültige Umgebung :environment',
|
||||
'route_not_available' => 'Pfad nicht verfügbar',
|
||||
'invalid_design_object' => 'Ungültiges benutzerdefiniertes Entwurfsobjekt',
|
||||
'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_never_run' => 'Aufgabenplaner lief noch nie',
|
||||
'self_update_not_available' => 'Integrierter Updater auf diesem System nicht verfügbar.',
|
||||
'user_detached' => 'Nutzer wurden vom Unternehmen entkoppelt',
|
||||
'create_webhook_failure' => 'Webhook konnte nicht erstellt werden',
|
||||
'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.',
|
||||
'payment_token_not_found' => 'Payment token not found, please try again. If an issue still persist, try with another payment method',
|
||||
'vendor_address1' => 'Vendor Street',
|
||||
'vendor_address2' => 'Vendor Apt/Suite',
|
||||
'partially_unapplied' => 'Partially Unapplied',
|
||||
'select_a_gmail_user' => 'Please select a user authenticated with Gmail',
|
||||
'list_long_press' => 'List Long Press',
|
||||
'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' => 'Zahlungstoken nicht gefunden, bitte versuchen Sie es erneut. Wenn das Problem weiterhin besteht, versuchen Sie es mit einer anderen Zahlungsmethode',
|
||||
'vendor_address1' => 'Straße Lieferant',
|
||||
'vendor_address2' => 'Lieferant Apt/Suite',
|
||||
'partially_unapplied' => 'Teilweise unangewandt',
|
||||
'select_a_gmail_user' => 'Bitte wählen Sie einen mit Gmail authentifizierten Benutzer',
|
||||
'list_long_press' => 'Liste Langes Drücken',
|
||||
'show_actions' => 'Zeige Aktionen',
|
||||
'start_multiselect' => 'Mehrfachauswahl',
|
||||
'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_credit_balance' => 'Converted Credit Balance',
|
||||
'converted_total' => 'Converted Total',
|
||||
'converted_paid_to_date' => 'Umgewandelt Bezahlt bis Datum',
|
||||
'converted_credit_balance' => 'Umgerechneter Guthabenbetrag',
|
||||
'converted_total' => 'Umgerechnet Total',
|
||||
'reply_to_name' => 'Name der Antwortadresse',
|
||||
'payment_status_-2' => 'Teilweise nicht angewendet',
|
||||
'color_theme' => 'Farbthema',
|
||||
'start_migration' => 'Beginne mit der Migration.',
|
||||
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact',
|
||||
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice',
|
||||
'recurring_cancellation_request' => 'Antrag auf Stornierung wiederkehrender Rechnungen von :contact',
|
||||
'recurring_cancellation_request_body' => ':contact vom Kunden :client bittet um Stornierung der wiederkehrenden Rechnung :invoice',
|
||||
'hello' => 'Hallo',
|
||||
'group_documents' => 'Gruppendokumente',
|
||||
'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',
|
||||
'accounting' => 'Buchhaltung',
|
||||
'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',
|
||||
'migration_api_secret_notice' => 'You can find API_SECRET in the .env file or Invoice Ninja v5. If property is missing, leave field blank.',
|
||||
'billing_coupon_notice' => 'Your discount will be applied on the checkout.',
|
||||
'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' => 'Ihr Rabatt wird an der Kasse abgezogen.',
|
||||
'use_last_email' => 'Vorherige E-Mail benutzen',
|
||||
'activate_company' => 'Unternehmen aktivieren',
|
||||
'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',
|
||||
'disable_two_factor' => 'Zwei-Faktor-Authentifizierung deaktivieren',
|
||||
'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',
|
||||
'recurring_invoice_issued_to' => 'Wiederkehrende Rechnung ausgestellt an',
|
||||
'subscription' => 'Abonnement',
|
||||
@ -4222,140 +4222,330 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
|
||||
'deleted_subscription' => 'Abonnement gelöscht',
|
||||
'removed_subscription' => 'Abonnement entfernt',
|
||||
'restored_subscription' => 'Abonnement wiederhergestellt',
|
||||
'search_subscription' => 'Search 1 Subscription',
|
||||
'search_subscription' => 'Suchen Sie 1 Abonnement',
|
||||
'search_subscriptions' => ':count Abonnements durchsuchen',
|
||||
'subdomain_is_not_available' => 'Subdomain ist nicht verfügbar',
|
||||
'connect_gmail' => 'Mit Gmail verbinden',
|
||||
'disconnect_gmail' => 'von Gmail trennen',
|
||||
'connected_gmail' => 'Mit Gmail erfolgreich verbunden',
|
||||
'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',
|
||||
'count_minutes' => ':count Minuten',
|
||||
'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_81' => ':user hat Abonnement :subscription geändert',
|
||||
'activity_82' => ':user hat Abonnement :subscription archiviert',
|
||||
'activity_83' => ':user hat Abonnement :subscription gelöscht',
|
||||
'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',
|
||||
|
||||
'notification_invoice_created_body' => 'The following invoice :invoice was created for client :client for :amount.',
|
||||
'notification_invoice_created_subject' => 'Invoice :invoice was created for :client',
|
||||
'notification_quote_created_body' => 'The following quote :invoice was created for client :client for :amount.',
|
||||
'notification_quote_created_subject' => 'Quote :invoice was created for :client',
|
||||
'notification_credit_created_body' => 'The following credit :invoice was created for client :client for :amount.',
|
||||
'notification_credit_created_subject' => 'Credit :invoice was created for :client',
|
||||
'max_companies' => 'Maximum companies migrated',
|
||||
'max_companies_desc' => 'You have reached your maximum number of companies. Delete existing companies to migrate new ones.',
|
||||
'migration_already_completed' => 'Company already migrated',
|
||||
'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.',
|
||||
'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.',
|
||||
'new_account' => 'New account',
|
||||
'activity_100' => ':user created recurring invoice :recurring_invoice',
|
||||
'activity_101' => ':user updated recurring invoice :recurring_invoice',
|
||||
'activity_102' => ':user archived recurring invoice :recurring_invoice',
|
||||
'activity_103' => ':user deleted recurring invoice :recurring_invoice',
|
||||
'activity_104' => ':user restored recurring invoice :recurring_invoice',
|
||||
'new_login_detected' => 'New login detected for your account.',
|
||||
'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',
|
||||
'download_backup_subject' => 'Your company backup is ready for download',
|
||||
'contact_details' => 'Contact Details',
|
||||
'download_backup_subject' => 'Your company backup is ready for download',
|
||||
'account_passwordless_login' => 'Account passwordless login',
|
||||
'user_duplicate_error' => 'Cannot add the same user to the same company',
|
||||
'user_cross_linked_error' => 'User exists but cannot be crossed linked to multiple accounts',
|
||||
'ach_verification_notification_label' => 'ACH verification',
|
||||
'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.',
|
||||
'notification_invoice_created_body' => 'Die Rechnung :invoice für den Kunden :client mit dem Betrag :amount wurde erstellt',
|
||||
'notification_invoice_created_subject' => 'Rechnung :invoice für :client erstellt',
|
||||
'notification_quote_created_body' => 'Das folgende Angebot :invoice wurde für den Kunde :client für :amount erstellt.',
|
||||
'notification_quote_created_subject' => 'Angebot :invoice für den Kunden :client wurde erstellt',
|
||||
'notification_credit_created_body' => 'Die Gutschrift :invoice für den Kunden :client über :amount wurde erstellt.',
|
||||
'notification_credit_created_subject' => 'Gutschrift :invoice für :client erstellt',
|
||||
'max_companies' => 'Maximale Anzahl an Firmen erreicht',
|
||||
'max_companies_desc' => 'Sie haben Ihre maximale Anzahl an Unternehmen erreicht. Löschen Sie vorhandene Unternehmen, um neue Firmen einzufügen.',
|
||||
'migration_already_completed' => 'Firma existiert bereits',
|
||||
'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' => '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' => 'Neues Konto',
|
||||
'activity_100' => ':user hat die wiederkehrende Rechnung :recurring_invoice erstellt.',
|
||||
'activity_101' => ':user hat die wiederkehrende Rechnung :recurring_invoice aktuallisiert',
|
||||
'activity_102' => ':user hat die wiederkehrende Rechnung :recurring_invoice archiviert',
|
||||
'activity_103' => ':user hat die wiederkehrende Rechnung :recurring_invoice gelöscht.',
|
||||
'activity_104' => ':user hat die wiederkehrende Rechnung :recurring_invoice wiederhergestellt.',
|
||||
'new_login_detected' => 'Neue Anmeldung für Ihr Konto erkannt.',
|
||||
'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' => 'Ihr Firmen-Backup steht zum Download bereit',
|
||||
'contact_details' => 'Kontakt Informationen',
|
||||
'download_backup_subject' => 'Ihr Firmen-Backup steht zum Download bereit',
|
||||
'account_passwordless_login' => 'Passwortfreies einloggen',
|
||||
'user_duplicate_error' => 'Derselbe Benutzer kann nicht derselben Firma hinzugefügt werden',
|
||||
'user_cross_linked_error' => 'Der Benutzer ist vorhanden, kann aber nicht mit mehreren Konten verknüpft werden',
|
||||
'ach_verification_notification_label' => 'ACH-Verifizierung',
|
||||
'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' => 'There was a request to login using link. If you did not request this, it\'s safe to ignore it.',
|
||||
'invoices_backup_subject' => 'Your invoices are ready for download',
|
||||
'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' => 'Ihre Rechnungen stehen zum Download bereit',
|
||||
'migration_failed_label' => 'Update der Datenbank-Struktur fehlgeschlagen',
|
||||
'migration_failed' => 'Looks like something went wrong with the migration for the following company:',
|
||||
'client_email_company_contact_label' => 'If you have any questions please contact us, we\'re here to help!',
|
||||
'quote_was_approved_label' => 'Quote was approved',
|
||||
'quote_was_approved' => 'We would like to inform you that quote was approved.',
|
||||
'company_import_failure_subject' => 'Error importing :company',
|
||||
'company_import_failure_body' => 'There was an error importing the company data, the error message was:',
|
||||
'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' => 'Wenn Sie Fragen haben, wenden Sie sich bitte an uns, wir sind gerne für Sie da!',
|
||||
'quote_was_approved_label' => 'Das Angebot wurde genehmigt',
|
||||
'quote_was_approved' => 'Wir möchten Ihnen mitteilen, dass das Angebot angenommen wurde.',
|
||||
'company_import_failure_subject' => 'Fehler beim Importieren von :company',
|
||||
'company_import_failure_body' => 'Beim Importieren der Unternehmensdaten ist ein Fehler aufgetreten, die Fehlermeldung lautete:',
|
||||
'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' => 'Machen Sie dies zu Ihrer bevorzugten Zahlungsmethode',
|
||||
'already_default_payment_method' => 'This is your preferred way of paying.',
|
||||
'auto_bill_disabled' => 'Auto Bill Disabled',
|
||||
'already_default_payment_method' => 'Dies ist die von Ihnen bevorzugte Art der Bezahlung.',
|
||||
'auto_bill_disabled' => 'Automatische Bezahlung Deaktiviert',
|
||||
'select_payment_method' => 'Wählen Sie eine Zahlungsmethode:',
|
||||
'login_without_password' => 'Ohne Passwort anmelden',
|
||||
'email_sent' => 'Benachrichtigen, wenn eine Rechnung <strong>versendet</strong> wurde',
|
||||
'one_time_purchases' => 'Einmalzahlungen',
|
||||
'recurring_purchases' => 'Wiederkehrende Käufe',
|
||||
'you_might_be_interested_in_following' => 'You might be interested in the following',
|
||||
'quotes_with_status_sent_can_be_approved' => 'Only quotes with "Sent" status can be approved.',
|
||||
'no_quotes_available_for_download' => 'No quotes available for download.',
|
||||
'you_might_be_interested_in_following' => 'Das könnte Sie interessieren',
|
||||
'quotes_with_status_sent_can_be_approved' => 'Nur Angebote mit dem Status "Gesendet" können genehmigt werden.',
|
||||
'no_quotes_available_for_download' => 'Keine Angebote zum Herunterladen verfügbar.',
|
||||
'copyright' => 'Copyright',
|
||||
'user_created_user' => ':user created :created_user at :time',
|
||||
'company_deleted' => 'Company deleted',
|
||||
'company_deleted_body' => 'Company [ :company ] was deleted by :user',
|
||||
'back_to' => 'Back to :url',
|
||||
'stripe_connect_migration_title' => 'Connect your Stripe Account',
|
||||
'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!',
|
||||
'email_quota_exceeded_subject' => 'Account email quota exceeded.',
|
||||
'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.',
|
||||
'auto_bill_option' => 'Opt in or out of having this invoice automatically charged.',
|
||||
'user_created_user' => ':user erstellt :created_user zu :time',
|
||||
'company_deleted' => 'Unternehmen gelöscht',
|
||||
'company_deleted_body' => 'Firma [ : company ] wurde von :user gelöscht',
|
||||
'back_to' => 'Zurück zu :url',
|
||||
'stripe_connect_migration_title' => 'Verbinden Sie Ihr Stripe-Konto',
|
||||
'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' => 'E-Mail-Kontingent überschritten.',
|
||||
'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' => 'Aktivieren oder deaktivieren Sie die automatische Belastung dieser Rechnung.',
|
||||
'lang_Arabic' => 'Arabisch',
|
||||
'lang_Persian' => 'Persisch',
|
||||
'lang_Latvian' => 'Lettisch',
|
||||
'expiry_date' => 'Ablaufdatum',
|
||||
'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',
|
||||
'locality' => 'Standort',
|
||||
'checking' => 'Checking',
|
||||
'savings' => 'Savings',
|
||||
'unable_to_verify_payment_method' => 'Unable to verify payment method.',
|
||||
'generic_gateway_error' => 'Gateway configuration error. Please check your credentials.',
|
||||
'savings' => 'Ersparnisse',
|
||||
'unable_to_verify_payment_method' => 'Die Zahlungsmethode kann nicht verifiziert werden.',
|
||||
'generic_gateway_error' => 'Gateway-Konfigurationsfehler. Bitte überprüfen Sie Ihre Anmeldedaten.',
|
||||
'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',
|
||||
'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.',
|
||||
'ideal' => 'iDEAL',
|
||||
'bank_account_holder' => 'Kontoinhaber',
|
||||
'aio_checkout' => 'All-in-one checkout',
|
||||
'aio_checkout' => 'All-in-one Checkout',
|
||||
'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_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',
|
||||
'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.',
|
||||
'you_need_to_accept_the_terms_before_proceeding' => 'Sie müssen die Bedingungen akzeptieren, um fortzufahren.',
|
||||
'direct_debit' => 'Direct Debit',
|
||||
'clone_to_expense' => 'Clone to expense',
|
||||
'direct_debit' => 'Lastschriftverfahren',
|
||||
'clone_to_expense' => 'Klonen zu Ausgabe',
|
||||
'checkout' => 'Kasse',
|
||||
'acss' => 'Pre-authorized debit payments',
|
||||
'invalid_amount' => 'Invalid amount. Number/Decimal values only.',
|
||||
'client_payment_failure_body' => 'Payment for Invoice :invoice for amount :amount failed.',
|
||||
'acss' => 'Vorautorisierte Lastschriftzahlungen',
|
||||
'invalid_amount' => 'Ungültiger Betrag. Nur Zahlen/Dezimalwerte.',
|
||||
'client_payment_failure_body' => 'Zahlung für Rechnung :invoice for amount :amount fehlgeschlagen.',
|
||||
'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>',
|
||||
'gocardless_mandate_not_ready' => 'Payment mandate is not ready. Please try again later.',
|
||||
'payment_type_instant_bank_pay' => 'Instant Bank Pay',
|
||||
'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' => 'Das Zahlungsmandat ist nicht fertig. Bitte versuchen Sie es später noch einmal.',
|
||||
'payment_type_instant_bank_pay' => 'Sofortige Banküberweisung',
|
||||
'payment_type_iDEAL' => 'iDEAL',
|
||||
'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_Instant Bank Pay' => 'Instant Bank Pay',
|
||||
'payment_type_Hosted Page' => 'Hosted Page',
|
||||
'payment_type_Instant Bank Pay' => 'Sofortige Banküberweisung',
|
||||
'payment_type_Hosted Page' => 'Gehostete Seite',
|
||||
'payment_type_GiroPay' => 'GiroPay',
|
||||
'payment_type_EPS' => 'EPS',
|
||||
'payment_type_Direct Debit' => 'Direct Debit',
|
||||
'payment_type_Direct Debit' => 'Lastschriftverfahren',
|
||||
'payment_type_Bancontact' => 'Bancontact',
|
||||
'payment_type_BECS' => 'BECS',
|
||||
'payment_type_ACSS' => 'ACSS',
|
||||
'gross_line_total' => 'Gross line total',
|
||||
'lang_Slovak' => 'Slovak',
|
||||
'gross_line_total' => 'Bruttozeile gesamt',
|
||||
'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",
|
||||
|
||||
);
|
||||
|
||||
|
@ -4530,6 +4530,20 @@ $LANG = array(
|
||||
'html_mode_help' => 'Preview updates faster but is less accurate',
|
||||
'status_color_theme' => 'Status 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",
|
||||
|
||||
);
|
||||
|
||||
|
19
resources/lang/et/auth.php
Normal file
19
resources/lang/et/auth.php
Normal 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.',
|
||||
|
||||
];
|
13
resources/lang/et/help.php
Normal file
13
resources/lang/et/help.php
Normal 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;
|
19
resources/lang/et/pagination.php
Normal file
19
resources/lang/et/pagination.php
Normal 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 »',
|
||||
|
||||
];
|
23
resources/lang/et/passwords.php
Normal file
23
resources/lang/et/passwords.php
Normal 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
7
resources/lang/et/t.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
$lang = [
|
||||
'client_settings' => 'Client Settings',
|
||||
];
|
||||
|
||||
return $lang;
|
4535
resources/lang/et/texts.php
Normal file
4535
resources/lang/et/texts.php
Normal file
File diff suppressed because it is too large
Load Diff
146
resources/lang/et/validation.php
Normal file
146
resources/lang/et/validation.php
Normal 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' => [],
|
||||
|
||||
];
|
@ -1,6 +1,6 @@
|
||||
<label class="flex items-center cursor-pointer">
|
||||
<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">
|
||||
{{ $invoice->auto_bill_enabled || $invoice->auto_bill === 'optout' ? ctrans('texts.auto_bill_enabled') : ctrans('texts.auto_bill_disabled') }}
|
||||
|
@ -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
|
@ -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>
|
@ -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
|
125
tests/Unit/Chart/ChartCurrencyTest.php
Normal file
125
tests/Unit/Chart/ChartCurrencyTest.php
Normal 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
2
webpack.mix.js
vendored
@ -150,6 +150,8 @@ mix.js("resources/js/app.js", "public/js")
|
||||
"resources/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');
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user