From b12fe64a63d89e2726cf449584a446ad4c5db5aa Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 07:59:25 +1100 Subject: [PATCH 01/12] Refactor gateway types --- app/Models/GatewayType.php | 14 ++++---------- app/PaymentDrivers/CheckoutComPaymentDriver.php | 11 +++-------- app/PaymentDrivers/StripePaymentDriver.php | 2 -- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 7e60467856ab..2cf245387bc2 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -22,16 +22,10 @@ class GatewayType extends StaticModel const BANK_TRANSFER = 2; const PAYPAL = 3; const CRYPTO = 4; - const DWOLLA = 5; - const CUSTOM1 = 6; - const ALIPAY = 7; - const SOFORT = 8; - const SEPA = 9; - const GOCARDLESS = 10; - const APPLE_PAY = 11; - const CUSTOM2 = 12; - const CUSTOM3 = 13; - const TOKEN = 'token'; + const CUSTOM = 5; + const ALIPAY = 6; + const SOFORT = 7; + const APPLE_PAY = 8; public function gateway() { diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php index bf6b4aa42a30..4d854817df27 100644 --- a/app/PaymentDrivers/CheckoutComPaymentDriver.php +++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php @@ -102,16 +102,11 @@ class CheckoutComPaymentDriver extends BaseDriver */ public function viewForType($gateway_type_id) { - + //currently only ever token or creditcard so no need for switches $this->payment_method = $gateway_type_id; - if ($gateway_type_id == GatewayType::CREDIT_CARD) { - return 'gateways.checkout.credit_card'; - } - - if ($gateway_type_id == GatewayType::TOKEN) { - return 'gateways.checkout.credit_card'; - } + return 'gateways.checkout.credit_card'; + } /** diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index 4d11d07b0295..9deed999d228 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -108,7 +108,6 @@ class StripePaymentDriver extends BaseDriver { $types = [ GatewayType::CREDIT_CARD, - //GatewayType::TOKEN, ]; if ($this->company_gateway->getSofortEnabled() && $this->invitation && $this->client() && isset($this->client()->country) && in_array($this->client()->country, ['AUT', 'BEL', 'DEU', 'ITA', 'NLD', 'ESP'])) { @@ -142,7 +141,6 @@ class StripePaymentDriver extends BaseDriver { switch ($gateway_type_id) { case GatewayType::CREDIT_CARD: - case GatewayType::TOKEN: return 'gateways.stripe.credit_card'; break; case GatewayType::SOFORT: From 1042bbec7bfcc71408c34abbb5b3998cf8df8ff0 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 08:16:39 +1100 Subject: [PATCH 02/12] Update vendor contact schema to match client contact schema --- app/Models/Vendor.php | 1 + app/Transformers/VendorTransformer.php | 1 + .../2014_10_13_000000_create_users_table.php | 1 + ...00_update_gateway_table_visible_column.php | 1 + ...2020_10_11_211122_vendor_schema_update.php | 47 +++++++++++++++++++ 5 files changed, 51 insertions(+) create mode 100644 database/migrations/2020_10_11_211122_vendor_schema_update.php diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 8cd1a54d5686..3cb8b05265ff 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -26,6 +26,7 @@ class Vendor extends BaseModel protected $fillable = [ 'name', + 'assigned_user_id', 'id_number', 'vat_number', 'work_phone', diff --git a/app/Transformers/VendorTransformer.php b/app/Transformers/VendorTransformer.php index 26cfc5e83c4b..a85e76a5af8d 100644 --- a/app/Transformers/VendorTransformer.php +++ b/app/Transformers/VendorTransformer.php @@ -85,6 +85,7 @@ class VendorTransformer extends EntityTransformer 'state' => $vendor->state ?: '', 'postal_code' => $vendor->postal_code ?: '', 'country_id' => (string) $vendor->country_id ?: '', + 'currency_id' => (string) $vendor->currency_id ?: '', 'custom_value1' => $vendor->custom_value1 ?: '', 'custom_value2' => $vendor->custom_value2 ?: '', 'custom_value3' => $vendor->custom_value3 ?: '', diff --git a/database/migrations/2014_10_13_000000_create_users_table.php b/database/migrations/2014_10_13_000000_create_users_table.php index f83680924a20..b4aa48dde235 100644 --- a/database/migrations/2014_10_13_000000_create_users_table.php +++ b/database/migrations/2014_10_13_000000_create_users_table.php @@ -1239,6 +1239,7 @@ class CreateUsersTable extends Migration $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade'); }); + Schema::create('expense_categories', function ($table) { $table->increments('id'); $table->unsignedInteger('user_id'); diff --git a/database/migrations/2020_09_27_215800_update_gateway_table_visible_column.php b/database/migrations/2020_09_27_215800_update_gateway_table_visible_column.php index c5b029f921f8..e991241343f2 100644 --- a/database/migrations/2020_09_27_215800_update_gateway_table_visible_column.php +++ b/database/migrations/2020_09_27_215800_update_gateway_table_visible_column.php @@ -42,6 +42,7 @@ class UpdateGatewayTableVisibleColumn extends Migration $t->boolean('is_deleted')->default(0); }); + } diff --git a/database/migrations/2020_10_11_211122_vendor_schema_update.php b/database/migrations/2020_10_11_211122_vendor_schema_update.php new file mode 100644 index 000000000000..cafedf9a10b5 --- /dev/null +++ b/database/migrations/2020_10_11_211122_vendor_schema_update.php @@ -0,0 +1,47 @@ +timestamp('email_verified_at')->nullable(); + $table->string('confirmation_code')->nullable(); + $table->boolean('confirmed')->default(false); + $table->timestamp('last_login')->nullable(); + $table->smallInteger('failed_logins')->nullable(); + $table->string('oauth_user_id', 100)->nullable()->unique(); + $table->unsignedInteger('oauth_provider_id')->nullable()->unique(); + $table->string('google_2fa_secret')->nullable(); + $table->string('accepted_terms_version')->nullable(); + $table->string('avatar', 255)->nullable(); + $table->string('avatar_type', 255)->nullable(); + $table->string('avatar_size', 255)->nullable(); + $table->string('password'); + $table->string('token')->nullable(); + $table->boolean('is_locked')->default(false); + $table->string('contact_key')->nullable(); + $table->rememberToken(); + $table->index(['company_id', 'email', 'deleted_at']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} From 4c08187bf1eaff135a6655428442e576f2ef8090 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 08:34:02 +1100 Subject: [PATCH 03/12] tests for documents --- app/Models/Project.php | 5 + app/Transformers/ExpenseTransformer.php | 11 +- app/Transformers/ProjectTransformer.php | 10 ++ app/Transformers/VendorTransformer.php | 10 ++ tests/Feature/DocumentsApiTest.php | 143 ++++++++++++++++++++++++ tests/MockAccountData.php | 6 + 6 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 tests/Feature/DocumentsApiTest.php diff --git a/app/Models/Project.php b/app/Models/Project.php index 62900d4e986f..e3669bc3ad7b 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -61,6 +61,11 @@ class Project extends BaseModel return $this->belongsTo(Client::class)->withTrashed(); } + public function documents() + { + return $this->morphMany(Document::class, 'documentable'); + } + // /** // * @return \Illuminate\Database\Eloquent\Relations\HasMany // */ diff --git a/app/Transformers/ExpenseTransformer.php b/app/Transformers/ExpenseTransformer.php index 7a78910a0b6a..8dcde2bfd23d 100644 --- a/app/Transformers/ExpenseTransformer.php +++ b/app/Transformers/ExpenseTransformer.php @@ -11,6 +11,7 @@ namespace App\Transformers; +use App\Models\Document; use App\Models\Expense; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\SoftDeletes; @@ -23,15 +24,23 @@ class ExpenseTransformer extends EntityTransformer use MakesHash; use SoftDeletes; protected $defaultIncludes = [ + 'documents', ]; /** * @var array */ protected $availableIncludes = [ - + 'documents', ]; + public function includeDocuments(Expense $expense) + { + $transformer = new DocumentTransformer($this->serializer); + + return $this->includeCollection($expense->documents, $transformer, Document::class); + } + /** * @param Expense $expense * diff --git a/app/Transformers/ProjectTransformer.php b/app/Transformers/ProjectTransformer.php index 8232603d14e3..6ba7d0749b97 100644 --- a/app/Transformers/ProjectTransformer.php +++ b/app/Transformers/ProjectTransformer.php @@ -11,6 +11,7 @@ namespace App\Transformers; +use App\Models\Document; use App\Models\Project; use App\Utils\Traits\MakesHash; @@ -22,14 +23,23 @@ class ProjectTransformer extends EntityTransformer use MakesHash; protected $defaultIncludes = [ + 'documents', ]; /** * @var array */ protected $availableIncludes = [ + 'documents' ]; + public function includeDocuments(Project $project) + { + $transformer = new DocumentTransformer($this->serializer); + + return $this->includeCollection($project->documents, $transformer, Document::class); + } + public function transform(Project $project) { return [ diff --git a/app/Transformers/VendorTransformer.php b/app/Transformers/VendorTransformer.php index a85e76a5af8d..e6a4c7799c03 100644 --- a/app/Transformers/VendorTransformer.php +++ b/app/Transformers/VendorTransformer.php @@ -12,6 +12,7 @@ namespace App\Transformers; use App\Models\Activity; +use App\Models\Document; use App\Models\Vendor; use App\Models\VendorContact; use App\Models\VendorGatewayToken; @@ -29,6 +30,7 @@ class VendorTransformer extends EntityTransformer protected $defaultIncludes = [ 'contacts', + 'documents' ]; /** @@ -36,6 +38,7 @@ class VendorTransformer extends EntityTransformer */ protected $availableIncludes = [ 'activities', + 'documents', ]; /** @@ -62,6 +65,13 @@ class VendorTransformer extends EntityTransformer return $this->includeCollection($vendor->contacts, $transformer, VendorContact::class); } + public function includeDocuments(Vendor $vendor) + { + $transformer = new DocumentTransformer($this->serializer); + + return $this->includeCollection($vendor->documents, $transformer, Document::class); + } + /** * @param Vendor $vendor * diff --git a/tests/Feature/DocumentsApiTest.php b/tests/Feature/DocumentsApiTest.php new file mode 100644 index 000000000000..512c918cb8f7 --- /dev/null +++ b/tests/Feature/DocumentsApiTest.php @@ -0,0 +1,143 @@ +makeTestData(); + + Session::start(); + + $this->faker = \Faker\Factory::create(); + + Model::reguard(); + } + + public function testClientDocuments() + { + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/clients'); + + $response->assertStatus(200); + $arr = $response->json(); + $this->assertArrayHasKey('documents', $arr['data'][0]); + + } + + + public function testInvoiceDocuments() + { + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/invoices'); + + $response->assertStatus(200); + $arr = $response->json(); + $this->assertArrayHasKey('documents', $arr['data'][0]); + + } + + + public function testProjectsDocuments() + { + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/projects'); + + $response->assertStatus(200); + $arr = $response->json(); + $this->assertArrayHasKey('documents', $arr['data'][0]); + + } + + + public function testExpenseDocuments() + { + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/expenses'); + + $response->assertStatus(200); + $arr = $response->json(); + $this->assertArrayHasKey('documents', $arr['data'][0]); + + } + + + public function testVendorDocuments() + { + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/vendors'); + + $response->assertStatus(200); + $arr = $response->json(); + $this->assertArrayHasKey('documents', $arr['data'][0]); + + } + + + public function testProductDocuments() + { + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/products'); + + $response->assertStatus(200); + $arr = $response->json(); + $this->assertArrayHasKey('documents', $arr['data'][0]); + + } + +} diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 31c88f16fd76..27acbaf2b934 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -35,6 +35,7 @@ use App\Models\Expense; use App\Models\GroupSetting; use App\Models\Invoice; use App\Models\InvoiceInvitation; +use App\Models\Product; use App\Models\Project; use App\Models\Quote; use App\Models\QuoteInvitation; @@ -159,6 +160,11 @@ trait MockAccountData $company_token->save(); + Product::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + ]); + $this->client = Client::factory()->create([ 'user_id' => $this->user->id, 'company_id' => $this->company->id, From f1c5f3718914051fc4ef3ff079b3450b957a1e49 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 08:36:59 +1100 Subject: [PATCH 04/12] Documents for tasks --- app/Transformers/TaskTransformer.php | 10 ++++++++++ tests/Feature/DocumentsApiTest.php | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/app/Transformers/TaskTransformer.php b/app/Transformers/TaskTransformer.php index b3d875349752..08d18981877b 100644 --- a/app/Transformers/TaskTransformer.php +++ b/app/Transformers/TaskTransformer.php @@ -11,6 +11,7 @@ namespace App\Transformers; +use App\Models\Document; use App\Models\Task; use App\Utils\Traits\MakesHash; @@ -22,14 +23,23 @@ class TaskTransformer extends EntityTransformer use MakesHash; protected $defaultIncludes = [ + 'documents' ]; /** * @var array */ protected $availableIncludes = [ + 'documents' ]; + public function includeDocuments(Vendor $vendor) + { + $transformer = new DocumentTransformer($this->serializer); + + return $this->includeCollection($task->documents, $transformer, Document::class); + } + public function transform(Task $task) { return [ diff --git a/tests/Feature/DocumentsApiTest.php b/tests/Feature/DocumentsApiTest.php index 512c918cb8f7..1b0ad8454674 100644 --- a/tests/Feature/DocumentsApiTest.php +++ b/tests/Feature/DocumentsApiTest.php @@ -140,4 +140,18 @@ class DocumentsApiTest extends TestCase } + // public function testTaskDocuments() + // { + + // $response = $this->withHeaders([ + // 'X-API-SECRET' => config('ninja.api_secret'), + // 'X-API-TOKEN' => $this->token, + // ])->get('/api/v1/tasks'); + + // $response->assertStatus(200); + // $arr = $response->json(); + // $this->assertArrayHasKey('documents', $arr['data'][0]); + + // } + } From 5072d63436dfe0131d475b74e38cb4e08254f447 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 10:21:24 +1100 Subject: [PATCH 05/12] Set visible to boolean cast --- app/Models/CompanyGateway.php | 4 ---- app/Models/Gateway.php | 2 +- app/Models/GatewayType.php | 3 ++- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index d569f26b96b1..3091a66247c4 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -79,10 +79,6 @@ class CompanyGateway extends BaseModel public function getTypeAlias($gateway_type_id) { - if ($gateway_type_id == 'token') { - $gateway_type_id = 1; - } - return GatewayType::find($gateway_type_id)->alias; } diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index 978838a98182..82aab212b1e0 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -20,7 +20,7 @@ class Gateway extends StaticModel 'is_offsite' => 'boolean', 'is_secure' => 'boolean', 'recommended' => 'boolean', - //'visible' => 'boolean', + 'visible' => 'boolean', 'sort_order' => 'int', 'updated_at' => 'timestamp', 'created_at' => 'timestamp', diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 2cf245387bc2..058fd116e8f5 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -26,7 +26,8 @@ class GatewayType extends StaticModel const ALIPAY = 6; const SOFORT = 7; const APPLE_PAY = 8; - + const SEPA = 9; + public function gateway() { return $this->belongsTo(Gateway::class); From c8a9997a980542715c0f6d6be689bbdf9beb61fb Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 10:27:38 +1100 Subject: [PATCH 06/12] fix return type in AddGatewayFee --- app/Services/Invoice/AddGatewayFee.php | 2 +- app/Transformers/TaskTransformer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/Invoice/AddGatewayFee.php b/app/Services/Invoice/AddGatewayFee.php index bc88092d3048..f7a5005fd7b1 100644 --- a/app/Services/Invoice/AddGatewayFee.php +++ b/app/Services/Invoice/AddGatewayFee.php @@ -45,7 +45,7 @@ class AddGatewayFee extends AbstractService $gateway_fee = round($this->company_gateway->calcGatewayFee($this->amount), $this->invoice->client->currency()->precision); if((int)$gateway_fee == 0) - return; + return $this->invoice; $this->cleanPendingGatewayFees(); diff --git a/app/Transformers/TaskTransformer.php b/app/Transformers/TaskTransformer.php index 08d18981877b..d5b21a4eacd9 100644 --- a/app/Transformers/TaskTransformer.php +++ b/app/Transformers/TaskTransformer.php @@ -33,7 +33,7 @@ class TaskTransformer extends EntityTransformer 'documents' ]; - public function includeDocuments(Vendor $vendor) + public function includeDocuments(Task $task) { $transformer = new DocumentTransformer($this->serializer); From c8770f09caffed8f010046551619a2b06a4769b0 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 15:10:34 +1100 Subject: [PATCH 07/12] Refactor for determining gateway fees --- app/Models/Client.php | 74 ++++++++++++++++------ app/Models/CompanyGateway.php | 5 +- app/PaymentDrivers/BaseDriver.php | 10 +-- app/PaymentDrivers/Stripe/Alipay.php | 4 +- app/Services/Invoice/AutoBillInvoice.php | 21 ++++-- tests/Unit/AutoBillInvoiceTest.php | 7 +- tests/Unit/Migration/FeesAndLimitsTest.php | 3 +- 7 files changed, 84 insertions(+), 40 deletions(-) diff --git a/app/Models/Client.php b/app/Models/Client.php index f651ca1f7725..41fa0b2b88f0 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -467,7 +467,8 @@ class Client extends BaseModel implements HasLocalePreference $company_gateways = $this->getSetting('company_gateway_ids'); - if ($company_gateways || $company_gateways == '0') { //we need to check for "0" here as we disable a payment gateway for a client with the number "0" + //we need to check for "0" here as we disable a payment gateway for a client with the number "0" + if ($company_gateways || $company_gateways == '0') { $transformed_ids = $this->transformKeys(explode(',', $company_gateways)); $gateways = $this->company @@ -480,32 +481,46 @@ class Client extends BaseModel implements HasLocalePreference $gateways = $this->company->company_gateways->where('is_deleted', false); } - $valid_gateways = $gateways->filter(function ($method) use ($amount) { - if (isset($method->fees_and_limits)) { - //sometimes the key value of the fees and limits object are not static, - //we have to harvest the key value as follows - $properties = array_keys(get_object_vars($method->fees_and_limits)); - $fees_and_limits = $method->fees_and_limits->{$properties[0]}; - } else { - return true; - } - if ((property_exists($fees_and_limits, 'min_limit')) && $fees_and_limits->min_limit !== null && $fees_and_limits->min_limit != -1 && $amount < $fees_and_limits->min_limit) { - return false; - } - if ((property_exists($fees_and_limits, 'max_limit')) && $fees_and_limits->max_limit !== null && $fees_and_limits->max_limit != -1 && $amount > $fees_and_limits->max_limit) { - return false; - } + // $valid_gateways = $gateways->filter(function ($method) use ($amount) { + // if (isset($method->fees_and_limits)) { + // //sometimes the key value of the fees and limits object are not static, + // //we have to harvest the key value as follows + // //Update!!! apparently we use the gateway_type_id + // $properties = array_keys(get_object_vars($method->fees_and_limits)); + // $fees_and_limits = $method->fees_and_limits->{$properties[0]}; //need to iterate over the $properties array as there may be many fees_and_limits + // } else { + // return true; + // } + + // if ((property_exists($fees_and_limits, 'min_limit')) && $fees_and_limits->min_limit !== null && $fees_and_limits->min_limit != -1 && $amount < $fees_and_limits->min_limit) { + // return false; + // } + + // if ((property_exists($fees_and_limits, 'max_limit')) && $fees_and_limits->max_limit !== null && $fees_and_limits->max_limit != -1 && $amount > $fees_and_limits->max_limit) { + // return false; + // } + + // return true; + // })->all(); + - return true; - })->all(); $payment_methods = []; - foreach ($valid_gateways as $gateway) { + foreach ($gateways as $gateway) { foreach ($gateway->driver($this)->gatewayTypes() as $type) { - $payment_methods[] = [$gateway->id => $type]; + + if(property_exists($gateway, 'fees_and_limits') property_exists($gateway->fees_and_limits, $type)){ + $fees_and_limits_for_payment_type = $gateway->fees_and_limits->{$type}; + } + else + continue; + + if($this->validGatewayForAmount($fees_and_limits_for_payment_type, $amount)) + $payment_methods[] = [$gateway->id => $type]; + } } @@ -533,6 +548,25 @@ class Client extends BaseModel implements HasLocalePreference return $payment_urls; } + private function validateFeesAndLimits($fees_and_limits_for_payment_type, $amount) :bool + { + if (isset($fees_and_limits_for_payment_type)) { + $fees_and_limits = $fees_and_limits_for_payment_type; + } else { + return true; + } + + if ((property_exists($fees_and_limits, 'min_limit')) && $fees_and_limits->min_limit !== null && $fees_and_limits->min_limit != -1 && $amount < $fees_and_limits->min_limit) { + return false; + } + + if ((property_exists($fees_and_limits, 'max_limit')) && $fees_and_limits->max_limit !== null && $fees_and_limits->max_limit != -1 && $amount > $fees_and_limits->max_limit) { + return false; + } + + return true; + } + public function preferredLocale() { $languages = Cache::get('languages'); diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 3091a66247c4..3d43a5c51c4e 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -228,7 +228,7 @@ class CompanyGateway extends BaseModel public function getFeesAndLimits() { - if (is_null($this->fees_and_limits)) { + if (is_null($this->fees_and_limits) || empty($this->fees_and_limits)) { return false; } @@ -269,6 +269,9 @@ class CompanyGateway extends BaseModel public function calcGatewayFee($amount, $include_taxes = false) { $fees_and_limits = $this->getFeesAndLimits(); +//dd($fees_and_limits); +// +// info(var_dump($$fees_and_limits)); if (! $fees_and_limits) { return 0; diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index d6089437a2e2..678d2728ba77 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -72,8 +72,8 @@ class BaseDriver extends AbstractPaymentDriver * Authorize a payment method. * * Returns a reusable token for storage for future payments - * @param const $payment_method the GatewayType::constant - * @return view Return a view for collecting payment method information + * @param const $payment_method The GatewayType::constant + * @return view Return a view for collecting payment method information */ public function authorize($payment_method) { @@ -82,8 +82,8 @@ class BaseDriver extends AbstractPaymentDriver /** * Executes purchase attempt for a given amount. * - * @param float $amount The amount to be collected - * @param bool $return_client_response Whether the method needs to return a response (otherwise we assume an unattended payment) + * @param float $amount The amount to be collected + * @param bool $return_client_response Whether the method needs to return a response (otherwise we assume an unattended payment) * @return mixed */ public function purchase($amount, $return_client_response = false) @@ -95,7 +95,7 @@ class BaseDriver extends AbstractPaymentDriver * * @param Payment $payment The Payment Object * @param float $amount The amount to be refunded - * @param bool $return_client_response Whether the method needs to return a response (otherwise we assume an unattended payment) + * @param bool $return_client_response Whether the method needs to return a response (otherwise we assume an unattended payment) * @return mixed */ public function refund(Payment $payment, $amount, $return_client_response = false) diff --git a/app/PaymentDrivers/Stripe/Alipay.php b/app/PaymentDrivers/Stripe/Alipay.php index c2f77cf490e8..06a37692066a 100644 --- a/app/PaymentDrivers/Stripe/Alipay.php +++ b/app/PaymentDrivers/Stripe/Alipay.php @@ -47,11 +47,11 @@ class Alipay { return route('client.payments.response', [ 'company_gateway_id' => $this->stripe->company_gateway->id, - 'gateway_type_id' => GatewayType::SOFORT, + 'gateway_type_id' => GatewayType::ALIPAY, 'hashed_ids' => implode(',', $data['hashed_ids']), 'amount' => $data['amount'], 'fee' => $data['fee'], - 'payment_method_id' => GatewayType::SOFORT, + 'payment_method_id' => GatewayType::ALIPAY, ]); } diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index f94a4d98ef47..8ca46609e902 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -15,6 +15,7 @@ use App\DataMapper\InvoiceItem; use App\Events\Payment\PaymentWasCreated; use App\Factory\PaymentFactory; use App\Models\Client; +use App\Models\Credit; use App\Models\Invoice; use App\Models\Payment; use App\Models\PaymentHash; @@ -56,6 +57,9 @@ class AutoBillInvoice extends AbstractService //if the credits cover the payments, we stop here, build the payment with credits and exit early $this->applyCreditPayment(); + info("partial = {$this->invoice->partial}"); + info("balance = {$this->invoice->balance}"); + /* Determine $amount */ if ($this->invoice->partial > 0) $amount = $this->invoice->partial; @@ -64,6 +68,8 @@ class AutoBillInvoice extends AbstractService else return $this->finalizePaymentUsingCredits(); + info("balance remains to be paid!!"); + $gateway_token = $this->getGateway($amount); /* Bail out if no payment methods available */ @@ -103,6 +109,7 @@ class AutoBillInvoice extends AbstractService $payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id); $payment->amount = $amount; + $payment->applied = $amount; $payment->client_id = $this->invoice->client_id; $payment->currency_id = $this->invoice->client->getSetting('currency_id'); $payment->date = now(); @@ -113,10 +120,12 @@ class AutoBillInvoice extends AbstractService $this->invoice->service()->setStatus(Invoice::STATUS_PAID)->save(); - foreach($this->used_credit as $credit) - { - $payment->credits()->attach($credit['credit_id'], ['amount' => $credit['amount']]); - } + foreach($this->used_credit as $credit) + { + $current_credit = Credit::find($credit['credit_id']); + $payment->credits()->attach($current_credit->id, ['amount' => $credit['amount']]); + $this->applyPaymentToCredit($current_credit, $credit['amount']); + } $payment->ledger() ->updatePaymentBalance($amount * -1) @@ -210,7 +219,7 @@ class AutoBillInvoice extends AbstractService - private function applyPaymentToCredit($credit, $amount) + private function applyPaymentToCredit($credit, $amount) :Credit { $credit_item = new InvoiceItem; @@ -226,7 +235,9 @@ class AutoBillInvoice extends AbstractService $credit->line_items = $credit_items; $credit = $credit->calc()->getCredit(); + $credit->save(); + return $credit; } /** diff --git a/tests/Unit/AutoBillInvoiceTest.php b/tests/Unit/AutoBillInvoiceTest.php index e31ddd2f15c6..5b7e4c89e05c 100644 --- a/tests/Unit/AutoBillInvoiceTest.php +++ b/tests/Unit/AutoBillInvoiceTest.php @@ -34,24 +34,19 @@ class AutoBillInvoiceTest extends TestCase public function testAutoBillFunctionality() { - // info("client balance = {$this->client->balance}"); - // info("invoice balance = {$this->invoice->balance}"); - - $this->assertEquals($this->client->balance, 10); $this->assertEquals($this->client->paid_to_date, 0); $this->assertEquals($this->client->credit_balance, 10); $this->invoice->service()->markSent()->autoBill()->save(); - // info(print_r($this->invoice->payments()->first()->toArray(),1)); $this->assertNotNull($this->invoice->payments()); $this->assertEquals(10, $this->invoice->payments()->sum('payments.amount')); - //info(print_r($this->invoice->payments()->get(),1)); $this->assertEquals($this->client->balance, 0); $this->assertEquals($this->client->paid_to_date, 10); $this->assertEquals($this->client->credit_balance, 0); + } } diff --git a/tests/Unit/Migration/FeesAndLimitsTest.php b/tests/Unit/Migration/FeesAndLimitsTest.php index 7550bf1a644b..ef737f075d6a 100644 --- a/tests/Unit/Migration/FeesAndLimitsTest.php +++ b/tests/Unit/Migration/FeesAndLimitsTest.php @@ -36,7 +36,8 @@ class FeesAndLimitsTest extends TestCase $data['fee_tax_rate2'] = ''; $data['fee_tax_name3'] = ''; $data['fee_tax_rate3'] = 0; - + $data['fee_cap'] = 0; + $fees_and_limits_array = []; $fees_and_limits_array[] = $data; From 046805995af4c9446854e0fa8432b2d3453b4ac1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 15:30:53 +1100 Subject: [PATCH 08/12] Recalculating gateway fees depending on gateway type --- app/Models/Client.php | 2 +- app/Services/Invoice/AutoBillInvoice.php | 4 +- .../Feature/CompanyGatewayResolutionTest.php | 79 +++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/CompanyGatewayResolutionTest.php diff --git a/app/Models/Client.php b/app/Models/Client.php index 41fa0b2b88f0..1cde445dedc2 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -512,7 +512,7 @@ class Client extends BaseModel implements HasLocalePreference foreach ($gateways as $gateway) { foreach ($gateway->driver($this)->gatewayTypes() as $type) { - if(property_exists($gateway, 'fees_and_limits') property_exists($gateway->fees_and_limits, $type)){ + if(property_exists($gateway, 'fees_and_limits') && property_exists($gateway->fees_and_limits, $type)){ $fees_and_limits_for_payment_type = $gateway->fees_and_limits->{$type}; } else diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 8ca46609e902..c73465fbbcb1 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -69,7 +69,7 @@ class AutoBillInvoice extends AbstractService return $this->finalizePaymentUsingCredits(); info("balance remains to be paid!!"); - + $gateway_token = $this->getGateway($amount); /* Bail out if no payment methods available */ @@ -79,6 +79,8 @@ class AutoBillInvoice extends AbstractService /* $gateway fee */ $fee = $gateway_token->gateway->calcGatewayFee($amount); +//determine exact fee as per PaymentController + /* Build payment hash */ $payment_hash = PaymentHash::create([ 'hash' => Str::random(128), diff --git a/tests/Feature/CompanyGatewayResolutionTest.php b/tests/Feature/CompanyGatewayResolutionTest.php new file mode 100644 index 000000000000..e0d9061bb240 --- /dev/null +++ b/tests/Feature/CompanyGatewayResolutionTest.php @@ -0,0 +1,79 @@ +withoutMiddleware( + ThrottleRequests::class + ); + + if (! config('ninja.testvars.stripe')) { + $this->markTestSkipped('Skip test no company gateways installed'); + } + + $this->faker = \Faker\Factory::create(); + + Model::reguard(); + + $this->makeTestData(); + + $this->withoutExceptionHandling(); + + $cg = new CompanyGateway; + $cg->company_id = $this->company->id; + $cg->user_id = $this->user->id; + $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; + $cg->require_cvv = true; + $cg->show_billing_address = true; + $cg->show_shipping_address = true; + $cg->update_details = true; + $cg->config = encrypt(config('ninja.testvars.stripe')); + $cg->save(); + } + + public function testGatewayResolution() + { + $this->assertTrue(true); + } +} From f063337ec4476919479a0a9ac3ede6c570b6e54c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 15:58:07 +1100 Subject: [PATCH 09/12] Refactoring gateway fees and limits --- app/Models/CompanyGateway.php | 25 +++++----- app/Services/Invoice/AutoBillInvoice.php | 2 +- .../Feature/CompanyGatewayResolutionTest.php | 47 +++++++++++++++++++ tests/Feature/CompanyGatewayTest.php | 5 +- 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 3d43a5c51c4e..301e48e43e23 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -226,19 +226,21 @@ class CompanyGateway extends BaseModel return $this->getConfigField('publishableKey'); } - public function getFeesAndLimits() + public function getFeesAndLimits($gateway_type_id) { if (is_null($this->fees_and_limits) || empty($this->fees_and_limits)) { return false; } - $fees_and_limits = new \stdClass; + return $this->fees_and_limits->{$gateway_type_id}; - foreach ($this->fees_and_limits as $key => $value) { - $fees_and_limits = $this->fees_and_limits->{$key}; - } + // $fees_and_limits = new \stdClass; - return $fees_and_limits; + // foreach ($this->fees_and_limits as $key => $value) { + // $fees_and_limits = $this->fees_and_limits->{$key}; + // } + +// return $fees_and_limits; } /** @@ -248,7 +250,7 @@ class CompanyGateway extends BaseModel * @param Client $client The client object * @return string The fee amount formatted in the client currency */ - public function calcGatewayFeeLabel($amount, Client $client) :string + public function calcGatewayFeeLabel($amount, Client $client, GatewayType $gateway_type_id = GatewayType::CREDIT_CARD) :string { $label = ''; @@ -256,7 +258,7 @@ class CompanyGateway extends BaseModel return $label; } - $fee = $this->calcGatewayFee($amount); + $fee = $this->calcGatewayFee($amount, $gateway_type_id); if ($fee > 0) { $fee = Number::formatMoney(round($fee, 2), $client); @@ -266,12 +268,9 @@ class CompanyGateway extends BaseModel return $label; } - public function calcGatewayFee($amount, $include_taxes = false) + public function calcGatewayFee($amount, $include_taxes = false, GatewayType $gateway_type_id = GatewayType::CREDIT_CARD) { - $fees_and_limits = $this->getFeesAndLimits(); -//dd($fees_and_limits); -// -// info(var_dump($$fees_and_limits)); + $fees_and_limits = $this->getFeesAndLimits($gateway_type_id); if (! $fees_and_limits) { return 0; diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index c73465fbbcb1..80c3e414c63e 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -79,7 +79,7 @@ class AutoBillInvoice extends AbstractService /* $gateway fee */ $fee = $gateway_token->gateway->calcGatewayFee($amount); -//determine exact fee as per PaymentController + //todo determine exact fee as per PaymentController /* Build payment hash */ $payment_hash = PaymentHash::create([ diff --git a/tests/Feature/CompanyGatewayResolutionTest.php b/tests/Feature/CompanyGatewayResolutionTest.php index e0d9061bb240..51ff50206383 100644 --- a/tests/Feature/CompanyGatewayResolutionTest.php +++ b/tests/Feature/CompanyGatewayResolutionTest.php @@ -10,6 +10,7 @@ */ namespace Tests\Feature; +use App\DataMapper\FeesAndLimits; use App\Factory\CreditFactory; use App\Factory\InvoiceItemFactory; use App\Helpers\Invoice\InvoiceSum; @@ -60,6 +61,49 @@ class CompanyGatewayResolutionTest extends TestCase $this->withoutExceptionHandling(); + $data = []; + $data[1]['min_limit'] = -1; + $data[1]['max_limit'] = -1; + $data[1]['fee_amount'] = 1.00; + $data[1]['fee_percent'] = 2; + $data[1]['fee_tax_name1'] = 'GST'; + $data[1]['fee_tax_rate1'] = 10; + $data[1]['fee_tax_name2'] = 'GST'; + $data[1]['fee_tax_rate2'] = 10; + $data[1]['fee_tax_name3'] = 'GST'; + $data[1]['fee_tax_rate3'] = 10; + $data[1]['fee_cap'] = 0; + + $json_config = config('ninja.testvars.stripe'); + $json_config->enable_ach = "0"; + + //disable ach here + $cg = new CompanyGateway; + $cg->company_id = $this->company->id; + $cg->user_id = $this->user->id; + $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; + $cg->require_cvv = true; + $cg->show_billing_address = true; + $cg->show_shipping_address = true; + $cg->update_details = true; + $cg->config = encrypt(json_encode($json_config)); + $cg->fees_and_limits = $data; + $cg->save(); + + $data = []; + $data[2]['min_limit'] = -1; + $data[2]['max_limit'] = -1; + $data[2]['fee_amount'] = 1.00; + $data[2]['fee_percent'] = 1; + $data[2]['fee_tax_name1'] = 'GST'; + $data[2]['fee_tax_rate1'] = 10; + $data[2]['fee_tax_name2'] = 'GST'; + $data[2]['fee_tax_rate2'] = 10; + $data[2]['fee_tax_name3'] = 'GST'; + $data[2]['fee_tax_rate3'] = 10; + $data[2]['fee_cap'] = 0; + + //ensable ach here $cg = new CompanyGateway; $cg->company_id = $this->company->id; $cg->user_id = $this->user->id; @@ -69,11 +113,14 @@ class CompanyGatewayResolutionTest extends TestCase $cg->show_shipping_address = true; $cg->update_details = true; $cg->config = encrypt(config('ninja.testvars.stripe')); + $cg->fees_and_limits = $data; $cg->save(); } public function testGatewayResolution() { $this->assertTrue(true); + + //i want to test here resolution of bank_transfers inside and outside of fees and limits. } } diff --git a/tests/Feature/CompanyGatewayTest.php b/tests/Feature/CompanyGatewayTest.php index 7da9cdd2977a..9d1618fa31bb 100644 --- a/tests/Feature/CompanyGatewayTest.php +++ b/tests/Feature/CompanyGatewayTest.php @@ -11,6 +11,7 @@ namespace Tests\Feature; use App\Models\CompanyGateway; +use App\Models\GatewayType; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\URL; use Tests\MockAccountData; @@ -178,12 +179,12 @@ class CompanyGatewayTest extends TestCase $total = 10.93; $total_invoice_count = 5; - $total_gateway_fee = round($cg->calcGatewayFee($total, true), 2); + $total_gateway_fee = round($cg->calcGatewayFee($total, true, GatewayType::CREDIT_CARD), 2); $this->assertEquals(1.58, $total_gateway_fee); /*simple pro rata*/ - $fees_and_limits = $cg->getFeesAndLimits(); + $fees_and_limits = $cg->getFeesAndLimits(GatewayType::CREDIT_CARD); /*Calculate all subcomponents of the fee*/ From 65a1f150d58f5e32fb6ea39bd0eedfa7310d264a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 20:38:55 +1100 Subject: [PATCH 10/12] Fixes for tests --- .../ClientPortal/PaymentController.php | 2 +- app/Models/CompanyGateway.php | 8 +- app/Services/Invoice/AddGatewayFee.php | 10 ++- app/Services/Invoice/InvoiceService.php | 4 +- app/Transformers/InvoiceTransformer.php | 1 + .../Feature/CompanyGatewayResolutionTest.php | 80 +++++++++++++------ tests/Feature/CompanyGatewayTest.php | 2 +- 7 files changed, 72 insertions(+), 35 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index c74d6721e7e5..388df11e4795 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -176,7 +176,7 @@ class PaymentController extends Controller // $fee_totals += $fee_tax; // } - $first_invoice->service()->addGatewayFee($gateway, $invoice_totals)->save(); + $first_invoice->service()->addGatewayFee($gateway, $payment_method_id, $invoice_totals)->save(); /** * diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 301e48e43e23..7742cee57d0e 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -232,6 +232,8 @@ class CompanyGateway extends BaseModel return false; } +info(print_r($this->fees_and_limits,1)); + return $this->fees_and_limits->{$gateway_type_id}; // $fees_and_limits = new \stdClass; @@ -250,7 +252,7 @@ class CompanyGateway extends BaseModel * @param Client $client The client object * @return string The fee amount formatted in the client currency */ - public function calcGatewayFeeLabel($amount, Client $client, GatewayType $gateway_type_id = GatewayType::CREDIT_CARD) :string + public function calcGatewayFeeLabel($amount, Client $client, $gateway_type_id = GatewayType::CREDIT_CARD) :string { $label = ''; @@ -268,8 +270,10 @@ class CompanyGateway extends BaseModel return $label; } - public function calcGatewayFee($amount, $include_taxes = false, GatewayType $gateway_type_id = GatewayType::CREDIT_CARD) + public function calcGatewayFee($amount, $include_taxes = false, $gateway_type_id = GatewayType::CREDIT_CARD) { + info($gateway_type_id); + $fees_and_limits = $this->getFeesAndLimits($gateway_type_id); if (! $fees_and_limits) { diff --git a/app/Services/Invoice/AddGatewayFee.php b/app/Services/Invoice/AddGatewayFee.php index f7a5005fd7b1..84af200f026e 100644 --- a/app/Services/Invoice/AddGatewayFee.php +++ b/app/Services/Invoice/AddGatewayFee.php @@ -31,18 +31,22 @@ class AddGatewayFee extends AbstractService private $amount; - public function __construct(CompanyGateway $company_gateway, Invoice $invoice, float $amount) + private $gateway_type_id; + + public function __construct(CompanyGateway $company_gateway, int $gateway_type_id, Invoice $invoice, float $amount) { $this->company_gateway = $company_gateway; $this->invoice = $invoice; $this->amount = $amount; + + $this->gateway_type_id = $gateway_type_id; } public function run() { - $gateway_fee = round($this->company_gateway->calcGatewayFee($this->amount), $this->invoice->client->currency()->precision); + $gateway_fee = round($this->company_gateway->calcGatewayFee($this->amount, $this->gateway_type_id), $this->invoice->client->currency()->precision); if((int)$gateway_fee == 0) return $this->invoice; @@ -78,7 +82,7 @@ class AddGatewayFee extends AbstractService $invoice_item->quantity = 1; $invoice_item->cost = $gateway_fee; - if ($fees_and_limits = $this->company_gateway->getFeesAndLimits()) { + if ($fees_and_limits = $this->company_gateway->getFeesAndLimits($this->gateway_type_id)) { $invoice_item->tax_rate1 = $fees_and_limits->fee_tax_rate1; $invoice_item->tax_rate2 = $fees_and_limits->fee_tax_rate2; $invoice_item->tax_rate3 = $fees_and_limits->fee_tax_rate3; diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index d814e382f7e8..325b8328ccea 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -93,9 +93,9 @@ class InvoiceService return $this; } - public function addGatewayFee(CompanyGateway $company_gateway, float $amount) + public function addGatewayFee(CompanyGateway $company_gateway, $gateway_type_id, float $amount) { - $this->invoice = (new AddGatewayFee($company_gateway, $this->invoice, $amount))->run(); + $this->invoice = (new AddGatewayFee($company_gateway, $gateway_type_id, $this->invoice, $amount))->run(); return $this; } diff --git a/app/Transformers/InvoiceTransformer.php b/app/Transformers/InvoiceTransformer.php index 980e1a2acf0b..a35986307900 100644 --- a/app/Transformers/InvoiceTransformer.php +++ b/app/Transformers/InvoiceTransformer.php @@ -95,6 +95,7 @@ class InvoiceTransformer extends EntityTransformer 'vendor_id' => (string) $this->encodePrimaryKey($invoice->vendor_id), 'status_id' => (string) ($invoice->status_id ?: 1), 'design_id' => (string) $this->encodePrimaryKey($invoice->design_id), + 'recurring_id' => (string) $this->encodePrimaryKey($invoice->recurring_id), 'created_at' => (int) $invoice->created_at, 'updated_at' => (int) $invoice->updated_at, 'archived_at' => (int) $invoice->deleted_at, diff --git a/tests/Feature/CompanyGatewayResolutionTest.php b/tests/Feature/CompanyGatewayResolutionTest.php index 51ff50206383..cbecd99f4586 100644 --- a/tests/Feature/CompanyGatewayResolutionTest.php +++ b/tests/Feature/CompanyGatewayResolutionTest.php @@ -18,6 +18,7 @@ use App\Listeners\Credit\CreateCreditInvitation; use App\Models\Client; use App\Models\CompanyGateway; use App\Models\Credit; +use App\Models\GatewayType; use App\Models\Invoice; use App\Models\Payment; use App\Models\Paymentable; @@ -41,6 +42,10 @@ class CompanyGatewayResolutionTest extends TestCase use DatabaseTransactions; use MockAccountData; + public $cg; + + public $cg1; + public function setUp() :void { parent::setUp(); @@ -64,7 +69,7 @@ class CompanyGatewayResolutionTest extends TestCase $data = []; $data[1]['min_limit'] = -1; $data[1]['max_limit'] = -1; - $data[1]['fee_amount'] = 1.00; + $data[1]['fee_amount'] = 0.00; $data[1]['fee_percent'] = 2; $data[1]['fee_tax_name1'] = 'GST'; $data[1]['fee_tax_rate1'] = 10; @@ -74,21 +79,33 @@ class CompanyGatewayResolutionTest extends TestCase $data[1]['fee_tax_rate3'] = 10; $data[1]['fee_cap'] = 0; - $json_config = config('ninja.testvars.stripe'); + $data[2]['min_limit'] = -1; + $data[2]['max_limit'] = -1; + $data[2]['fee_amount'] = 0.00; + $data[2]['fee_percent'] = 1; + $data[2]['fee_tax_name1'] = 'GST'; + $data[2]['fee_tax_rate1'] = 10; + $data[2]['fee_tax_name2'] = 'GST'; + $data[2]['fee_tax_rate2'] = 10; + $data[2]['fee_tax_name3'] = 'GST'; + $data[2]['fee_tax_rate3'] = 10; + $data[2]['fee_cap'] = 0; + + $json_config = json_decode(config('ninja.testvars.stripe')); $json_config->enable_ach = "0"; //disable ach here - $cg = new CompanyGateway; - $cg->company_id = $this->company->id; - $cg->user_id = $this->user->id; - $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; - $cg->require_cvv = true; - $cg->show_billing_address = true; - $cg->show_shipping_address = true; - $cg->update_details = true; - $cg->config = encrypt(json_encode($json_config)); - $cg->fees_and_limits = $data; - $cg->save(); + $this->cg = new CompanyGateway; + $this->cg->company_id = $this->company->id; + $this->cg->user_id = $this->user->id; + $this->cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; + $this->cg->require_cvv = true; + $this->cg->show_billing_address = true; + $this->cg->show_shipping_address = true; + $this->cg->update_details = true; + $this->cg->config = encrypt(json_encode($json_config)); + $this->cg->fees_and_limits = $data; + $this->cg->save(); $data = []; $data[2]['min_limit'] = -1; @@ -104,23 +121,34 @@ class CompanyGatewayResolutionTest extends TestCase $data[2]['fee_cap'] = 0; //ensable ach here - $cg = new CompanyGateway; - $cg->company_id = $this->company->id; - $cg->user_id = $this->user->id; - $cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; - $cg->require_cvv = true; - $cg->show_billing_address = true; - $cg->show_shipping_address = true; - $cg->update_details = true; - $cg->config = encrypt(config('ninja.testvars.stripe')); - $cg->fees_and_limits = $data; - $cg->save(); + $this->cg1 = new CompanyGateway; + $this->cg1->company_id = $this->company->id; + $this->cg1->user_id = $this->user->id; + $this->cg1->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; + $this->cg1->require_cvv = true; + $this->cg1->show_billing_address = true; + $this->cg1->show_shipping_address = true; + $this->cg1->update_details = true; + $this->cg1->config = encrypt(config('ninja.testvars.stripe')); + $this->cg1->fees_and_limits = $data; + $this->cg1->save(); } + /** + * @covers \App\Models\CompanyGateway::calcGatewayFee() + */ public function testGatewayResolution() { - $this->assertTrue(true); - + //i want to test here resolution of bank_transfers inside and outside of fees and limits. + + $fee = $this->cg->calcGatewayFee(10, false, GatewayType::CREDIT_CARD); + + $this->assertEquals(0.2, $fee); + + $fee = $this->cg->calcGatewayFee(10, false, GatewayType::BANK_TRANSFER); + + $this->assertEquals(0.1, $fee); + } } diff --git a/tests/Feature/CompanyGatewayTest.php b/tests/Feature/CompanyGatewayTest.php index 9d1618fa31bb..8dc1499fc17a 100644 --- a/tests/Feature/CompanyGatewayTest.php +++ b/tests/Feature/CompanyGatewayTest.php @@ -142,7 +142,7 @@ class CompanyGatewayTest extends TestCase $balance = $this->invoice->balance; - $this->invoice = $this->invoice->service()->addGatewayFee($cg, $this->invoice->balance)->save(); + $this->invoice = $this->invoice->service()->addGatewayFee($cg, GatewayType::CREDIT_CARD, $this->invoice->balance)->save(); $this->invoice = $this->invoice->calc()->getInvoice(); $items = $this->invoice->line_items; From 1eadbde5443a9e328beca6408d2f3dbbabaecc0d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 22:02:02 +1100 Subject: [PATCH 11/12] working on refactor of company gateway fees --- .../ClientPortal/PaymentController.php | 2 +- app/Models/Client.php | 19 ++-- app/Models/CompanyGateway.php | 9 -- .../Feature/CompanyGatewayResolutionTest.php | 103 +++++++++++++----- 4 files changed, 89 insertions(+), 44 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index 388df11e4795..6b7dc20d2a07 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -155,7 +155,7 @@ class PaymentController extends Controller }); } - $payment_methods = auth()->user()->client->getPaymentMethods(array_sum(array_column($payable_invoices, 'amount_with_fee'))); + //$payment_methods = auth()->user()->client->getPaymentMethods(array_sum(array_column($payable_invoices, 'amount_with_fee'))); $payment_method_id = request()->input('payment_method_id'); diff --git a/app/Models/Client.php b/app/Models/Client.php index 1cde445dedc2..fe041aff0333 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -511,15 +511,18 @@ class Client extends BaseModel implements HasLocalePreference foreach ($gateways as $gateway) { foreach ($gateway->driver($this)->gatewayTypes() as $type) { - - if(property_exists($gateway, 'fees_and_limits') && property_exists($gateway->fees_and_limits, $type)){ - $fees_and_limits_for_payment_type = $gateway->fees_and_limits->{$type}; + // info(print_r($gateway,1)); + // info($type); + if(property_exists($gateway->fees_and_limits, $type)) + { + if($this->validGatewayForAmount($gateway->fees_and_limits->{$type}, $amount)){ + $payment_methods[] = [$gateway->id => $type]; + } } - else - continue; - - if($this->validGatewayForAmount($fees_and_limits_for_payment_type, $amount)) + else + { $payment_methods[] = [$gateway->id => $type]; + } } } @@ -548,7 +551,7 @@ class Client extends BaseModel implements HasLocalePreference return $payment_urls; } - private function validateFeesAndLimits($fees_and_limits_for_payment_type, $amount) :bool + public function validGatewayForAmount($fees_and_limits_for_payment_type, $amount) :bool { if (isset($fees_and_limits_for_payment_type)) { $fees_and_limits = $fees_and_limits_for_payment_type; diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 7742cee57d0e..0bdcea04cbeb 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -232,17 +232,8 @@ class CompanyGateway extends BaseModel return false; } -info(print_r($this->fees_and_limits,1)); - return $this->fees_and_limits->{$gateway_type_id}; - // $fees_and_limits = new \stdClass; - - // foreach ($this->fees_and_limits as $key => $value) { - // $fees_and_limits = $this->fees_and_limits->{$key}; - // } - -// return $fees_and_limits; } /** diff --git a/tests/Feature/CompanyGatewayResolutionTest.php b/tests/Feature/CompanyGatewayResolutionTest.php index cbecd99f4586..8a54062985e3 100644 --- a/tests/Feature/CompanyGatewayResolutionTest.php +++ b/tests/Feature/CompanyGatewayResolutionTest.php @@ -32,6 +32,7 @@ use Illuminate\Support\Carbon; use Illuminate\Validation\ValidationException; use Tests\MockAccountData; use Tests\TestCase; +use Illuminate\Support\Facades\Crypt; /** * @test @@ -107,31 +108,31 @@ class CompanyGatewayResolutionTest extends TestCase $this->cg->fees_and_limits = $data; $this->cg->save(); - $data = []; - $data[2]['min_limit'] = -1; - $data[2]['max_limit'] = -1; - $data[2]['fee_amount'] = 1.00; - $data[2]['fee_percent'] = 1; - $data[2]['fee_tax_name1'] = 'GST'; - $data[2]['fee_tax_rate1'] = 10; - $data[2]['fee_tax_name2'] = 'GST'; - $data[2]['fee_tax_rate2'] = 10; - $data[2]['fee_tax_name3'] = 'GST'; - $data[2]['fee_tax_rate3'] = 10; - $data[2]['fee_cap'] = 0; + // $data = []; + // $data[2]['min_limit'] = -1; + // $data[2]['max_limit'] = -1; + // $data[2]['fee_amount'] = 1.00; + // $data[2]['fee_percent'] = 1; + // $data[2]['fee_tax_name1'] = 'GST'; + // $data[2]['fee_tax_rate1'] = 10; + // $data[2]['fee_tax_name2'] = 'GST'; + // $data[2]['fee_tax_rate2'] = 10; + // $data[2]['fee_tax_name3'] = 'GST'; + // $data[2]['fee_tax_rate3'] = 10; + // $data[2]['fee_cap'] = 0; - //ensable ach here - $this->cg1 = new CompanyGateway; - $this->cg1->company_id = $this->company->id; - $this->cg1->user_id = $this->user->id; - $this->cg1->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; - $this->cg1->require_cvv = true; - $this->cg1->show_billing_address = true; - $this->cg1->show_shipping_address = true; - $this->cg1->update_details = true; - $this->cg1->config = encrypt(config('ninja.testvars.stripe')); - $this->cg1->fees_and_limits = $data; - $this->cg1->save(); + // //ensable ach here + // $this->cg1 = new CompanyGateway; + // $this->cg1->company_id = $this->company->id; + // $this->cg1->user_id = $this->user->id; + // $this->cg1->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; + // $this->cg1->require_cvv = true; + // $this->cg1->show_billing_address = true; + // $this->cg1->show_shipping_address = true; + // $this->cg1->update_details = true; + // $this->cg1->config = encrypt(config('ninja.testvars.stripe')); + // $this->cg1->fees_and_limits = $data; + // $this->cg1->save(); } /** @@ -140,8 +141,6 @@ class CompanyGatewayResolutionTest extends TestCase public function testGatewayResolution() { - //i want to test here resolution of bank_transfers inside and outside of fees and limits. - $fee = $this->cg->calcGatewayFee(10, false, GatewayType::CREDIT_CARD); $this->assertEquals(0.2, $fee); @@ -151,4 +150,56 @@ class CompanyGatewayResolutionTest extends TestCase $this->assertEquals(0.1, $fee); } + + /** + * @covers \App|Models\Client::validGatewayForAmount() + */ + + public function testValidationForGatewayAmount() + { + $this->assertTrue($this->client->validGatewayForAmount($this->cg->fees_and_limits->{1}, 10)); + $this->assertTrue($this->client->validGatewayForAmount($this->cg->fees_and_limits->{2}, 10)); + } + + public function testAvailablePaymentMethodsCount() + { + $amount = 10; + $payment_methods = []; + + $this->assertInstanceOf("\\stdClass", $this->cg->fees_and_limits); + $this->assertObjectHasAttribute('min_limit',$this->cg->fees_and_limits->{1}); + + foreach ($this->cg->driver($this->client)->gatewayTypes() as $type) + { + + if(property_exists($this->cg->fees_and_limits, $type)) + { + if($this->client->validGatewayForAmount($this->cg->fees_and_limits->{$type}, $amount)){ + info("valid gateways"); + $payment_methods[] = [$this->cg->id => $type]; + } + } + else + { + $payment_methods[] = [$this->cg->id => $type]; + } + + } + + $this->assertEquals(3, count($payment_methods)); + } + + public function testAddAchBackIntoMethods() + { + $this->assertEquals(3, count($this->cg->driver($this->client)->gatewayTypes())); + + $cg_config = json_decode(decrypt($this->cg->config)); + $cg_config->enable_ach = "1"; + $this->cg->config = encrypt(json_encode($cg_config)); + $this->cg->save(); + + $this->assertEquals(4, count($this->cg->driver($this->client)->gatewayTypes())); + + info(print_r($this->client->getPaymentMethods(10),1)); + } } From d89b4fcbc37b1bdb1aae91d0b61525493589a331 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 22:25:27 +1100 Subject: [PATCH 12/12] Refactor for gateway fee calculations --- app/Models/Client.php | 44 +++++-------------- app/Models/CompanyGateway.php | 1 - app/Services/Invoice/AutoBillInvoice.php | 2 +- .../Feature/CompanyGatewayResolutionTest.php | 30 +------------ 4 files changed, 12 insertions(+), 65 deletions(-) diff --git a/app/Models/Client.php b/app/Models/Client.php index fe041aff0333..f4af6cee2fab 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -474,54 +474,30 @@ class Client extends BaseModel implements HasLocalePreference $gateways = $this->company ->company_gateways ->whereIn('id', $transformed_ids) - ->sortby(function ($model) use ($transformed_ids) { - return array_search($model->id, $transformed_ids); + ->sortby(function ($model) use ($transformed_ids) { //company gateways are sorted in order of priority + return array_search($model->id, $transformed_ids);// this closure sorts for us }); } else { $gateways = $this->company->company_gateways->where('is_deleted', false); } - - - // $valid_gateways = $gateways->filter(function ($method) use ($amount) { - // if (isset($method->fees_and_limits)) { - // //sometimes the key value of the fees and limits object are not static, - // //we have to harvest the key value as follows - // //Update!!! apparently we use the gateway_type_id - // $properties = array_keys(get_object_vars($method->fees_and_limits)); - // $fees_and_limits = $method->fees_and_limits->{$properties[0]}; //need to iterate over the $properties array as there may be many fees_and_limits - // } else { - // return true; - // } - - // if ((property_exists($fees_and_limits, 'min_limit')) && $fees_and_limits->min_limit !== null && $fees_and_limits->min_limit != -1 && $amount < $fees_and_limits->min_limit) { - // return false; - // } - - // if ((property_exists($fees_and_limits, 'max_limit')) && $fees_and_limits->max_limit !== null && $fees_and_limits->max_limit != -1 && $amount > $fees_and_limits->max_limit) { - // return false; - // } - - // return true; - // })->all(); - - - $payment_methods = []; foreach ($gateways as $gateway) { foreach ($gateway->driver($this)->gatewayTypes() as $type) { - // info(print_r($gateway,1)); - // info($type); - if(property_exists($gateway->fees_and_limits, $type)) + + if(isset($gateway->fees_and_limits) && property_exists($gateway->fees_and_limits, $type)) { - if($this->validGatewayForAmount($gateway->fees_and_limits->{$type}, $amount)){ + + if($this->validGatewayForAmount($gateway->fees_and_limits->{$type}, $amount)) $payment_methods[] = [$gateway->id => $type]; - } + } else { + $payment_methods[] = [$gateway->id => $type]; + } } @@ -544,7 +520,7 @@ class Client extends BaseModel implements HasLocalePreference 'label' => ctrans('texts.'.$gateway->getTypeAlias($gateway_type_id)).$fee_label, 'company_gateway_id' => $gateway_id, 'gateway_type_id' => $gateway_type_id, - ]; + ]; } } diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 0bdcea04cbeb..b05d20a94b05 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -263,7 +263,6 @@ class CompanyGateway extends BaseModel public function calcGatewayFee($amount, $include_taxes = false, $gateway_type_id = GatewayType::CREDIT_CARD) { - info($gateway_type_id); $fees_and_limits = $this->getFeesAndLimits($gateway_type_id); diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 80c3e414c63e..42bc0498e094 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -77,7 +77,7 @@ class AutoBillInvoice extends AbstractService return $this->invoice; /* $gateway fee */ - $fee = $gateway_token->gateway->calcGatewayFee($amount); + $fee = $gateway_token->gateway->calcGatewayFee($amount, $this->invoice->uses_inclusive_taxes); //todo determine exact fee as per PaymentController diff --git a/tests/Feature/CompanyGatewayResolutionTest.php b/tests/Feature/CompanyGatewayResolutionTest.php index 8a54062985e3..fd2cc0db0f1d 100644 --- a/tests/Feature/CompanyGatewayResolutionTest.php +++ b/tests/Feature/CompanyGatewayResolutionTest.php @@ -92,10 +92,10 @@ class CompanyGatewayResolutionTest extends TestCase $data[2]['fee_tax_rate3'] = 10; $data[2]['fee_cap'] = 0; + //disable ach here $json_config = json_decode(config('ninja.testvars.stripe')); $json_config->enable_ach = "0"; - //disable ach here $this->cg = new CompanyGateway; $this->cg->company_id = $this->company->id; $this->cg->user_id = $this->user->id; @@ -108,31 +108,7 @@ class CompanyGatewayResolutionTest extends TestCase $this->cg->fees_and_limits = $data; $this->cg->save(); - // $data = []; - // $data[2]['min_limit'] = -1; - // $data[2]['max_limit'] = -1; - // $data[2]['fee_amount'] = 1.00; - // $data[2]['fee_percent'] = 1; - // $data[2]['fee_tax_name1'] = 'GST'; - // $data[2]['fee_tax_rate1'] = 10; - // $data[2]['fee_tax_name2'] = 'GST'; - // $data[2]['fee_tax_rate2'] = 10; - // $data[2]['fee_tax_name3'] = 'GST'; - // $data[2]['fee_tax_rate3'] = 10; - // $data[2]['fee_cap'] = 0; - // //ensable ach here - // $this->cg1 = new CompanyGateway; - // $this->cg1->company_id = $this->company->id; - // $this->cg1->user_id = $this->user->id; - // $this->cg1->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23'; - // $this->cg1->require_cvv = true; - // $this->cg1->show_billing_address = true; - // $this->cg1->show_shipping_address = true; - // $this->cg1->update_details = true; - // $this->cg1->config = encrypt(config('ninja.testvars.stripe')); - // $this->cg1->fees_and_limits = $data; - // $this->cg1->save(); } /** @@ -142,11 +118,8 @@ class CompanyGatewayResolutionTest extends TestCase { $fee = $this->cg->calcGatewayFee(10, false, GatewayType::CREDIT_CARD); - $this->assertEquals(0.2, $fee); - $fee = $this->cg->calcGatewayFee(10, false, GatewayType::BANK_TRANSFER); - $this->assertEquals(0.1, $fee); } @@ -175,7 +148,6 @@ class CompanyGatewayResolutionTest extends TestCase if(property_exists($this->cg->fees_and_limits, $type)) { if($this->client->validGatewayForAmount($this->cg->fees_and_limits->{$type}, $amount)){ - info("valid gateways"); $payment_methods[] = [$this->cg->id => $type]; } }