diff --git a/app/Console/Commands/CreateSingleAccount.php b/app/Console/Commands/CreateSingleAccount.php
index 7dde1e3bc9..df64204634 100644
--- a/app/Console/Commands/CreateSingleAccount.php
+++ b/app/Console/Commands/CreateSingleAccount.php
@@ -25,6 +25,7 @@ use App\Models\Account;
use App\Models\Company;
use App\Models\Country;
use App\Models\Expense;
+use App\Models\Gateway;
use App\Models\Invoice;
use App\Models\Product;
use App\Models\Project;
diff --git a/app/DataMapper/EmailTemplateDefaults.php b/app/DataMapper/EmailTemplateDefaults.php
index b63dba59c7..93be2e4e98 100644
--- a/app/DataMapper/EmailTemplateDefaults.php
+++ b/app/DataMapper/EmailTemplateDefaults.php
@@ -92,6 +92,11 @@ class EmailTemplateDefaults
case 'email_subject_custom3':
return self::emailInvoiceSubject();
+ case 'email_vendor_notification_subject':
+ return self::emailVendorNotificationSubject();
+
+ case 'email_vendor_notification_body':
+ return self::emailVendorNotificationBody();
default:
return self::emailInvoiceTemplate();
@@ -99,6 +104,16 @@ class EmailTemplateDefaults
}
}
+ public static function emailVendorNotificationSubject()
+ {
+ return self::transformText('vendor_notification_subject');
+ }
+
+ public static function emailVendorNotificationBody()
+ {
+ return self::transformText('vendor_notification_body');
+ }
+
public static function emailInvoiceSubject()
{
return ctrans('texts.invoice_subject', ['number'=>'$number', 'account'=>'$company.name']);
diff --git a/app/Export/CSV/ClientExport.php b/app/Export/CSV/ClientExport.php
index 29178f5236..6579ed3f04 100644
--- a/app/Export/CSV/ClientExport.php
+++ b/app/Export/CSV/ClientExport.php
@@ -11,16 +11,17 @@
namespace App\Export\CSV;
-use App\Libraries\MultiDB;
-use App\Models\Client;
-use App\Models\Company;
-use App\Transformers\ClientContactTransformer;
-use App\Transformers\ClientTransformer;
use App\Utils\Ninja;
use App\Utils\Number;
-use Illuminate\Database\Eloquent\Builder;
-use Illuminate\Support\Facades\App;
+use App\Models\Client;
use League\Csv\Writer;
+use App\Models\Company;
+use App\Libraries\MultiDB;
+use Illuminate\Support\Facades\App;
+use App\Export\Decorators\Decorator;
+use App\Transformers\ClientTransformer;
+use Illuminate\Database\Eloquent\Builder;
+use App\Transformers\ClientContactTransformer;
class ClientExport extends BaseExport
{
@@ -32,6 +33,8 @@ class ClientExport extends BaseExport
public string $date_key = 'created_at';
+ private Decorator $decorator;
+
public array $entity_keys = [
'address1' => 'client.address1',
'address2' => 'client.address2',
@@ -84,6 +87,8 @@ class ClientExport extends BaseExport
$this->input = $input;
$this->client_transformer = new ClientTransformer();
$this->contact_transformer = new ClientContactTransformer();
+ $this->decorator = new Decorator();
+
}
public function returnJson()
diff --git a/app/Export/CSV/PaymentExport.php b/app/Export/CSV/PaymentExport.php
index c92fc5983f..86ee229bfd 100644
--- a/app/Export/CSV/PaymentExport.php
+++ b/app/Export/CSV/PaymentExport.php
@@ -11,14 +11,15 @@
namespace App\Export\CSV;
-use App\Libraries\MultiDB;
+use App\Utils\Ninja;
+use League\Csv\Writer;
use App\Models\Company;
use App\Models\Payment;
-use App\Transformers\PaymentTransformer;
-use App\Utils\Ninja;
-use Illuminate\Database\Eloquent\Builder;
+use App\Libraries\MultiDB;
use Illuminate\Support\Facades\App;
-use League\Csv\Writer;
+use App\Transformers\PaymentTransformer;
+use Illuminate\Database\Eloquent\Builder;
+use App\Export\Decorators\Decorator;
class PaymentExport extends BaseExport
{
@@ -28,13 +29,16 @@ class PaymentExport extends BaseExport
public Writer $csv;
+ private Decorator $decorator;
+
public function __construct(Company $company, array $input)
{
$this->company = $company;
$this->input = $input;
$this->entity_transformer = new PaymentTransformer();
+ $this->decorator = new Decorator();
}
-
+
private function init(): Builder
{
@@ -113,6 +117,8 @@ class PaymentExport extends BaseExport
} elseif (array_key_exists($key, $transformed_entity)) {
$entity[$key] = $transformed_entity[$key];
} else {
+
+ // $entity[$key] = $this->decorator->transform($key, $payment);
$entity[$key] = $this->resolveKey($key, $payment, $this->entity_transformer);
}
diff --git a/app/Export/Decorators/ClientDecorator.php b/app/Export/Decorators/ClientDecorator.php
index 83fbdad948..3af0cef276 100644
--- a/app/Export/Decorators/ClientDecorator.php
+++ b/app/Export/Decorators/ClientDecorator.php
@@ -11,10 +11,165 @@
namespace App\Export\Decorators;
-class ClientDecorator implements DecoratorInterface
+use App\Models\Client;
+
+class ClientDecorator extends Decorator implements DecoratorInterface
{
- public function transform(): string
+ private $entity_key = 'client';
+
+ public function transform(string $key, mixed $entity): mixed
{
- return 'Payment Decorator';
+ $client = false;
+
+ if($entity instanceof Client){
+ $client = $entity;
+ }
+ elseif($entity->client) {
+ $client = $entity->client;
+ }
+
+ if($client && method_exists($this, $key)) {
+ return $this->{$key}($client);
+ }
+
+ return '';
}
+
+ public function name(Client $client) {
+ return $client->present()->name();
+ }
+ public function number(Client $client) {
+ return $client->number ?? '';
+ }
+ public function user(Client $client) {
+ return $client->user->present()->name();
+ }
+ public function assigned_user(Client $client) {
+ return $client->assigned_user ? $client->user->present()->name() : '';
+ }
+ public function balance(Client $client) {
+ return $client->balance ?? 0;
+ }
+ public function paid_to_date(Client $client) {
+ return $client->paid_to_date ?? 0;
+ }
+ public function currency_id(Client $client) {
+ return $client->currency() ? $client->currency()->code : $client->company->currency()->code;
+ }
+ public function website(Client $client) {
+ return $client->website ?? '';
+ }
+ public function private_notes(Client $client) {
+ return $client->private_notes ?? '';
+ }
+ public function industry_id(Client $client) {
+ return $client->industry ? ctrans("texts.industry_{$client->industry->name}") : '';
+ }
+ public function size_id(Client $client) {
+ return $client->size ? ctrans("texts.size_{$client->size->name}") : '';
+ }
+ public function phone(Client $client) {
+ return $client->phone ?? '';
+ }
+ public function address1(Client $client) {
+ return $client->address1 ?? '';
+ }
+ public function address2(Client $client) {
+ return $client->address2 ?? '';
+ }
+ public function city(Client $client) {
+ return $client->city ?? '';
+ }
+ public function state(Client $client) {
+ return $client->state ?? '';
+ }
+ public function postal_code(Client $client) {
+ return $client->postal_code ?? '';
+ }
+ public function country_id(Client $client) {
+ return $client->country ? ctrans("texts.country_{$client->country->name}") : '';
+ }
+ public function shipping_address1(Client $client) {
+ return $client->shipping_address1 ?? '';
+ }
+ public function shipping_address2(Client $client) {
+ return $client->shipping_address2 ?? '';
+ }
+ public function shipping_city(Client $client) {
+ return $client->shipping_city ?? '';
+ }
+ public function shipping_state(Client $client) {
+ return $client->shipping_state ?? '';
+ }
+ public function shipping_postal_code(Client $client) {
+ return $client->shipping_postal_code ?? '';
+ }
+ public function shipping_country_id(Client $client) {
+ return $client->shipping_country ? ctrans("texts.country_{$client->shipping_country->name}") : '';
+ }
+ public function payment_terms(Client $client) {
+ return $client?->settings?->payment_terms ?? $client->company->settings->payment_terms;
+ }
+ public function vat_number(Client $client) {
+ return $client->vat_number ?? '';
+ }
+ public function id_number(Client $client) {
+ return $client->id_number ?? '';
+ }
+ public function public_notes(Client $client) {
+ return $client->public_notes ?? '';
+ }
+ public function custom_value1(Client $client) {
+ return $client->custom_value1 ?? '';
+ }
+ public function custom_value2(Client $client) {
+ return $client->custom_value2 ?? '';
+ }
+ public function custom_value3(Client $client) {
+ return $client->custom_value3 ?? '';
+ }
+ public function custom_value4(Client $client) {
+ return $client->custom_value4 ?? '';
+ }
+ public function payment_balance(Client $client) {
+ return $client->payment_balance ?? 0;
+ }
+ public function credit_balance(Client $client) {
+ return $client->credit_balance ?? 0;
+ }
+ public function classification(Client $client) {
+ ctrans("texts.{$client->classification}") ?? '';
+ }
+
+
+
+ ////////contact details/////////////////
+ /*
+ public function phone(Client $client) {
+
+ }
+ public function first_name(Client $client) {
+
+ }
+ public function last_name(Client $client) {
+
+ }
+ public function email(Client $client) {
+
+ }
+ public function custom_value1(Client $client) {
+
+ }
+ public function custom_value2(Client $client) {
+
+ }
+ public function custom_value3(Client $client) {
+
+ }
+ public function custom_value4(Client $client) {
+
+ }
+ */
+
+
}
diff --git a/app/Export/Decorators/ContactDecorator.php b/app/Export/Decorators/ContactDecorator.php
new file mode 100644
index 0000000000..b6588ce44f
--- /dev/null
+++ b/app/Export/Decorators/ContactDecorator.php
@@ -0,0 +1,20 @@
+ $value = (new ClientDecorator($entity, $key))->transform(),
- ($entity instanceof Payment) => $value = (new PaymentDecorator($entity, $key))->transform(),
- ($entity instanceof Invoice) => $value = (new InvoiceDecorator($entity, $key))->transform(),
- ($entity instanceof RecurringInvoice) => $value = (new RecurringInvoiceDecorator($entity, $key))->transform(),
- ($entity instanceof Credit) => $value = (new CreditDecorator($entity, $key))->transform(),
- ($entity instanceof Quote) => $value = (new QuoteDecorator($entity, $key))->transform(),
- ($entity instanceof Task) => $value = (new TaskDecorator($entity, $key))->transform(),
- ($entity instanceof Expense) => $value = (new ExpenseDecorator($entity, $key))->transform(),
- ($entity instanceof Project) => $value = (new ProjectDecorator($entity, $key))->transform(),
- ($entity instanceof Product) => $value = (new ProductDecorator($entity, $key))->transform(),
- ($entity instanceof Vendor) => $value = (new VendorDecorator($entity, $key))->transform(),
- ($entity instanceof PurchaseOrder) => $value = (new PurchaseOrderDecorator($entity, $key))->transform(),
- default => $value = '',
- };
-
- return $value;
}
+ public function transform(string $key, mixed $entity): mixed
+ {
+ $index = $this->getKeyPart(0, $key);
+ $column = $this->getKeyPart(1, $key);
+
+ return $this->{$index}()->transform($column, $entity);
+
+ }
+
+ public function invoice(): InvoiceDecorator
+ {
+ return new InvoiceDecorator();
+ }
+
+ public function client(): ClientDecorator
+ {
+ return new ClientDecorator();
+ }
+
+ public function contact(): ContactDecorator
+ {
+ return new ContactDecorator();
+ }
+
+ public function vendor_contact(): VendorContactDecorator
+ {
+ return new VendorContactDecorator();
+ }
+
+ public function payment(): PaymentDecorator
+ {
+ return new PaymentDecorator();
+ }
+
+ public function credit(): CreditDecorator
+ {
+ return new CreditDecorator();
+ }
+
+ public function vendor(): VendorDecorator
+ {
+ return new VendorDecorator();
+ }
+
+ public function expense(): ExpenseDecorator
+ {
+ return new ExpenseDecorator();
+ }
+
+ public function product(): ProductDecorator
+ {
+ return new ProductDecorator();
+ }
+
+ public function project(): ProjectDecorator
+ {
+ return new ProjectDecorator();
+ }
+
+ public function task(): TaskDecorator
+ {
+ return new TaskDecorator();
+ }
+
+ public function quote(): QuoteDecorator
+ {
+ return new QuoteDecorator();
+ }
+
+ public function recurring_invoice(): RecurringInvoiceDecorator
+ {
+ return new RecurringInvoiceDecorator();
+ }
+
+ public function purchase_order(): PurchaseOrderDecorator
+ {
+ return new PurchaseOrderDecorator();
+ }
+
+ public function getKeyPart(int $index, string $key): string
+ {
+ $parts = explode('.', $key);
+
+ return $parts[$index];
+ }
}
diff --git a/app/Export/Decorators/DecoratorInterface.php b/app/Export/Decorators/DecoratorInterface.php
index a19b231f8f..cd8b6fa035 100644
--- a/app/Export/Decorators/DecoratorInterface.php
+++ b/app/Export/Decorators/DecoratorInterface.php
@@ -12,5 +12,5 @@
namespace App\Export\Decorators;
interface DecoratorInterface {
- public function transform(): string;
+ public function transform(string $key, mixed $entity): mixed;
}
diff --git a/app/Export/Decorators/ExpenseDecorator.php b/app/Export/Decorators/ExpenseDecorator.php
index 828d28690e..718a442855 100644
--- a/app/Export/Decorators/ExpenseDecorator.php
+++ b/app/Export/Decorators/ExpenseDecorator.php
@@ -13,7 +13,7 @@ namespace App\Export\Decorators;
class ExpenseDecorator implements DecoratorInterface
{
- public function transform(): string
+ public function transform(string $key, mixed $entity): mixed
{
return 'Payment Decorator';
}
diff --git a/app/Export/Decorators/InvoiceDecorator.php b/app/Export/Decorators/InvoiceDecorator.php
index 2898965c7a..f3eb80c4c8 100644
--- a/app/Export/Decorators/InvoiceDecorator.php
+++ b/app/Export/Decorators/InvoiceDecorator.php
@@ -11,9 +11,9 @@
namespace App\Export\Decorators;
-class InvoiceDecorator implements DecoratorInterface
+class InvoiceDecorator extends Decorator implements DecoratorInterface
{
- public function transform(): string
+ public function transform(string $key, mixed $entity): mixed
{
return 'Payment Decorator';
}
diff --git a/app/Export/Decorators/PaymentDecorator.php b/app/Export/Decorators/PaymentDecorator.php
index 9a287d4a08..f9af06b182 100644
--- a/app/Export/Decorators/PaymentDecorator.php
+++ b/app/Export/Decorators/PaymentDecorator.php
@@ -11,10 +11,132 @@
namespace App\Export\Decorators;
-class PaymentDecorator implements DecoratorInterface{
+use App\Models\Payment;
- public function transform(): string
+class PaymentDecorator extends Decorator implements DecoratorInterface{
+
+ private $entity_key = 'payment';
+
+ public function transform(string $key, $entity): mixed
{
- return 'Payment Decorator';
+ $payment = false;
+
+ if($entity instanceof Payment){
+ $payment = $entity;
+ }
+ elseif($entity->payment) {
+ $payment = $entity->payment;
+ }
+
+ if($key == 'amount' && (!$entity instanceof Payment)){
+ return $entity->payments()->exists() ? $entity->payments()->withoutTrashed()->sum('paymentables.amount') : ctrans('texts.unpaid');
+ }
+ elseif($key == 'refunded' && (!$entity instanceof Payment)) {
+ return $entity->payments()->exists() ? $entity->payments()->withoutTrashed()->sum('paymentables.refunded') : '';
+ }
+ elseif($key == 'applied' && (!$entity instanceof Payment)) {
+ $refunded = $entity->payments()->withoutTrashed()->sum('paymentables.refunded');
+ $amount = $entity->payments()->withoutTrashed()->sum('paymentables.amount');
+ return $entity->payments()->withoutTrashed()->exists() ? ($amount - $refunded) : '';
+ }
+
+ if($payment && method_exists($this, $key)) {
+ return $this->{$key}($payment);
+ }
+
+ return '';
}
+
+ public function date(Payment $payment) {
+ return $payment->date ?? '';
+ }
+
+ public function amount(Payment $payment) {
+ return $payment->amount ?? '';
+ }
+
+ public function refunded(Payment $payment) {
+ return $payment->refunded ?? '';
+ }
+
+ public function applied(Payment $payment) {
+ return $payment->applied ?? '';
+ }
+ public function transaction_reference(Payment $payment) {
+ return $payment->transaction_reference ?? '';
+ }
+ public function currency(Payment $payment) {
+ return $payment->currency()->exists() ? $payment->currency->code : $payment->company->currency()->code;
+ }
+
+ public function exchange_rate(Payment $payment) {
+ return $payment->exchange_rate ?? 1;
+ }
+
+ public function number(Payment $payment) {
+ return $payment->number ?? '';
+ }
+
+ public function method(Payment $payment) {
+ return $payment->translatedType();
+ }
+
+ public function status(Payment $payment) {
+ return $payment->stringStatus($payment->status_id);
+ }
+
+ public function private_notes(Payment $payment) {
+ return strip_tags($payment->private_notes) ?? '';
+ }
+
+ public function custom_value1(Payment $payment) {
+ return $payment->custom_value1 ?? '';
+ }
+
+ public function custom_value2(Payment $payment) {
+ return $payment->custom_value2 ?? '';
+ }
+
+ public function custom_value3(Payment $payment) {
+ return $payment->custom_value3 ?? '';
+ }
+
+ public function custom_value4(Payment $payment) {
+ return $payment->custom_value4 ?? '';
+ }
+
+ public function user_id(Payment $payment) {
+ return $payment->user ? $payment->user->present()->name() : '';
+ }
+
+ public function assigned_user_id(Payment $payment) {
+ return $payment->assigned_user ? $payment->assigned_user->present()->name() : '';
+ }
+
+ public function project_id(Payment $payment) {
+ return $payment->project()->exists() ? $payment->project->name : '';
+ }
+
+ ///////////////////////////////////////////////////
+
+ public function vendor_id(Payment $payment){
+ return $payment->vendor()->exists() ? $payment->vendor->name : '';
+ }
+
+ public function exchange_currency(Payment $payment){
+ return $payment->exchange_currency()->exists() ? $payment->exchange_currency->code : '';
+ }
+
+ public function gateway_type_id(Payment $payment) {
+ return $payment->gateway_type ? $payment->gateway_type->name : 'Unknown Type';
+ }
+
+ public function client_id(Payment $payment) {
+ return $payment->client->present()->name();
+ }
+
+ public function type_id(Payment $payment) {
+ return $payment->translatedType();
+ }
+
}
\ No newline at end of file
diff --git a/app/Export/Decorators/ProductDecorator.php b/app/Export/Decorators/ProductDecorator.php
index b22d940fbe..d7b9fc7295 100644
--- a/app/Export/Decorators/ProductDecorator.php
+++ b/app/Export/Decorators/ProductDecorator.php
@@ -13,7 +13,7 @@ namespace App\Export\Decorators;
class ProductDecorator implements DecoratorInterface
{
- public function transform(): string
+ public function transform(string $key, mixed $entity): mixed
{
return 'Payment Decorator';
}
diff --git a/app/Export/Decorators/ProjectDecorator.php b/app/Export/Decorators/ProjectDecorator.php
index 62b5d8ee91..91ce5b87ff 100644
--- a/app/Export/Decorators/ProjectDecorator.php
+++ b/app/Export/Decorators/ProjectDecorator.php
@@ -13,7 +13,7 @@ namespace App\Export\Decorators;
class ProjectDecorator implements DecoratorInterface
{
- public function transform(): string
+ public function transform(string $key, mixed $entity): mixed
{
return 'Payment Decorator';
}
diff --git a/app/Export/Decorators/PurchaseOrderDecorator.php b/app/Export/Decorators/PurchaseOrderDecorator.php
index c5d8dd3a34..b25f7001d5 100644
--- a/app/Export/Decorators/PurchaseOrderDecorator.php
+++ b/app/Export/Decorators/PurchaseOrderDecorator.php
@@ -11,9 +11,9 @@
namespace App\Export\Decorators;
-class PurchaseOrderDecorator implements DecoratorInterface
+class PurchaseOrderDecorator extends Decorator implements DecoratorInterface
{
- public function transform(): string
+ public function transform(string $key, mixed $entity): mixed
{
return 'Payment Decorator';
}
diff --git a/app/Export/Decorators/QuoteDecorator.php b/app/Export/Decorators/QuoteDecorator.php
index 96161c96d4..49e073452c 100644
--- a/app/Export/Decorators/QuoteDecorator.php
+++ b/app/Export/Decorators/QuoteDecorator.php
@@ -11,9 +11,9 @@
namespace App\Export\Decorators;
-class QuoteDecorator implements DecoratorInterface
+class QuoteDecorator extends Decorator implements DecoratorInterface
{
- public function transform(): string
+ public function transform(string $key, mixed $entity): mixed
{
return 'Payment Decorator';
}
diff --git a/app/Export/Decorators/RecurringInvoiceDecorator.php b/app/Export/Decorators/RecurringInvoiceDecorator.php
index d14e6f705e..02cd646b5d 100644
--- a/app/Export/Decorators/RecurringInvoiceDecorator.php
+++ b/app/Export/Decorators/RecurringInvoiceDecorator.php
@@ -11,9 +11,9 @@
namespace App\Export\Decorators;
-class RecurringInvoiceDecorator implements DecoratorInterface
+class RecurringInvoiceDecorator extends Decorator implements DecoratorInterface
{
- public function transform(): string
+ public function transform(string $key, mixed $entity): mixed
{
return 'Payment Decorator';
}
diff --git a/app/Export/Decorators/TaskDecorator.php b/app/Export/Decorators/TaskDecorator.php
index 60005ad59a..a6f8254c66 100644
--- a/app/Export/Decorators/TaskDecorator.php
+++ b/app/Export/Decorators/TaskDecorator.php
@@ -11,9 +11,9 @@
namespace App\Export\Decorators;
-class TaskDecorator implements DecoratorInterface
+class TaskDecorator extends Decorator implements DecoratorInterface
{
- public function transform(): string
+ public function transform(string $key, mixed $entity): mixed
{
return 'Payment Decorator';
}
diff --git a/app/Export/Decorators/VendorContactDecorator.php b/app/Export/Decorators/VendorContactDecorator.php
new file mode 100644
index 0000000000..0e1b1d26ad
--- /dev/null
+++ b/app/Export/Decorators/VendorContactDecorator.php
@@ -0,0 +1,20 @@
+format('Y-m-d H:i:s');
+
$client = auth()->guard('contact')->user()->client;
+ $client->private_notes = $trial_started;
$client->fill($request->all());
$client->save();
@@ -150,6 +153,7 @@ class NinjaPlanController extends Controller
$account->plan_expires = now()->addDays(14);
$account->is_trial=true;
$account->hosted_company_count = 10;
+ $account->trial_started = now();
$account->save();
}
diff --git a/app/Http/Controllers/CompanyGatewayController.php b/app/Http/Controllers/CompanyGatewayController.php
index 9914327f52..990a886f90 100644
--- a/app/Http/Controllers/CompanyGatewayController.php
+++ b/app/Http/Controllers/CompanyGatewayController.php
@@ -194,21 +194,27 @@ class CompanyGatewayController extends BaseController
*/
public function store(StoreCompanyGatewayRequest $request)
{
- $company_gateway = CompanyGatewayFactory::create(auth()->user()->company()->id, auth()->user()->id);
+ /** @var \App\Models\User $user */
+ $user = auth()->user();
+
+ $company_gateway = CompanyGatewayFactory::create($user->company()->id, $user->id);
$company_gateway->fill($request->all());
$company_gateway->save();
- /*Always ensure at least one fees and limits object is set per gateway*/
- if (! isset($company_gateway->fees_and_limits)) {
- $gateway_types = $company_gateway->driver(new Client)->gatewayTypes();
-
- $fees_and_limits = new \stdClass;
- $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
-
- $company_gateway->fees_and_limits = $fees_and_limits;
- $company_gateway->save();
+ /*Always ensure at least one fees and limits object is set per gateway*/
+ $gateway_types = $company_gateway->driver(new Client)->getAvailableMethods();
+
+ $fees_and_limits = $company_gateway->fees_and_limits;
+
+ foreach($gateway_types as $key => $gateway_type)
+ {
+ if(!property_exists($fees_and_limits, $key))
+ $fees_and_limits->{$key} = new FeesAndLimits;
}
+ $company_gateway->fees_and_limits = $fees_and_limits;
+ $company_gateway->save();
+
ApplePayDomain::dispatch($company_gateway, $company_gateway->company->db);
if (in_array($company_gateway->gateway_key, $this->stripe_keys)) {
@@ -381,10 +387,18 @@ class CompanyGatewayController extends BaseController
{
$company_gateway->fill($request->all());
- if (! $request->has('fees_and_limits')) {
- $company_gateway->fees_and_limits = '';
+ /*Always ensure at least one fees and limits object is set per gateway*/
+ $gateway_types = $company_gateway->driver(new Client)->getAvailableMethods();
+
+ $fees_and_limits = $company_gateway->fees_and_limits;
+
+ foreach($gateway_types as $key => $gateway_type) {
+ if(!property_exists($fees_and_limits, $key)) {
+ $fees_and_limits->{$key} = new FeesAndLimits;
+ }
}
+ $company_gateway->fees_and_limits = $fees_and_limits;
$company_gateway->save();
if($company_gateway->gateway_key == $this->checkout_key) {
diff --git a/app/Http/Requests/CompanyGateway/StoreCompanyGatewayRequest.php b/app/Http/Requests/CompanyGateway/StoreCompanyGatewayRequest.php
index 5f148f5043..d007de2427 100644
--- a/app/Http/Requests/CompanyGateway/StoreCompanyGatewayRequest.php
+++ b/app/Http/Requests/CompanyGateway/StoreCompanyGatewayRequest.php
@@ -11,6 +11,7 @@
namespace App\Http\Requests\CompanyGateway;
+use App\DataMapper\FeesAndLimits;
use App\Http\Requests\Request;
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
use App\Models\Gateway;
@@ -28,7 +29,10 @@ class StoreCompanyGatewayRequest extends Request
*/
public function authorize() : bool
{
- return auth()->user()->isAdmin();
+ /** @var \App\Models\User $user */
+ $user = auth()->user();
+
+ return $user->isAdmin();
}
public function rules()
@@ -64,6 +68,7 @@ class StoreCompanyGatewayRequest extends Request
if (isset($input['fees_and_limits'])) {
$input['fees_and_limits'] = $this->cleanFeesAndLimits($input['fees_and_limits']);
}
+
}
$this->replace($input);
diff --git a/app/Http/Requests/CompanyGateway/UpdateCompanyGatewayRequest.php b/app/Http/Requests/CompanyGateway/UpdateCompanyGatewayRequest.php
index 465449cce2..3949bdb804 100644
--- a/app/Http/Requests/CompanyGateway/UpdateCompanyGatewayRequest.php
+++ b/app/Http/Requests/CompanyGateway/UpdateCompanyGatewayRequest.php
@@ -27,7 +27,10 @@ class UpdateCompanyGatewayRequest extends Request
*/
public function authorize()
{
- return auth()->user()->isAdmin();
+ /** @var \App\Models\User $user */
+ $user = auth()->user();
+
+ return $user->isAdmin();
}
public function rules()
diff --git a/app/Jobs/Account/CreateAccount.php b/app/Jobs/Account/CreateAccount.php
index 4dc50f4a2a..7c487ca8fe 100644
--- a/app/Jobs/Account/CreateAccount.php
+++ b/app/Jobs/Account/CreateAccount.php
@@ -92,6 +92,8 @@ class CreateAccount
$spaa9f78 = (new CreateUser($this->request, $sp794f3f, $sp035a66, true))->handle();
+ $sp035a66->service()->localizeCompany($spaa9f78);
+
(new CreateCompanyPaymentTerms($sp035a66, $spaa9f78))->handle();
(new CreateCompanyTaskStatuses($sp035a66, $spaa9f78))->handle();
@@ -124,8 +126,6 @@ class CreateAccount
NinjaMailerJob::dispatch($nmo, true);
- // \Modules\Admin\Jobs\Account\NinjaUser::dispatch([], $sp035a66);
-
(new \Modules\Admin\Jobs\Account\NinjaUser([], $sp035a66))->handle();
}
diff --git a/app/Jobs/Company/CreateCompany.php b/app/Jobs/Company/CreateCompany.php
index a9377fa478..984c29993c 100644
--- a/app/Jobs/Company/CreateCompany.php
+++ b/app/Jobs/Company/CreateCompany.php
@@ -84,6 +84,7 @@ class CreateCompany
match($settings->country_id) {
'724' => $company = $this->spanishSetup($company),
'36' => $company = $this->australiaSetup($company),
+ '710' => $company = $this->southAfticaSetup($company),
default => $company->save(),
};
@@ -103,7 +104,7 @@ class CreateCompany
if(request()->hasHeader('cf-ipcountry')) {
- $c = Country::where('iso_3166_2', request()->header('cf-ipcountry'))->first();
+ $c = Country::query()->where('iso_3166_2', request()->header('cf-ipcountry'))->first();
if($c) {
return (string)$c->id;
@@ -115,7 +116,7 @@ class CreateCompany
if($details && property_exists($details, 'countryCode')) {
- $c = Country::where('iso_3166_2', $details->countryCode)->first();
+ $c = Country::query()->where('iso_3166_2', $details->countryCode)->first();
if($c) {
return (string)$c->id;
@@ -153,15 +154,6 @@ class CreateCompany
$company->save();
- //user does not exist yet.
- // MultiDB::setDb($company->db);
- // $user = \App\Models\User::where('account_id', $company->account_id)->first();
-
- // $tax_rate = TaxRateFactory::create($company->id, $user->id);
- // $tax_rate->name = $company->tax_data->regions->EU->subregions->ES->tax_name;
- // $tax_rate->rate = $company->tax_data->regions->EU->subregions->ES->tax_rate;
- // $tax_rate->save();
-
return $company;
} catch(\Exception $e) {
@@ -174,6 +166,40 @@ class CreateCompany
}
+ private function southAfticaSetup(Company $company): Company
+ {
+
+ try {
+
+ $company->enabled_item_tax_rates = 1;
+ $company->enabled_tax_rates = 1;
+
+ $translations = new \stdClass;
+ $translations->invoice = "Tax Invoice";
+
+ $settings = $company->settings;
+ $settings->currency_id = '4';
+ $settings->timezone_id = '56';
+ $settings->translations = $translations;
+
+ $company->settings = $settings;
+
+ $company->save();
+
+ return $company;
+
+ } catch(\Exception $e) {
+ nlog($e->getMessage());
+ nlog("SETUP: could not complete setup for South African Locale");
+ }
+
+ $company->save();
+
+ return $company;
+
+
+ }
+
private function australiaSetup(Company $company): Company
{
try {
@@ -193,17 +219,6 @@ class CreateCompany
$company->save();
- //$user = $company->account->users()->first();
- //user does not exist yet.
- // MultiDB::setDb($company->db);
- // $user = \App\Models\User::where('account_id', $company->account_id)->first();
-
-
- // $tax_rate = TaxRateFactory::create($company->id, $user->id);
- // $tax_rate->name = $company->tax_data->regions->AU->subregions->AU->tax_name;
- // $tax_rate->rate = $company->tax_data->regions->AU->subregions->AU->tax_rate;
- // $tax_rate->save();
-
return $company;
} catch(\Exception $e) {
diff --git a/app/Jobs/Expense/VendorExpenseNotify.php b/app/Jobs/Expense/VendorExpenseNotify.php
new file mode 100644
index 0000000000..16a6b83a33
--- /dev/null
+++ b/app/Jobs/Expense/VendorExpenseNotify.php
@@ -0,0 +1,99 @@
+db);
+
+ $this->expense->vendor->contacts->filter(function(VendorContact $contact){
+ return $contact->send_email && $contact->email;
+ })->each(function(VendorContact $contact){
+ $this->notify($contact);
+ });
+ }
+
+ private function notify(VendorContact $contact)
+ {
+
+ $mo = new EmailObject;
+ $mo->contact = $contact;
+ $mo->vendor_contact_id = $contact->id;
+ $mo->user_id = $this->expense->user_id;
+ $mo->company_key = $this->expense->company->company_key;
+ $mo->subject = ctrans('texts.vendor_notification_subject', [
+ 'amount' => Number::formatMoney($this->expense->amount, $contact->vendor),
+ 'vendor' => $contact->vendor->present()->name(),
+ ]);
+ $mo->body = ctrans('texts.vendor_notification_body', [
+ 'vendor' => $this->expense->vendor->present()->name(),
+ 'amount' => Number::formatMoney($this->expense->amount, $contact->vendor),
+ 'contact' => $contact->present()->name(),
+ 'payment_date' => $this->translateDate($this->expense->payment_date, $this->expense->company->date_format(), $this->expense->vendor->locale()),
+ 'transaction_reference' => $this->expense->transaction_reference ?? '',
+ 'number' => $this->expense->number,
+ ]);
+ $mo->template = '';
+ $mo->email_template_body = 'vendor_notification_subject';
+ $mo->email_template_subject = 'vendor_notification_body';
+ $mo->vendor_id = $contact->vendor_id ?? null;
+ $mo->variables = [
+ 'amount' => Number::formatMoney($this->expense->amount, $contact->vendor),
+ 'contact' => $contact->present()->name(),
+ 'vendor' => $contact->vendor->present()->name(),
+ 'payment_date' => $this->translateDate($this->expense->payment_date, $this->expense->company->date_format(), $this->expense->vendor->locale()),
+ 'transaction_reference' => $this->expense->transaction_reference ?? '',
+ 'number' => $this->expense->number,
+ ];
+
+ Email::dispatch($mo, $this->expense->company);
+
+ $fields = new \stdClass();
+ $fields->expense_id = $this->expense->id;
+ $fields->vendor_id = $contact->vendor_id;
+ $fields->vendor_contact_id = $contact->id;
+ $fields->user_id = $this->expense->user_id;
+ $fields->company_id = $contact->company_id;
+ $fields->activity_type_id = Activity::VENDOR_NOTIFICATION_EMAIL;
+ $fields->account_id = $this->expense->company->account_id;
+
+ $activity_repo = new ActivityRepository();
+ $activity_repo->save($fields, $this->expense, Ninja::eventVars());
+
+ }
+}
\ No newline at end of file
diff --git a/app/Listeners/LogRequestSending.php b/app/Listeners/LogRequestSending.php
new file mode 100644
index 0000000000..c123ef8092
--- /dev/null
+++ b/app/Listeners/LogRequestSending.php
@@ -0,0 +1,43 @@
+request->headers());
+ nlog($event->request->url());
+ nlog(json_encode($event->request->headers()));
+ nlog($event->request->body());
+
+ }
+}
diff --git a/app/Listeners/LogResponseReceived.php b/app/Listeners/LogResponseReceived.php
new file mode 100644
index 0000000000..77dff20299
--- /dev/null
+++ b/app/Listeners/LogResponseReceived.php
@@ -0,0 +1,56 @@
+request->headers());
+ nlog($event->request->url());
+ nlog(json_encode($event->request->headers()));
+ nlog($event->request->body());
+
+ nlog("Response");
+ nlog($event->response->headers());
+ nlog(json_encode($event->response->headers()));
+ nlog($event->response->body());
+ nlog($event->response->json());
+ }
+}
diff --git a/app/Models/Activity.php b/app/Models/Activity.php
index e8b1a7f05d..311ee6b614 100644
--- a/app/Models/Activity.php
+++ b/app/Models/Activity.php
@@ -257,6 +257,8 @@ class Activity extends StaticModel
const PAYMENT_EMAILED = 138;
+ const VENDOR_NOTIFICATION_EMAIL = 139;
+
protected $casts = [
'is_system' => 'boolean',
'updated_at' => 'timestamp',
diff --git a/app/Models/Company.php b/app/Models/Company.php
index d177a8108f..9b3096be8b 100644
--- a/app/Models/Company.php
+++ b/app/Models/Company.php
@@ -23,6 +23,7 @@ use Laracasts\Presenter\PresentableTrait;
use App\Utils\Traits\CompanySettingsSaver;
use Illuminate\Notifications\Notification;
use App\Models\Presenters\CompanyPresenter;
+use App\Services\Company\CompanyService;
use App\Services\Notification\NotificationService;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -974,4 +975,9 @@ class Company extends BaseModel
return $this->e_invoice_certificate_passphrase;
}
+ public function service(): CompanyService
+ {
+ return new CompanyService($this);
+ }
+
}
diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php
index ccb25724ef..21b61b9c32 100644
--- a/app/Models/CompanyGateway.php
+++ b/app/Models/CompanyGateway.php
@@ -117,7 +117,6 @@ class CompanyGateway extends BaseModel
16 => ['card' => 'images/credit_cards/Test-Discover-Icon.png', 'text' => 'Discover'],
];
-
// const TYPE_PAYPAL = 300;
// const TYPE_STRIPE = 301;
// const TYPE_LEDGER = 302;
@@ -132,6 +131,7 @@ class CompanyGateway extends BaseModel
// const TYPE_MOLLIE = 312;
// const TYPE_EWAY = 313;
// const TYPE_FORTE = 314;
+ // const PAYPAL_PPCP = 323;
public $gateway_consts = [
'38f2c48af60c7dd69e04248cbb24c36e' => 300,
@@ -150,6 +150,7 @@ class CompanyGateway extends BaseModel
'65faab2ab6e3223dbe848b1686490baz' => 320,
'b9886f9257f0c6ee7c302f1c74475f6c' => 321,
'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9' => 322,
+ '80af24a6a691230bbec33e930ab40666' => 323,
];
protected $touches = [];
diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php
index 681889a68e..d811ea7ed0 100644
--- a/app/Models/Gateway.php
+++ b/app/Models/Gateway.php
@@ -90,7 +90,7 @@ class Gateway extends StaticModel
if ($this->id == 1) {
$link = 'http://reseller.authorize.net/application/?id=5560364';
- } elseif ($this->id == 15) {
+ } elseif (in_array($this->id,[15,60,61])) {
$link = 'https://www.paypal.com/us/cgi-bin/webscr?cmd=_login-api-run';
} elseif ($this->id == 24) {
$link = 'https://www.2checkout.com/referral?r=2c37ac2298';
@@ -202,7 +202,19 @@ class Gateway extends StaticModel
// GatewayType::PRZELEWY24 => ['refund' => false, 'token_billing' => false],
// GatewayType::SOFORT => ['refund' => false, 'token_billing' => false],
]; //Paypal
-
+ case 61:
+ return [
+ GatewayType::PAYPAL => ['refund' => false, 'token_billing' => false],
+ GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => false],
+ GatewayType::VENMO => ['refund' => false, 'token_billing' => false],
+ // GatewayType::SEPA => ['refund' => false, 'token_billing' => false],
+ // GatewayType::BANCONTACT => ['refund' => false, 'token_billing' => false],
+ // GatewayType::EPS => ['refund' => false, 'token_billing' => false],
+ // GatewayType::MYBANK => ['refund' => false, 'token_billing' => false],
+ // GatewayType::PAYLATER => ['refund' => false, 'token_billing' => false],
+ // GatewayType::PRZELEWY24 => ['refund' => false, 'token_billing' => false],
+ // GatewayType::SOFORT => ['refund' => false, 'token_billing' => false],
+ ]; //Paypal PPCP
default:
return [];
}
diff --git a/app/Models/Presenters/ClientPresenter.php b/app/Models/Presenters/ClientPresenter.php
index e9ada6d652..bd3a322e33 100644
--- a/app/Models/Presenters/ClientPresenter.php
+++ b/app/Models/Presenters/ClientPresenter.php
@@ -110,6 +110,11 @@ class ClientPresenter extends EntityPresenter
return $str;
}
+ public function shipping_country_code(): string
+ {
+ return $this->entity->shipping_country ? $this->entity->shipping_country->iso_3166_2 : $this->entity->country->iso_3166_2;
+ }
+
public function phone()
{
return $this->entity->phone ?: '';
diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php
index 781e2b1a7a..082f5280c2 100644
--- a/app/Models/SystemLog.php
+++ b/app/Models/SystemLog.php
@@ -148,6 +148,8 @@ class SystemLog extends Model
const TYPE_RAZORPAY = 322;
+ const TYPE_PAYPAL_PPCP = 323;
+
const TYPE_QUOTA_EXCEEDED = 400;
const TYPE_UPSTREAM_FAILURE = 401;
diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php
index 243a386913..d6365f7e65 100644
--- a/app/Models/Vendor.php
+++ b/app/Models/Vendor.php
@@ -13,6 +13,7 @@ namespace App\Models;
use App\DataMapper\CompanySettings;
use App\Models\Presenters\VendorPresenter;
+use App\Services\Vendor\VendorService;
use App\Utils\Traits\AppSetup;
use App\Utils\Traits\GeneratesCounter;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -280,4 +281,9 @@ class Vendor extends BaseModel
{
return $this->company->company_key.'/'.$this->vendor_hash.'/backups';
}
+
+ public function service()
+ {
+ return new VendorService($this);
+ }
}
diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php
index d0f7456450..ad9493865c 100644
--- a/app/PaymentDrivers/BaseDriver.php
+++ b/app/PaymentDrivers/BaseDriver.php
@@ -108,6 +108,11 @@ class BaseDriver extends AbstractPaymentDriver
return $this;
}
+ public function getAvailableMethods(): array
+ {
+ return self::$methods;
+ }
+
/**
* Required fields for client to fill, to proceed with gateway actions.
*
diff --git a/app/PaymentDrivers/CheckoutCom/CreditCard.php b/app/PaymentDrivers/CheckoutCom/CreditCard.php
index 5436ce35ba..a55f538556 100644
--- a/app/PaymentDrivers/CheckoutCom/CreditCard.php
+++ b/app/PaymentDrivers/CheckoutCom/CreditCard.php
@@ -263,6 +263,16 @@ class CreditCard implements MethodInterface
if (is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
+
+ SystemLogger::dispatch(
+ $error_details,
+ SystemLog::CATEGORY_GATEWAY_RESPONSE,
+ SystemLog::EVENT_GATEWAY_ERROR,
+ SystemLog::TYPE_CHECKOUT,
+ $this->checkout->client,
+ $this->checkout->client->company,
+ );
+
}
$this->checkout->unWindGatewayFees($this->checkout->payment_hash);
@@ -294,7 +304,7 @@ class CreditCard implements MethodInterface
);
return new PaymentFailed($e->getMessage(), $e->getCode());
- // return $this->checkout->processInternallyFailedPayment($this->checkout, $human_exception);
+
} catch (CheckoutAuthorizationException $e) {
// Bad Invalid authorization
diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php
index 7f4fea2ab7..90aac54f0e 100644
--- a/app/PaymentDrivers/CheckoutComPaymentDriver.php
+++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php
@@ -415,7 +415,8 @@ class CheckoutComPaymentDriver extends BaseDriver
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_CHECKOUT,
- $this->client
+ $this->client,
+ $this->client->company,
);
return $payment;
@@ -436,7 +437,8 @@ class CheckoutComPaymentDriver extends BaseDriver
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_CHECKOUT,
- $this->client
+ $this->client,
+ $this->client->company
);
return false;
diff --git a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php
new file mode 100644
index 0000000000..2684aaa1ce
--- /dev/null
+++ b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php
@@ -0,0 +1,484 @@
+ 'paypal',
+ 1 => 'card',
+ 25 => 'venmo',
+ // 9 => 'sepa',
+ // 12 => 'bancontact',
+ // 17 => 'eps',
+ // 15 => 'giropay',
+ // 13 => 'ideal',
+ // 26 => 'mercadopago',
+ // 27 => 'mybank',
+ // 28 => 'paylater',
+ // 16 => 'p24',
+ // 7 => 'sofort'
+ ];
+
+ /**
+ * Return an array of
+ * enabled gateway payment methods
+ *
+ * @return array
+ */
+ public function gatewayTypes(): array
+ {
+
+ return collect($this->company_gateway->fees_and_limits)
+ ->filter(function ($fee){
+ return $fee->is_enabled;
+ })->map(function ($fee, $key){
+ return (int)$key;
+ })->toArray();
+
+ }
+
+ private function getPaymentMethod($gateway_type_id): int
+ {
+ $method = PaymentType::PAYPAL;
+
+ match($gateway_type_id){
+ "1" => $method = PaymentType::CREDIT_CARD_OTHER,
+ "3" => $method = PaymentType::PAYPAL,
+ "25" => $method = PaymentType::VENMO,
+ };
+
+ return $method;
+ }
+
+ private function getFundingOptions():string
+ {
+
+ $enums = [
+ 1 => 'card',
+ 3 => 'paypal',
+ 25 => 'venmo',
+ // 9 => 'sepa',
+ // 12 => 'bancontact',
+ // 17 => 'eps',
+ // 15 => 'giropay',
+ // 13 => 'ideal',
+ // 26 => 'mercadopago',
+ // 27 => 'mybank',
+ // 28 => 'paylater',
+ // 16 => 'p24',
+ // 7 => 'sofort'
+ ];
+
+ $funding_options = '';
+
+ foreach($this->company_gateway->fees_and_limits as $key => $value) {
+
+ if($value->is_enabled) {
+
+ $funding_options .=$enums[$key].',';
+
+ }
+
+ }
+
+ return rtrim($funding_options, ',');
+
+ }
+
+ /**
+ * Initialize the Paypal gateway.
+ *
+ * Attempt to generate and return the access token.
+ *
+ * @return self
+ */
+ public function init(): self
+ {
+
+ $this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com';
+
+ $secret = config('ninja.paypal.secret');
+ $client_id = config('ninja.paypal.client_id');
+
+ if($this->access_token && $this->token_expiry && $this->token_expiry->isFuture())
+ return $this;
+
+ $response = Http::withBasicAuth($client_id, $secret)
+ ->withHeaders(['Content-Type' => 'application/x-www-form-urlencoded'])
+ ->withQueryParameters(['grant_type' => 'client_credentials'])
+ ->post("{$this->api_endpoint_url}/v1/oauth2/token");
+
+ if($response->successful()) {
+ $this->access_token = $response->json()['access_token'];
+ $this->token_expiry = now()->addSeconds($response->json()['expires_in'] - 60);
+ } else {
+ throw new PaymentFailed('Unable to gain access token from Paypal. Check your configuration', 401);
+ }
+
+ return $this;
+
+ }
+
+ public function setPaymentMethod($payment_method_id)
+ {
+ if(!$payment_method_id) {
+ return $this;
+ }
+
+ $this->gateway_type_id = $payment_method_id;
+
+ $this->paypal_payment_method = $this->funding_options[$payment_method_id];
+
+ return $this;
+ }
+
+ public function authorizeView($payment_method)
+ {
+ // PayPal doesn't support direct authorization.
+
+ return $this;
+ }
+
+ public function authorizeResponse($request)
+ {
+ // PayPal doesn't support direct authorization.
+
+ return $this;
+ }
+
+ private function checkPaymentsReceivable(): self
+ {
+
+ if($this->company_gateway->getConfigField('status') != 'activated'){
+
+ if (class_exists(\Modules\Admin\Services\PayPal\PayPalService::class)) {
+ $pp = new \Modules\Admin\Services\PayPal\PayPalService($this->company_gateway->company, $this->company_gateway->user);
+ $pp->updateMerchantStatus($this->company_gateway);
+
+ $this->company_gateway = $this->company_gateway->fresh();
+ $config = $this->company_gateway->getConfig();
+
+ if($config->status == 'activated')
+ return $this;
+
+ }
+
+ throw new PaymentFailed('Unable to accept payments at this time, please contact PayPal for more information.', 401);
+ }
+
+ return $this;
+
+ }
+
+ public function processPaymentView($data)
+ {
+ $this->init()->checkPaymentsReceivable();
+
+ $data['gateway'] = $this;
+ $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]);
+ $this->payment_hash->save();
+
+ $data['client_id'] = config('ninja.paypal.client_id');
+ $data['token'] = $this->getClientToken();
+ $data['order_id'] = $this->createOrder($data);
+ $data['funding_source'] = $this->paypal_payment_method;
+ $data['gateway_type_id'] = $this->gateway_type_id;
+ $data['merchantId'] = $this->company_gateway->getConfigField('merchantId');
+
+ // nlog($data['merchantId']);
+
+ return render('gateways.paypal.ppcp.pay', $data);
+
+ }
+
+ private function getClientToken(): string
+ {
+
+ $r = $this->gatewayRequest('/v1/identity/generate-token', 'post', ['body' => '']);
+
+ if($r->successful())
+ return $r->json()['client_token'];
+
+ throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401);
+
+ }
+
+ public function processPaymentResponse($request)
+ {
+
+ $request['gateway_response'] = str_replace("Error: ", "", $request['gateway_response']);
+ $response = json_decode($request['gateway_response'], true);
+
+ if(isset($response['status']) && $response['status'] == 'COMPLETED' && isset($response['purchase_units'])) {
+
+ $data = [
+ 'payment_type' => $this->getPaymentMethod($request->gateway_type_id),
+ 'amount' => $response['purchase_units'][0]['amount']['value'],
+ 'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'],
+ 'gateway_type_id' => GatewayType::PAYPAL,
+ ];
+
+ $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED);
+
+ SystemLogger::dispatch(
+ ['response' => $response, 'data' => $data],
+ SystemLog::CATEGORY_GATEWAY_RESPONSE,
+ SystemLog::EVENT_GATEWAY_SUCCESS,
+ SystemLog::TYPE_PAYPAL,
+ $this->client,
+ $this->client->company,
+ );
+
+ return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
+
+ } else {
+
+ if(isset($response['headers']) ?? false)
+ unset($response['headers']);
+
+ SystemLogger::dispatch(
+ ['response' => $response],
+ SystemLog::CATEGORY_GATEWAY_RESPONSE,
+ SystemLog::EVENT_GATEWAY_FAILURE,
+ SystemLog::TYPE_PAYPAL,
+ $this->client,
+ $this->client->company,
+ );
+
+ $message = $response['body']['details'][0]['description'] ?? 'Payment failed. Please try again.';
+
+ throw new PaymentFailed($message, 400);
+ }
+ }
+
+ private function paymentSource(): array
+ {
+ /** we only need to support paypal as payment source until as we are only using hosted payment buttons */
+ return $this->injectPayPalPaymentSource();
+
+ }
+
+ /**@deprecated v5.7.54 */
+ private function injectVenmoPaymentSource(): array
+ {
+
+ return [
+ "venmo" => [
+ "email_address" => $this->client->present()->email(),
+ ],
+ ];
+
+ }
+
+ /**@deprecated v5.7.54 */
+ private function injectCardPaymentSource(): array
+ {
+
+ return [
+ "card" => [
+
+ "name" => $this->client->present()->name(),
+ "email_address" => $this->client->present()->email(),
+ "billing_address" => $this->getBillingAddress(),
+ "experience_context"=> [
+ "user_action" => "PAY_NOW"
+ ],
+ ],
+ ];
+
+ }
+
+ private function injectPayPalPaymentSource(): array
+ {
+
+ return [
+ "paypal" => [
+
+ "name" => [
+ "given_name" => $this->client->present()->first_name(),
+ "surname" => $this->client->present()->last_name(),
+ ],
+ "email_address" => $this->client->present()->email(),
+ "address" => $this->getBillingAddress(),
+ "experience_context"=> [
+ "user_action" => "PAY_NOW"
+ ],
+ ],
+ ];
+
+ }
+
+ private function createOrder(array $data): string
+ {
+
+ $_invoice = collect($this->payment_hash->data->invoices)->first();
+
+ $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
+
+ $description = collect($invoice->line_items)->map(function ($item){
+ return $item->notes;
+ })->implode("\n");
+
+ $order = [
+
+ "intent" => "CAPTURE",
+ "payment_source" => $this->paymentSource(),
+ "purchase_units" => [
+ [
+ "description" =>ctrans('texts.invoice_number').'# '.$invoice->number,
+ "invoice_id" => $invoice->number,
+ "payee" => [
+ "merchant_id" => $this->company_gateway->getConfigField('merchantId'),
+ ],
+ "payment_instruction" => [
+ "disbursement_mode" => "INSTANT",
+ ],
+ $this->getShippingAddress(),
+ "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,
+ "description" => substr($description, 0, 127),
+ "quantity" => "1",
+ "unit_amount" => [
+ "currency_code" => $this->client->currency()->code,
+ "value" => (string)$data['amount_with_fee']
+ ],
+ ],
+ ],
+ ],
+ ]
+ ];
+
+
+ if($shipping = $this->getShippingAddress()){
+ $order['purchase_units'][0] = $shipping;
+ }
+
+ $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order);
+
+ nlog($r->json());
+
+ return $r->json()['id'];
+
+ }
+
+ private function getBillingAddress(): array
+ {
+ return
+ [
+ "address_line_1" => $this->client->address1,
+ "address_line_2" => $this->client->address2,
+ "admin_area_2" => $this->client->city,
+ "admin_area_1" => $this->client->state,
+ "postal_code" => $this->client->postal_code,
+ "country_code" => $this->client->country->iso_3166_2,
+ ];
+ }
+
+ private function getShippingAddress(): ?array
+ {
+ return $this->company_gateway->require_shipping_address ?
+ [
+ "shipping" => [
+ "address" =>
+ [
+ "address_line_1" => $this->client->shipping_address1,
+ "address_line_2" => $this->client->shipping_address2,
+ "admin_area_2" => $this->client->shipping_city,
+ "admin_area_1" => $this->client->shipping_state,
+ "postal_code" => $this->client->shipping_postal_code,
+ "country_code" => $this->client->present()->shipping_country_code(),
+ ],
+ ]
+ ]
+ : null;
+
+ }
+
+ public function gatewayRequest(string $uri, string $verb, array $data, ?array $headers = [])
+ {
+ $this->init();
+
+ $r = Http::withToken($this->access_token)
+ ->withHeaders($this->getHeaders($headers))
+ ->{$verb}("{$this->api_endpoint_url}{$uri}", $data);
+
+ if($r->successful()) {
+ return $r;
+ }
+
+ throw new PaymentFailed("Gateway failure - {$r->body()}", 401);
+
+ }
+
+ private function getHeaders(array $headers = []): array
+ {
+ return array_merge([
+ 'Accept' => 'application/json',
+ 'Content-type' => 'application/json',
+ 'Accept-Language' => 'en_US',
+ 'PayPal-Partner-Attribution-Id' => 'invoiceninja_SP_PPCP',
+ 'PayPal-Request-Id' => Str::uuid()->toString(),
+ ], $headers);
+ }
+
+ private function feeCalc($invoice, $invoice_total)
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php
index 336e04f130..d0395ee6b5 100644
--- a/app/PaymentDrivers/StripePaymentDriver.php
+++ b/app/PaymentDrivers/StripePaymentDriver.php
@@ -140,13 +140,13 @@ class StripePaymentDriver extends BaseDriver
return $this;
}
+
/**
* Returns the gateway types.
*/
public function gatewayTypes(): array
{
$types = [
- // GatewayType::CRYPTO,
GatewayType::CREDIT_CARD,
];
diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php
index 8c4448b230..6014514236 100644
--- a/app/Providers/EventServiceProvider.php
+++ b/app/Providers/EventServiceProvider.php
@@ -182,7 +182,6 @@ use App\Listeners\Mail\MailSentListener;
use App\Listeners\Misc\InvitationViewedListener;
use App\Listeners\Payment\PaymentBalanceActivity;
use App\Listeners\Payment\PaymentEmailedActivity;
-use App\Listeners\Payment\PaymentEmailFailureActivity;
use App\Listeners\Payment\PaymentNotification;
use App\Listeners\Payment\PaymentRestoredActivity;
use App\Listeners\PurchaseOrder\CreatePurchaseOrderActivity;
@@ -281,6 +280,13 @@ class EventServiceProvider extends ServiceProvider
*
*/
protected $listen = [
+
+ // RequestSending::class => [
+ // LogRequestSending::class,
+ // ],
+ // ResponseReceived::class => [
+ // LogResponseReceived::class,
+ // ],
AccountCreated::class => [
],
MessageSending::class => [
diff --git a/app/Repositories/ExpenseRepository.php b/app/Repositories/ExpenseRepository.php
index d71006e9bb..307ccdaeec 100644
--- a/app/Repositories/ExpenseRepository.php
+++ b/app/Repositories/ExpenseRepository.php
@@ -18,9 +18,10 @@ use App\Factory\ExpenseFactory;
use App\Models\ExpenseCategory;
use App\Utils\Traits\GeneratesCounter;
use Illuminate\Database\QueryException;
+use App\Jobs\Expense\VendorExpenseNotify;
+use Illuminate\Database\Eloquent\Collection;
use Carbon\Exceptions\InvalidFormatException;
use App\Libraries\Currency\Conversion\CurrencyApi;
-use Illuminate\Database\Eloquent\Collection;
/**
* ExpenseRepository.
@@ -31,6 +32,7 @@ class ExpenseRepository extends BaseRepository
private $completed = true;
+ private $notify_vendor = false;
/**
* Saves the expense and its contacts.
*
@@ -41,10 +43,15 @@ class ExpenseRepository extends BaseRepository
*/
public function save(array $data, Expense $expense): Expense
{
+ /** @var \App\Models\User $user */
+ $user = auth()->user();
+
if(isset($data['payment_date']) && $data['payment_date'] == $expense->payment_date) {
//do nothing
- } elseif(isset($data['payment_date']) && strlen($data['payment_date']) > 1 && $expense->company->notify_vendor_when_paid) {
- nlog("NOT SAME");
+ }
+ elseif(isset($data['payment_date']) && strlen($data['payment_date']) > 1 && $user->company()->notify_vendor_when_paid && ($data['vendor_id'] || $expense->vendor_id)) {
+ nlog("ping");
+ $this->notify_vendor = true;
}
$expense->fill($data);
@@ -63,6 +70,9 @@ class ExpenseRepository extends BaseRepository
$this->saveDocuments($data['documents'], $expense);
}
+ if($this->notify_vendor)
+ VendorExpenseNotify::dispatch($expense, $expense->company->db);
+
return $expense;
}
diff --git a/app/Services/Company/CompanyService.php b/app/Services/Company/CompanyService.php
new file mode 100644
index 0000000000..49d8cb5cb7
--- /dev/null
+++ b/app/Services/Company/CompanyService.php
@@ -0,0 +1,76 @@
+company->settings->country_id) {
+
+ case '36': // Australia
+ $taxes[] = ['name' => 'GST', 'rate' => 10];
+ break;
+ case '40': // Austria
+ $taxes[] = ['name' => 'USt', 'rate' => 20];
+ break;
+ case '56': // Belgium
+ $taxes[] = ['name' => 'BTW', 'rate' => 21];
+ break;
+ case '100': // Bulgaria
+ $taxes[] = ['name' => 'ДДС', 'rate' => 20];
+ break;
+ case '250': // France
+ $taxes[] = ['name' => 'TVA', 'rate' => 20];
+ break;
+ case '276': // Germany
+ $taxes[] = ['name' => 'MwSt', 'rate' => 19];
+ break;
+ case '554': // New Zealand
+ $taxes[] = ['name' => 'GST', 'rate' => 15];
+ break;
+ case '710': // South Africa
+ $taxes[] = ['name' => 'VAT', 'rate' => 15];
+ break;
+ case '724': // Spain
+ $taxes[] = ['name' => 'IVA', 'rate' => 21];
+ break;
+
+ default:
+ return;
+ }
+
+ foreach($taxes as $tax) {
+ $tax_rate = TaxRateFactory::create($this->company->id, $user->id);
+ $tax_rate->fill($tax);
+ $tax_rate->save();
+ }
+
+ }catch(\Exception $e){
+ nlog($e->getMessage());
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php
index 0484b5228a..35413125db 100644
--- a/app/Services/Email/EmailDefaults.php
+++ b/app/Services/Email/EmailDefaults.php
@@ -166,7 +166,6 @@ class EmailDefaults
if (strlen($this->email->email_object->body) > 3) {
// A Custom Message has been set in the email screen.
- // return $this;
} elseif (strlen($this->email->email_object->settings?->{$this->email->email_object->email_template_body}) > 3) {
// A body has been saved in the settings.
$this->email->email_object->body = $this->email->email_object->settings?->{$this->email->email_object->email_template_body};
diff --git a/app/Services/Vendor/VendorService.php b/app/Services/Vendor/VendorService.php
new file mode 100644
index 0000000000..100438957f
--- /dev/null
+++ b/app/Services/Vendor/VendorService.php
@@ -0,0 +1,21 @@
+=7.1.3",
- "symfony/intl": "^5.4|^6.0",
- "twig/twig": "^2.7|^3.0"
+ "php": ">=7.2.5",
+ "symfony/intl": "^5.4|^6.0|^7.0",
+ "twig/twig": "^3.0"
},
"require-dev": {
- "symfony/phpunit-bridge": "^5.4|^6.3"
+ "symfony/phpunit-bridge": "^6.4|^7.0"
},
"type": "library",
"autoload": {
@@ -13567,7 +13566,7 @@
"twig"
],
"support": {
- "source": "https://github.com/twigphp/intl-extra/tree/v3.7.1"
+ "source": "https://github.com/twigphp/intl-extra/tree/v3.8.0"
},
"funding": [
{
@@ -13579,30 +13578,31 @@
"type": "tidelift"
}
],
- "time": "2023-07-29T15:34:56+00:00"
+ "time": "2023-11-21T17:27:48+00:00"
},
{
"name": "twig/twig",
- "version": "v3.7.1",
+ "version": "v3.8.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
- "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554"
+ "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
- "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
+ "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-ctype": "^1.8",
- "symfony/polyfill-mbstring": "^1.3"
+ "symfony/polyfill-mbstring": "^1.3",
+ "symfony/polyfill-php80": "^1.22"
},
"require-dev": {
"psr/container": "^1.0|^2.0",
- "symfony/phpunit-bridge": "^5.4.9|^6.3"
+ "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0"
},
"type": "library",
"autoload": {
@@ -13638,7 +13638,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
- "source": "https://github.com/twigphp/Twig/tree/v3.7.1"
+ "source": "https://github.com/twigphp/Twig/tree/v3.8.0"
},
"funding": [
{
@@ -13650,7 +13650,7 @@
"type": "tidelift"
}
],
- "time": "2023-08-28T11:09:02+00:00"
+ "time": "2023-11-21T18:54:41+00:00"
},
{
"name": "twilio/sdk",
@@ -14825,16 +14825,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
- "version": "v3.38.0",
+ "version": "v3.39.1",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
- "reference": "7e6070026e76aa09d77a47519625c86593fb8e31"
+ "reference": "857046d26b0d92dc13c4be769309026b100b517e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7e6070026e76aa09d77a47519625c86593fb8e31",
- "reference": "7e6070026e76aa09d77a47519625c86593fb8e31",
+ "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/857046d26b0d92dc13c4be769309026b100b517e",
+ "reference": "857046d26b0d92dc13c4be769309026b100b517e",
"shasum": ""
},
"require": {
@@ -14844,16 +14844,16 @@
"ext-tokenizer": "*",
"php": "^7.4 || ^8.0",
"sebastian/diff": "^4.0 || ^5.0",
- "symfony/console": "^5.4 || ^6.0",
- "symfony/event-dispatcher": "^5.4 || ^6.0",
- "symfony/filesystem": "^5.4 || ^6.0",
- "symfony/finder": "^5.4 || ^6.0",
- "symfony/options-resolver": "^5.4 || ^6.0",
+ "symfony/console": "^5.4 || ^6.0 || ^7.0",
+ "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0",
+ "symfony/filesystem": "^5.4 || ^6.0 || ^7.0",
+ "symfony/finder": "^5.4 || ^6.0 || ^7.0",
+ "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0",
"symfony/polyfill-mbstring": "^1.27",
"symfony/polyfill-php80": "^1.27",
"symfony/polyfill-php81": "^1.27",
- "symfony/process": "^5.4 || ^6.0",
- "symfony/stopwatch": "^5.4 || ^6.0"
+ "symfony/process": "^5.4 || ^6.0 || ^7.0",
+ "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0"
},
"require-dev": {
"facile-it/paraunit": "^1.3 || ^2.0",
@@ -14867,8 +14867,8 @@
"phpspec/prophecy": "^1.16",
"phpspec/prophecy-phpunit": "^2.0",
"phpunit/phpunit": "^9.5",
- "symfony/phpunit-bridge": "^6.2.3",
- "symfony/yaml": "^5.4 || ^6.0"
+ "symfony/phpunit-bridge": "^6.2.3 || ^7.0",
+ "symfony/yaml": "^5.4 || ^6.0 || ^7.0"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
@@ -14906,7 +14906,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
- "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.38.0"
+ "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.39.1"
},
"funding": [
{
@@ -14914,7 +14914,7 @@
"type": "github"
}
],
- "time": "2023-11-07T08:44:54+00:00"
+ "time": "2023-11-24T22:59:03+00:00"
},
{
"name": "hamcrest/hamcrest-php",
@@ -15628,16 +15628,16 @@
},
{
"name": "phpstan/phpstan",
- "version": "1.10.41",
+ "version": "1.10.44",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "c6174523c2a69231df55bdc65b61655e72876d76"
+ "reference": "bf84367c53a23f759513985c54ffe0d0c249825b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6174523c2a69231df55bdc65b61655e72876d76",
- "reference": "c6174523c2a69231df55bdc65b61655e72876d76",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/bf84367c53a23f759513985c54ffe0d0c249825b",
+ "reference": "bf84367c53a23f759513985c54ffe0d0c249825b",
"shasum": ""
},
"require": {
@@ -15686,20 +15686,20 @@
"type": "tidelift"
}
],
- "time": "2023-11-05T12:57:57+00:00"
+ "time": "2023-11-21T16:30:46+00:00"
},
{
"name": "phpunit/php-code-coverage",
- "version": "10.1.7",
+ "version": "10.1.9",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "355324ca4980b8916c18b9db29f3ef484078f26e"
+ "reference": "a56a9ab2f680246adcf3db43f38ddf1765774735"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e",
- "reference": "355324ca4980b8916c18b9db29f3ef484078f26e",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/a56a9ab2f680246adcf3db43f38ddf1765774735",
+ "reference": "a56a9ab2f680246adcf3db43f38ddf1765774735",
"shasum": ""
},
"require": {
@@ -15756,7 +15756,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
- "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7"
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.9"
},
"funding": [
{
@@ -15764,7 +15764,7 @@
"type": "github"
}
],
- "time": "2023-10-04T15:34:17+00:00"
+ "time": "2023-11-23T12:23:20+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -17537,16 +17537,16 @@
},
{
"name": "theseer/tokenizer",
- "version": "1.2.1",
+ "version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
- "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
+ "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
- "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
+ "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"shasum": ""
},
"require": {
@@ -17575,7 +17575,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": {
"issues": "https://github.com/theseer/tokenizer/issues",
- "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.2"
},
"funding": [
{
@@ -17583,7 +17583,7 @@
"type": "github"
}
],
- "time": "2021-07-28T10:34:58+00:00"
+ "time": "2023-11-20T00:12:19+00:00"
}
],
"aliases": [],
diff --git a/config/ninja.php b/config/ninja.php
index be246ea105..3f48fd1f53 100644
--- a/config/ninja.php
+++ b/config/ninja.php
@@ -86,6 +86,7 @@ return [
'password' => 'password',
'stripe' => env('STRIPE_KEYS', ''),
'paypal' => env('PAYPAL_KEYS', ''),
+ 'ppcp' => env('PPCP_KEYS', ''),
'paypal_rest' => env('PAYPAL_REST_KEYS', ''),
'authorize' => env('AUTHORIZE_KEYS', ''),
'checkout' => env('CHECKOUT_KEYS', ''),
@@ -223,4 +224,8 @@ return [
'client_id' => env('SHOPIFY_CLIENT_ID', null),
'client_secret' => env('SHOPIFY_CLIENT_SECRET', null),
],
+ 'paypal' => [
+ 'secret' => env('PAYPAL_SECRET', null),
+ 'client_id' => env('PAYPAL_CLIENT_ID', null),
+ ]
];
diff --git a/database/migrations/2023_10_15_204204_add_paypal_ppcp.php b/database/migrations/2023_10_15_204204_add_paypal_ppcp.php
new file mode 100644
index 0000000000..2fdee3ea77
--- /dev/null
+++ b/database/migrations/2023_10_15_204204_add_paypal_ppcp.php
@@ -0,0 +1,49 @@
+clientId = "";
+ $fields->secret = "";
+ $fields->testMode = false;
+
+ $paypal = new Gateway;
+ $paypal->id = 61;
+ $paypal->name = 'PayPal Platform';
+ $paypal->key = '80af24a6a691230bbec33e930ab40666';
+ $paypal->provider = 'PayPal_PPCP';
+ $paypal->is_offsite = false;
+ $paypal->fields = \json_encode($fields);
+ $paypal->visible = 1;
+ $paypal->site_url = 'https://www.paypal.com/';
+ $paypal->save();
+
+ }
+
+ Gateway::whereIn('id', [60, 15, 49])->update(['visible' => 0]);
+
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ //
+ }
+};
diff --git a/database/seeders/PaymentLibrariesSeeder.php b/database/seeders/PaymentLibrariesSeeder.php
index 6803c4bf5f..ef83fddaf2 100644
--- a/database/seeders/PaymentLibrariesSeeder.php
+++ b/database/seeders/PaymentLibrariesSeeder.php
@@ -84,6 +84,7 @@ class PaymentLibrariesSeeder extends Seeder
['id' => 58, 'name' => 'Razorpay', 'provider' => 'Razorpay', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9', 'fields' => '{"apiKey":"","apiSecret":""}'],
['id' => 59, 'name' => 'Forte', 'provider' => 'Forte', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'kivcvjexxvdiyqtj3mju5d6yhpeht2xs', 'fields' => '{"testMode":false,"apiLoginId":"","apiAccessId":"","secureKey":"","authOrganizationId":"","organizationId":"","locationId":""}'],
['id' => 60, 'name' => 'PayPal REST', 'provider' => 'PayPal_Rest', 'key' => '80af24a6a691230bbec33e930ab40665', 'fields' => '{"clientId":"","secret":"","signature":"","testMode":false}'],
+ ['id' => 61, 'name' => 'PayPal Platform', 'provider' => 'PayPal_PPCP', 'key' => '80af24a6a691230bbec33e930ab40666', 'fields' => '{"testMode":false}'],
];
foreach ($gateways as $gateway) {
@@ -103,9 +104,8 @@ class PaymentLibrariesSeeder extends Seeder
Gateway::whereIn('id', [1,3,7,11,15,20,39,46,55,50,57,52,58,59,60])->update(['visible' => 1]);
if (Ninja::isHosted()) {
- Gateway::whereIn('id', [20])->update(['visible' => 0]);
- Gateway::whereIn('id', [56])->update(['visible' => 1]);
- Gateway::whereIn('id', [49])->update(['visible' => 1]);
+ Gateway::whereIn('id', [20,49])->update(['visible' => 0]);
+ Gateway::whereIn('id', [56,61])->update(['visible' => 1]);
}
Gateway::all()->each(function ($gateway) {
diff --git a/lang/en/texts.php b/lang/en/texts.php
index 64be06692e..d1a4d812ad 100644
--- a/lang/en/texts.php
+++ b/lang/en/texts.php
@@ -5213,6 +5213,10 @@ $LANG = array(
'payment_email_all_contacts' => 'Payment Email To All Contacts',
'payment_email_all_contacts_help' => 'Sends the payment email to all contacts when enabled',
'add_line' => 'Add Line',
+ 'activity_139' => 'Expense :expense notification sent to :contact',
+ 'vendor_notification_subject' => 'Confirmation of payment :amount sent to :vendor',
+ 'vendor_notification_body' => 'Payment processed for :amount dated :payment_date.
[Transaction Reference: :transaction_reference]',
+
);
return $LANG;
diff --git a/resources/views/portal/ninja2020/gateways/paypal/ppcp/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/ppcp/pay.blade.php
new file mode 100644
index 0000000000..508d228586
--- /dev/null
+++ b/resources/views/portal/ninja2020/gateways/paypal/ppcp/pay.blade.php
@@ -0,0 +1,69 @@
+@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => 'PayPal'])
+
+@section('gateway_head')
+
+
+@endsection
+
+@section('gateway_content')
+