Merge pull request #9385 from turbo124/v5-develop

Gateway authentication
This commit is contained in:
David Bomba 2024-03-17 22:37:40 +11:00 committed by GitHub
commit 94d4da8513
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 88842 additions and 140 deletions

View File

@ -499,25 +499,28 @@ class CompanySettings extends BaseSettings
public $use_unapplied_payment = 'off'; //always, option, off //@implemented
public $enable_rappen_rounding = false;
public static $casts = [
'use_unapplied_payment' => 'string',
'show_pdfhtml_on_mobile' => 'bool',
'payment_email_all_contacts' => 'bool',
'statement_design_id' => 'string',
'delivery_note_design_id' => 'string',
'payment_receipt_design_id' => 'string',
'payment_refund_design_id' => 'string',
'classification' => 'string',
'enable_e_invoice' => 'bool',
'classification' => 'string',
'default_expense_payment_type_id' => 'string',
'e_invoice_type' => 'string',
'mailgun_endpoint' => 'string',
'client_initiated_payments' => 'bool',
'client_initiated_payments_minimum' => 'float',
'sync_invoice_quote_columns' => 'bool',
'show_task_item_description' => 'bool',
'allow_billable_task_items' => 'bool',
'enable_rappen_rounding' => 'bool',
'use_unapplied_payment' => 'string',
'show_pdfhtml_on_mobile' => 'bool',
'payment_email_all_contacts' => 'bool',
'statement_design_id' => 'string',
'delivery_note_design_id' => 'string',
'payment_receipt_design_id' => 'string',
'payment_refund_design_id' => 'string',
'classification' => 'string',
'enable_e_invoice' => 'bool',
'classification' => 'string',
'default_expense_payment_type_id' => 'string',
'e_invoice_type' => 'string',
'mailgun_endpoint' => 'string',
'client_initiated_payments' => 'bool',
'client_initiated_payments_minimum' => 'float',
'sync_invoice_quote_columns' => 'bool',
'show_task_item_description' => 'bool',
'allow_billable_task_items' => 'bool',
'accept_client_input_quote_approval' => 'bool',
'custom_sending_email' => 'string',
'show_paid_stamp' => 'bool',

File diff suppressed because it is too large Load Diff

View File

@ -294,6 +294,7 @@ class BaseExport
'line_total' => 'item.line_total',
'gross_line_total' => 'item.gross_line_total',
'tax_amount' => 'item.tax_amount',
'product_cost' => 'item.product_cost'
];
protected array $quote_report_keys = [

View File

@ -58,7 +58,10 @@ class DesignFilters extends QueryFilters
public function entities(string $entities = ''): Builder
{
if(stripos($entities, 'statement') !== false)
$entities = 'client';
if (strlen($entities) == 0 || str_contains($entities, ',')) {
return $this->builder;
}

View File

@ -52,6 +52,7 @@ class InvoiceSum
public InvoiceItemSum $invoice_items;
private $rappen_rounding = false;
/**
* Constructs the object with Invoice and Settings object.
*
@ -63,8 +64,11 @@ class InvoiceSum
if ($this->invoice->client) {
$this->precision = $this->invoice->client->currency()->precision;
$this->rappen_rounding = $this->invoice->client->getSetting('enable_rappen_rounding');
} else {
$this->precision = $this->invoice->vendor->currency()->precision;
$this->rappen_rounding = $this->invoice->vendor->getSetting('enable_rappen_rounding');
}
$this->tax_map = new Collection();
@ -252,11 +256,20 @@ class InvoiceSum
/* Set new calculated total */
$this->invoice->amount = $this->formatValue($this->getTotal(), $this->precision);
if($this->rappen_rounding)
$this->invoice->amount = $this->roundRappen($this->invoice->amount);
$this->invoice->total_taxes = $this->getTotalTaxes();
return $this;
}
function roundRappen($value): float
{
return round($value / .05, 0) * .05;
}
public function getSubTotal()
{
return $this->sub_total;

View File

@ -47,6 +47,8 @@ class InvoiceSumInclusive
private $precision;
private $rappen_rounding = false;
public InvoiceItemSumInclusive $invoice_items;
/**
* Constructs the object with Invoice and Settings object.
@ -59,8 +61,10 @@ class InvoiceSumInclusive
if ($this->invoice->client) {
$this->precision = $this->invoice->client->currency()->precision;
$this->rappen_rounding = $this->invoice->client->getSetting('enable_rappen_rounding');
} else {
$this->precision = $this->invoice->vendor->currency()->precision;
$this->rappen_rounding = $this->invoice->vendor->getSetting('enable_rappen_rounding');
}
$this->tax_map = new Collection();
@ -268,12 +272,22 @@ class InvoiceSumInclusive
}
/* Set new calculated total */
/** @todo - rappen rounding here */
$this->invoice->amount = $this->formatValue($this->getTotal(), $this->precision);
if($this->rappen_rounding) {
$this->invoice->amount = $this->roundRappen($this->invoice->amount);
}
$this->invoice->total_taxes = $this->getTotalTaxes();
return $this;
}
function roundRappen($value): float
{
return round($value / .05, 0) * .05;
}
public function getSubTotal()
{

View File

@ -20,6 +20,7 @@ use App\Http\Requests\CompanyGateway\DestroyCompanyGatewayRequest;
use App\Http\Requests\CompanyGateway\EditCompanyGatewayRequest;
use App\Http\Requests\CompanyGateway\ShowCompanyGatewayRequest;
use App\Http\Requests\CompanyGateway\StoreCompanyGatewayRequest;
use App\Http\Requests\CompanyGateway\TestCompanyGatewayRequest;
use App\Http\Requests\CompanyGateway\UpdateCompanyGatewayRequest;
use App\Jobs\Util\ApplePayDomain;
use App\Models\Client;
@ -535,4 +536,12 @@ class CompanyGatewayController extends BaseController
return $this->listResponse(CompanyGateway::withTrashed()->company()->whereIn('id', $request->ids));
}
public function test(TestCompanyGatewayRequest $request, CompanyGateway $company_gateway)
{
return response()->json(['message' => $company_gateway->driver()->auth() ? 'true' : 'false'], 200);
}
}

View File

@ -63,8 +63,12 @@ class LogoutController extends BaseController
$ct->company
->tokens()
->where('is_system', true)
->forceDelete();
->cursor()
->each(function ($ct){
$ct->token = \Illuminate\Support\Str::random(64);
$ct->save();
});
return response()->json(['message' => 'All tokens deleted'], 200);
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\CompanyGateway;
use App\Http\Requests\Request;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class TestCompanyGatewayRequest extends Request
{
use MakesHash;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
/** @var \App\Models\User $user */
$user = auth()->user();
return $user->isAdmin();
}
public function rules()
{
return [
];
}
public function prepareForValidation()
{
$input = $this->all();
$this->replace($input);
}
}

View File

@ -5,7 +5,7 @@
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
*1`
* @license https://www.elastic.co/licensing/elastic-license
*/
@ -21,6 +21,7 @@ class BlackListRule implements ValidationRule
{
/** Bad domains +/- dispoable email domains */
private array $blacklist = [
'wireconnected.com',
'secure-coinspot.com',
'casasotombo.com',
'otpku.com',

View File

@ -229,8 +229,8 @@ class NinjaMailerJob implements ShouldQueue
private function incrementEmailCounter(): void
{
if (in_array($this->mailer, ['default', 'mailgun']))
Cache::increment("email_quota" . $this->company->account->key);
if(in_array($this->mailer, ['default','mailgun','postmark']))
Cache::increment("email_quota".$this->company->account->key);
}
/**

View File

@ -21,6 +21,7 @@ use Illuminate\Support\Str;
use App\Models\CompanyToken;
use App\Models\ClientContact;
use App\Models\VendorContact;
use App\DataProviders\SMSNumbers;
use Illuminate\Support\Facades\DB;
/**
@ -537,6 +538,10 @@ class MultiDB
$current_db = config('database.default');
if(SMSNumbers::hasNumber($phone)){
return true;
}
foreach (self::$dbs as $db) {
self::setDB($db);
if ($exists = Account::where('account_sms_verification_number', $phone)->where('account_sms_verified', true)->exists()) {

View File

@ -222,8 +222,9 @@ class RequiredClientInfo extends Component
$this->show_form = true;
$hash = Cache::get(request()->input('hash'));
$invoice = Invoice::find($this->decodePrimaryKey($hash['invoice_id']));
$this->invoice_terms = Invoice::find($this->decodePrimaryKey($hash['invoice_id']))->terms;
$this->invoice_terms = $invoice->terms;
}
count($this->fields) > 0 || $this->show_terms

View File

@ -102,7 +102,7 @@ class Account extends BaseModel
private $free_plan_email_quota = 20;
private $paid_plan_email_quota = 400;
private $paid_plan_email_quota = 300;
/**
* @var string
@ -503,11 +503,13 @@ class Account extends BaseModel
}
if ($this->isPaid()) {
$multiplier = $this->plan == 'enterprise' ? 2 : 1.2;
$limit = $this->paid_plan_email_quota;
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 50;
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * (20 * $multiplier);
} else {
$limit = $this->free_plan_email_quota;
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 2;
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 1.5;
}
return min($limit, 1000);

View File

@ -485,7 +485,7 @@ class Client extends BaseModel implements HasLocalePreference
}
/*Company Settings*/
elseif ((property_exists($this->company->settings, $setting) != false) && (isset($this->company->settings->{$setting}) !== false)) {
elseif ((property_exists($this->company->settings, $setting) !== false) && (isset($this->company->settings->{$setting}) !== false)) {
return $this->company->settings->{$setting};
} elseif (property_exists(CompanySettings::defaults(), $setting)) {
return CompanySettings::defaults()->{$setting};

View File

@ -195,4 +195,9 @@ class AuthorizePaymentDriver extends BaseDriver
{
return (new AuthorizeCustomer($this))->importCustomers();
}
public function auth(): bool
{
return $this->init()->getPublicClientKey() ?? false;
}
}

View File

@ -806,4 +806,9 @@ class BaseDriver extends AbstractPaymentDriver
{
return true;
}
public function auth(): bool
{
return true;
}
}

View File

@ -157,34 +157,6 @@ class BraintreePaymentDriver extends BaseDriver
}
}
// public function updateCustomer()
// {
// $customer = $this->findOrCreateCustomer();
// $result = $this->gateway->customer()->update(
// $customer->id,
// [
// 'firstName' => $this->client->present()->name(),
// 'email' => $this->client->present()->email(),
// 'phone' => $this->client->present()->phone(),
// 'creditCard' => [
// 'billingAddress' => [
// 'options' => [
// 'updateExisting' => true
// ],
// 'firstName' => $this->client->present()->first_name() ?: $this->client->present()->name(),
// 'lastName' => $this->client->present()->last_name() ?: '',
// 'streetAddress' => $this->client->address1 ?: '',
// 'extendedAddress' =>$this->client->address2 ?: '',
// 'locality' => $this->client->city ?: '',
// 'postalCode' => $this->client->postal_code ?: '',
// 'countryCodeAlpha2' => $this->client->country ? $this->client->country->iso_3166_2 : 'US',
// ],
// ],
// ]
// );
// }
public function refund(Payment $payment, $amount, $return_client_response = false)
{
$this->init();
@ -324,13 +296,21 @@ class BraintreePaymentDriver extends BaseDriver
nlog('braintree webhook');
// if($webhookNotification)
// nlog($webhookNotification->kind);
// // Example values for webhook notification properties
// $message = $webhookNotification->kind; // "subscription_went_past_due"
// $message = $webhookNotification->timestamp->format('D M j G:i:s T Y'); // "Sun Jan 1 00:00:00 UTC 2012"
return response()->json([], 200);
}
public function auth(): bool
{
try {
$ct =$this->init()->gateway->clientToken()->generate();
nlog($ct);
return true;
}
catch(\Exception $e) {
}
return false;
}
}

View File

@ -534,4 +534,16 @@ class CheckoutComPaymentDriver extends BaseDriver
{
// Gateway doesn't support this feature.
}
public function auth(): bool
{
try{
$this->init()->gateway->getCustomersClient('x');
return true;
}
catch(\Exception $e){
}
return false;
}
}

View File

@ -211,4 +211,13 @@ class EwayPaymentDriver extends BaseDriver
return $fields;
}
public function auth(): bool
{
$response =$this->init()->eway->queryTransaction('xx');
return (bool) count($response->getErrors()) == 0;
}
}

View File

@ -11,13 +11,14 @@
namespace App\PaymentDrivers;
use App\Jobs\Util\SystemLogger;
use App\Models\GatewayType;
use App\Models\Payment;
use App\Models\SystemLog;
use App\PaymentDrivers\Forte\ACH;
use App\PaymentDrivers\Forte\CreditCard;
use App\Models\GatewayType;
use App\Jobs\Util\SystemLogger;
use App\Utils\Traits\MakesHash;
use App\PaymentDrivers\Forte\ACH;
use Illuminate\Support\Facades\Http;
use App\PaymentDrivers\Forte\CreditCard;
class FortePaymentDriver extends BaseDriver
{
@ -183,6 +184,26 @@ class FortePaymentDriver extends BaseDriver
];
}
public function auth(): bool
{
$forte_base_uri = "https://sandbox.forte.net/api/v3/";
if ($this->company_gateway->getConfigField('testMode') == false) {
$forte_base_uri = "https://api.forte.net/v3/";
}
$forte_api_access_id = $this->company_gateway->getConfigField('apiAccessId');
$forte_secure_key = $this->company_gateway->getConfigField('secureKey');
$forte_auth_organization_id = $this->company_gateway->getConfigField('authOrganizationId');
$forte_organization_id = $this->company_gateway->getConfigField('organizationId');
$forte_location_id = $this->company_gateway->getConfigField('locationId');
$response = Http::withBasicAuth($forte_api_access_id, $forte_secure_key)
->withHeaders(['X-Forte-Auth-Organization-Id' => $forte_organization_id])
->get("{$forte_base_uri}/organizations/{$forte_organization_id}/locations/{$forte_location_id}/customers/");
return $response->successful();
}
// public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
// {
// return $this->payment_method->yourTokenBillingImplmentation();

View File

@ -558,4 +558,17 @@ class GoCardlessPaymentDriver extends BaseDriver
{
return render('gateways.gocardless.verification');
}
public function auth(): bool
{
try {
$customers = $this->init()->gateway->customers()->list();
return true;
}
catch(\Exception $e){
}
return false;
}
}

View File

@ -420,4 +420,20 @@ class MolliePaymentDriver extends BaseDriver
{
return \number_format((float) $amount, 2, '.', '');
}
public function auth(): bool
{
$this->init();
try {
$p = $this->gateway->payments->page();
return true;
}
catch(\Exception $e){
}
return false;
}
}

View File

@ -560,5 +560,18 @@ class PayPalPPCPPaymentDriver extends BaseDriver
PayPalWebhook::dispatch($request->all(), $request->headers->all(), $this->access_token);
}
public function auth(): bool
{
try {
$this->init()->getClientToken();
return true;
}
catch(\Exception $e) {
}
return false;
}
}

View File

@ -81,11 +81,7 @@ class PayPalRestPaymentDriver extends BaseDriver
public function init()
{
// $this->omnipay_gateway = Omnipay::create(
// $this->company_gateway->gateway->provider
// );
// $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig());
$this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com';
$secret = $this->company_gateway->getConfigField('secret');
@ -380,61 +376,6 @@ class PayPalRestPaymentDriver extends BaseDriver
return $r->json()['id'];
// $_invoice = collect($this->payment_hash->data->invoices)->first();
// $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
// $order = [
// "intent" => "CAPTURE",
// "payer" => [
// "name" => [
// "given_name" => $this->client->present()->first_name(),
// "surname" => $this->client->present()->last_name(),
// ],
// "email_address" => $this->client->present()->email(),
// "address" => [
// "address_line_1" => $this->client->address1,
// "address_line_2" => $this->client->address2,
// "admin_area_1" => $this->client->city,
// "admin_area_2" => $this->client->state,
// "postal_code" => $this->client->postal_code,
// "country_code" => $this->client->country->iso_3166_2,
// ]
// ],
// "purchase_units" => [
// [
// "description" => ctrans('texts.invoice_number').'# '.$invoice->number,
// "invoice_id" => $invoice->number,
// "amount" => [
// "value" => (string)$data['amount_with_fee'],
// "currency_code" => $this->client->currency()->code,
// "breakdown" => [
// "item_total" => [
// "currency_code" => $this->client->currency()->code,
// "value" => (string)$data['amount_with_fee']
// ]
// ]
// ],
// "items" => [
// [
// "name" => ctrans('texts.invoice_number').'# '.$invoice->number,
// "quantity" => "1",
// "unit_amount" => [
// "currency_code" => $this->client->currency()->code,
// "value" => (string)$data['amount_with_fee']
// ],
// ],
// ],
// ]
// ]
// ];
// $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order);
// return $r->json()['id'];
}
private function getShippingAddress(): ?array
@ -520,5 +461,17 @@ class PayPalRestPaymentDriver extends BaseDriver
return 0;
}
public function auth(): bool
{
try {
$this->init()->getClientToken();
return true;
}
catch(\Exception $e) {
}
return false;
}
}

View File

@ -246,4 +246,18 @@ class PaytracePaymentDriver extends BaseDriver
return false;
}
public function auth(): bool
{
try {
$this->init()->generateAuthHeaders() && strlen($this->company_gateway->getConfigField('integratorId')) > 2;
return true;
}
catch(\Exception $e){
}
return false;
}
}

View File

@ -429,4 +429,17 @@ class SquarePaymentDriver extends BaseDriver
return $amount;
}
public function auth(): bool
{
$api_response = $this->init()
->square
->getCustomersApi()
->listCustomers();
return (bool) count($api_response->getErrors()) == 0;
}
}

View File

@ -990,4 +990,20 @@ class StripePaymentDriver extends BaseDriver
return mb_convert_encoding(pack('H*', $matches[1]), 'UTF-8', 'UCS-2BE');
}, $string);
}
public function auth(): bool
{
$this->init();
try {
$this->verifyConnect();
return true;
}
catch(\Exception $e) {
}
return false;
}
}

View File

@ -162,6 +162,7 @@ class Statement
$ts->addGlobal(['show_credits' => $this->options['show_credits_table']]);
$ts->addGlobal(['show_aging' => $this->options['show_aging_table']]);
$ts->addGlobal(['show_payments' => $this->options['show_payments_table']]);
$ts->addGlobal(['currency_code' => $this->client->company->currency()->code]);
$ts->build([
'variables' => collect([$variables]),

View File

@ -250,8 +250,8 @@ class Email implements ShouldQueue
private function incrementEmailCounter(): void
{
if (in_array($this->mailer, ['default', 'mailgun']))
Cache::increment("email_quota" . $this->company->account->key);
if(in_array($this->mailer, ['default','mailgun','postmark']))
Cache::increment("email_quota".$this->company->account->key);
}
/**

View File

@ -113,24 +113,30 @@ class PdfMock
/** @var \App\Models\Invoice | \App\Models\Credit | \App\Models\Quote $entity */
$entity = Invoice::factory()->make();
$entity->client = Client::factory()->make(['settings' => $settings]);
$entity->client->setRelation('company', $this->company);
$entity->invitation = InvoiceInvitation::factory()->make();
break;
case 'quote':
/** @var \App\Models\Invoice | \App\Models\Credit | \App\Models\Quote $entity */
$entity = Quote::factory()->make();
$entity->client = Client::factory()->make(['settings' => $settings]);
$entity->client->setRelation('company', $this->company);
$entity->invitation = QuoteInvitation::factory()->make();
break;
case 'credit':
/** @var \App\Models\Invoice | \App\Models\Credit | \App\Models\Quote $entity */
$entity = Credit::factory()->make();
$entity->client = Client::factory()->make(['settings' => $settings]);
$entity->client->setRelation('company', $this->company);
$entity->invitation = CreditInvitation::factory()->make();
break;
case 'purchase_order':
/** @var \App\Models\Invoice | \App\Models\Credit | \App\Models\Quote $entity */
/** @var \App\Models\PurchaseOrder $entity */
$entity = PurchaseOrder::factory()->make();
$entity->client = Client::factory()->make(['settings' => $settings]);
// $entity->client = Client::factory()->make(['settings' => $settings]);
$entity->vendor = Vendor::factory()->make();
$entity->vendor->setRelation('company', $this->company);
$entity->invitation = PurchaseOrderInvitation::factory()->make();
break;
case PurchaseOrder::class:
@ -138,17 +144,17 @@ class PdfMock
$entity = PurchaseOrder::factory()->make();
$entity->invitation = PurchaseOrderInvitation::factory()->make();
$entity->vendor = Vendor::factory()->make();
$entity->invitation->setRelation('company', $this->company);
break;
default:
$entity = false;
break;
}
$entity->tax_map = $this->getTaxMap();
$entity->total_tax_map = $this->getTotalTaxMap();
$entity->invitation->company = $this->company;
return $entity;
}

View File

@ -613,8 +613,6 @@ class TemplateService
$this->payment = $payment;
$this->addGlobal(['currency_code' => $payment->currency->code ?? $this->company->currency()->code]);
$credits = $payment->credits->map(function ($credit) use ($payment) {
return [
'credit' => $credit->number,

View File

@ -16,7 +16,6 @@ return new class extends Migration
$table->decimal('discount', 20, 6)->default(0)->change();
});
Schema::table('credits', function (Blueprint $table) {
$table->decimal('discount', 20, 6)->default(0)->change();
});

View File

@ -5259,6 +5259,9 @@ $lang = array(
'select_email_provider' => 'Set your email as the sending user',
'purchase_order_items' => 'Purchase Order Items',
'csv_rows_length' => 'No data found in this CSV file',
'accept_payments_online' => 'Accept Payments Online',
'all_payment_gateways' => 'View all payment gateways',
'product_cost' => 'Product cost',
);
return $lang;

View File

@ -460,7 +460,7 @@ $lang = array(
'edit_token' => 'Éditer le jeton',
'delete_token' => 'Supprimer le jeton',
'token' => 'Jeton',
'add_gateway' => 'Add Payment Gateway',
'add_gateway' => 'Ajouter une passerelle de paiement',
'delete_gateway' => 'Supprimer la passerelle',
'edit_gateway' => 'Éditer la passerelle',
'updated_gateway' => 'La passerelle a été mise à jour',
@ -5248,9 +5248,11 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'payment_type_help' => 'Définit le <b>type de paiement manuel<b/> par défaut.',
'quote_valid_until_help' => 'Le nombre de jours pour lesquels la soumission est valide',
'expense_payment_type_help' => 'Le type de paiement de dépenses par défaut à utiliser',
'paylater' => 'Pay in 4',
'payment_provider' => 'Payment Provider',
'paylater' => 'Payer en 4',
'payment_provider' => 'Fournisseur de paiement',
'select_email_provider' => 'Définir le courriel pour l\'envoi',
'purchase_order_items' => 'Articles du bon d\'achat',
'csv_rows_length' => 'Aucune donnée dans ce fichier CSV',
);
return $lang;

View File

@ -195,7 +195,9 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
Route::get('company_ledger', [CompanyLedgerController::class, 'index'])->name('company_ledger.index');
Route::resource('company_gateways', CompanyGatewayController::class);
Route::post('company_gateways/bulk', [CompanyGatewayController::class, 'bulk'])->name('company_gateways.bulk');
Route::post('company_gateways/{company_gateway}/test', [CompanyGatewayController::class, 'test'])->name('company_gateways.test');
Route::put('company_users/{user}', [CompanyUserController::class, 'update']);
Route::put('company_users/{user}/preferences', [CompanyUserController::class, 'updatePreferences']);

View File

@ -49,6 +49,88 @@ class InvoiceTest extends TestCase
$this->invoice_calc = new InvoiceSum($this->invoice);
}
public function testRappenRounding()
{
$c_settings = $this->client->settings;
$c_settings->enable_rappen_rounding = true;
$c = \App\Models\Client::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'settings' => $c_settings,
]);
$item = InvoiceItemFactory::create();
$item->quantity = 1;
$item->cost = 10.01;
$item->type_id = '1';
$item->tax_id = '1';
$line_items[] = $item;
$i = Invoice::factory()->create([
'discount' => 0,
'tax_name1' => '',
'tax_name2' => '',
'tax_name3' => '',
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $c->id,
'line_items' => $line_items,
'status_id' => 1,
]);
$invoice_calc = new InvoiceSum($i);
$ii = $invoice_calc->build()->getInvoice();
$this->assertEquals(10, $ii->amount);
}
public function testRappenRoundingUp()
{
$c_settings = $this->client->settings;
$c_settings->enable_rappen_rounding = true;
$c = \App\Models\Client::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'settings' => $c_settings,
]);
$item = InvoiceItemFactory::create();
$item->quantity = 1;
$item->cost = 10.09;
$item->type_id = '1';
$item->tax_id = '1';
$line_items[] = $item;
$i = Invoice::factory()->create([
'discount' => 0,
'tax_name1' => '',
'tax_name2' => '',
'tax_name3' => '',
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $c->id,
'line_items' => $line_items,
'status_id' => 1,
]);
$invoice_calc = new InvoiceSum($i);
$ii = $invoice_calc->build()->getInvoice();
$this->assertEquals(10.10, round($ii->amount,2));
}
public function testPartialDueDateCast()
{
$i = Invoice::factory()

View File

@ -0,0 +1,36 @@
<?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 Tests\Unit;
use App\DataProviders\SMSNumbers;
use Tests\TestCase;
/**
* @test
*/
class SmsNumberTest extends TestCase
{
public function testArrayHit()
{
$this->assertTrue(SMSNumbers::hasNumber("+461614222"));
}
public function testArrayMiss()
{
$this->assertFalse(SMSNumbers::hasNumber("+5485454"));
}
public function testSmsArrayType()
{
$this->assertIsArray(SMSNumbers::getNumbers());
}
}