Merge branch 'invoiceninja:v5-develop' into v5-develop
This commit is contained in:
commit
775a60b71d
|
|
@ -1 +1 @@
|
|||
5.10.16
|
||||
5.10.19
|
||||
|
|
@ -15,7 +15,6 @@ use App\Jobs\Cron\AutoBillCron;
|
|||
use App\Jobs\Cron\RecurringExpensesCron;
|
||||
use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use App\Jobs\Cron\SubscriptionCron;
|
||||
use App\Jobs\Cron\UpdateCalculatedFields;
|
||||
use App\Jobs\Invoice\InvoiceCheckLateWebhook;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
use App\Jobs\Ninja\BankTransactionSync;
|
||||
|
|
@ -33,6 +32,7 @@ use App\Jobs\Util\SchedulerCheck;
|
|||
use App\Jobs\Util\UpdateExchangeRates;
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Models\Account;
|
||||
use App\PaymentDrivers\Rotessa\Jobs\TransactionReport;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
|
@ -65,12 +65,12 @@ class Kernel extends ConsoleKernel
|
|||
/* Checks for scheduled tasks */
|
||||
$schedule->job(new TaskScheduler())->hourlyAt(10)->withoutOverlapping()->name('task-scheduler-job')->onOneServer();
|
||||
|
||||
/* Checks Rotessa Transactions */
|
||||
$schedule->job(new TransactionReport())->dailyAt('01:48')->withoutOverlapping()->name('rotessa-transaction-report')->onOneServer();
|
||||
|
||||
/* Stale Invoice Cleanup*/
|
||||
$schedule->job(new CleanStaleInvoiceOrder())->hourlyAt(30)->withoutOverlapping()->name('stale-invoice-job')->onOneServer();
|
||||
|
||||
/* Stale Invoice Cleanup*/
|
||||
$schedule->job(new UpdateCalculatedFields())->hourlyAt(40)->withoutOverlapping()->name('update-calculated-fields-job')->onOneServer();
|
||||
|
||||
/* Checks for large companies and marked them as is_large */
|
||||
$schedule->job(new CompanySizeCheck())->dailyAt('23:20')->withoutOverlapping()->name('company-size-job')->onOneServer();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataProviders;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\DataProviders;
|
||||
|
||||
use Omnipay\Rotessa\Object\Frequency;
|
||||
|
||||
final class Frequencies
|
||||
{
|
||||
public static function get() : array {
|
||||
return Frequency::getTypes();
|
||||
}
|
||||
|
||||
public static function getFromType() {
|
||||
|
||||
}
|
||||
public static function getOnePayment() {
|
||||
return Frequency::ONCE;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,5 +22,5 @@
|
|||
*/
|
||||
function ctrans(string $string, $replace = [], $locale = null): string
|
||||
{
|
||||
return trans($string, $replace, $locale);
|
||||
return html_entity_decode(trans($string, $replace, $locale));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class NinjaPlanController extends Controller
|
|||
|
||||
$data['intent'] = $setupIntent;
|
||||
$data['client'] = Auth::guard('contact')->user()->client;
|
||||
|
||||
|
||||
return $this->render('plan.trial', $data);
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +88,8 @@ class NinjaPlanController extends Controller
|
|||
{
|
||||
$trial_started = "Trial Started @ ".now()->format('Y-m-d H:i:s');
|
||||
|
||||
auth()->guard('contact')->user()->fill($request->only(['first_name','last_name']))->save();
|
||||
|
||||
$client = auth()->guard('contact')->user()->client;
|
||||
$client->private_notes = $trial_started;
|
||||
$client->fill($request->all());
|
||||
|
|
|
|||
|
|
@ -567,9 +567,9 @@ class CompanyGatewayController extends BaseController
|
|||
{
|
||||
|
||||
//Throttle here
|
||||
if (Cache::has("throttle_polling:import_customers:{$company_gateway->company->company_key}:{$company_gateway->hashed_id}")) {
|
||||
return response()->json(['message' => 'Please wait whilst your previous attempts complete.'], 200);
|
||||
}
|
||||
// if (Cache::has("throttle_polling:import_customers:{$company_gateway->company->company_key}:{$company_gateway->hashed_id}")) {
|
||||
// return response()->json(['message' => 'Please wait whilst your previous attempts complete.'], 200);
|
||||
// }
|
||||
|
||||
dispatch(function () use ($company_gateway) {
|
||||
MultiDB::setDb($company_gateway->company->db);
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ class ExportController extends BaseController
|
|||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$hash = Str::uuid();
|
||||
$hash = Str::uuid()->toString();
|
||||
$url = \Illuminate\Support\Facades\URL::temporarySignedRoute('protected_download', now()->addHour(), ['hash' => $hash]);
|
||||
Cache::put($hash, $url, now()->addHour());
|
||||
Cache::put($hash, $url, 3600);
|
||||
|
||||
CompanyExport::dispatch($user->getCompany(), $user, $hash);
|
||||
|
||||
|
|
|
|||
|
|
@ -298,6 +298,9 @@ class PreviewController extends BaseController
|
|||
->mock();
|
||||
} catch(SyntaxError $e) {
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
return response()->json(['message' => 'invalid data access', 'errors' => ['design.design.body' => $e->getMessage()]], 422);
|
||||
}
|
||||
|
||||
if (request()->query('html') == 'true') {
|
||||
return $ts->getHtml();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace App\Http\Controllers\VendorPortal;
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Models\VendorContact;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\TranslationHelper;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class VendorContactController extends Controller
|
||||
{
|
||||
|
|
@ -58,14 +58,14 @@ class VendorContactController extends Controller
|
|||
'settings' => $vendor_contact->vendor->company->settings,
|
||||
'company' => $vendor_contact->vendor->company,
|
||||
'sidebar' => $this->sidebarMenu(),
|
||||
'countries' => TranslationHelper::getCountries(),
|
||||
'countries' => app('countries'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(VendorContact $vendor_contact)
|
||||
public function update(Request $request, VendorContact $vendor_contact)
|
||||
{
|
||||
$vendor_contact->fill(request()->all());
|
||||
$vendor_contact->vendor->fill(request()->all());
|
||||
$vendor_contact->fill($request->all());
|
||||
$vendor_contact->vendor->fill($request->all());
|
||||
$vendor_contact->push();
|
||||
|
||||
return back()->withSuccess(ctrans('texts.profile_updated_successfully'));
|
||||
|
|
@ -76,16 +76,10 @@ class VendorContactController extends Controller
|
|||
$enabled_modules = auth()->guard('vendor')->user()->company->enabled_modules;
|
||||
$data = [];
|
||||
|
||||
// TODO: Enable dashboard once it's completed.
|
||||
// $this->settings->enable_client_portal_dashboard
|
||||
// $data[] = [ 'title' => ctrans('texts.dashboard'), 'url' => 'client.dashboard', 'icon' => 'activity'];
|
||||
|
||||
if (self::MODULE_PURCHASE_ORDERS & $enabled_modules) {
|
||||
$data[] = ['title' => ctrans('texts.purchase_orders'), 'url' => 'vendor.purchase_orders.index', 'icon' => 'file-text'];
|
||||
}
|
||||
|
||||
// $data[] = ['title' => ctrans('texts.documents'), 'url' => 'client.documents.index', 'icon' => 'download'];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class ContactRegister
|
|||
// As a fallback for self-hosted, it will use default company in the system
|
||||
// if key isn't provided in the url.
|
||||
if (! $request->route()->parameter('company_key') && Ninja::isSelfHost()) {
|
||||
$company = Account::query()->first()->default_company;
|
||||
$company = Account::query()->first()->default_company ?? Account::query()->first()->companies->first();
|
||||
|
||||
if (! $company->client_can_register) {
|
||||
abort(400, 'Registration disabled');
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@ namespace App\Http\Middleware;
|
|||
|
||||
use Closure;
|
||||
use Illuminate\Cache\RateLimiter;
|
||||
use Illuminate\Contracts\Redis\Factory as Redis;
|
||||
use Illuminate\Redis\Limiters\DurationLimiter;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
|
||||
class ThrottleRequestsWithPredis extends ThrottleRequests
|
||||
class ThrottleRequestsWithPredis extends \Illuminate\Routing\Middleware\ThrottleRequests
|
||||
{
|
||||
/**
|
||||
* The Redis factory implementation.
|
||||
*
|
||||
* @var \Illuminate\Redis\Connections\Connection
|
||||
* @var \Illuminate\Contracts\Redis\Factory
|
||||
*/
|
||||
protected $redis;
|
||||
|
||||
|
|
@ -32,14 +32,14 @@ class ThrottleRequestsWithPredis extends ThrottleRequests
|
|||
|
||||
/**
|
||||
* Create a new request throttler.
|
||||
*
|
||||
* @param \Illuminate\Cache\RateLimiter $limiter
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(RateLimiter $limiter)
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
public function __construct(RateLimiter $limiter, Redis $redis)
|
||||
{
|
||||
parent::__construct($limiter);
|
||||
|
||||
/** @phpstan-ignore-next-line */
|
||||
$this->redis = \Illuminate\Support\Facades\Redis::connection('sentinel-cache');
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ class ThrottleRequestsWithPredis extends ThrottleRequests
|
|||
protected function handleRequest($request, Closure $next, array $limits)
|
||||
{
|
||||
foreach ($limits as $limit) {
|
||||
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decayMinutes)) {
|
||||
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decaySeconds)) {
|
||||
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
|
||||
}
|
||||
}
|
||||
|
|
@ -79,16 +79,13 @@ class ThrottleRequestsWithPredis extends ThrottleRequests
|
|||
*
|
||||
* @param string $key
|
||||
* @param int $maxAttempts
|
||||
* @param int $decayMinutes
|
||||
* @param int $decaySeconds
|
||||
* @return mixed
|
||||
*/
|
||||
protected function tooManyAttempts($key, $maxAttempts, $decayMinutes)
|
||||
protected function tooManyAttempts($key, $maxAttempts, $decaySeconds)
|
||||
{
|
||||
$limiter = new DurationLimiter(
|
||||
$this->redis,
|
||||
$key,
|
||||
$maxAttempts,
|
||||
$decayMinutes * 60
|
||||
$this->getRedisConnection(), $key, $maxAttempts, $decaySeconds
|
||||
);
|
||||
|
||||
return tap(! $limiter->acquire(), function () use ($key, $limiter) {
|
||||
|
|
@ -121,4 +118,13 @@ class ThrottleRequestsWithPredis extends ThrottleRequests
|
|||
{
|
||||
return $this->decaysAt[$key] - $this->currentTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Redis connection that should be used for throttling.
|
||||
*
|
||||
*/
|
||||
protected function getRedisConnection()
|
||||
{
|
||||
return $this->redis;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,12 +31,8 @@ class TwigLint implements ValidationRule
|
|||
try {
|
||||
$twig->parse($twig->tokenize(new \Twig\Source(preg_replace('/<!--.*?-->/s', '', $value), '')));
|
||||
} catch (\Twig\Error\SyntaxError $e) {
|
||||
// echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
|
||||
nlog($e->getMessage());
|
||||
$fail($e->getMessage());
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\ViewComposers\Components\Rotessa;
|
||||
|
||||
|
|
@ -27,16 +36,13 @@ class AccountComponent extends Component
|
|||
'routing_number' => null,
|
||||
'institution_number' => null,
|
||||
'transit_number' => null,
|
||||
'bank_name' => ' ',
|
||||
'bank_name' => null,
|
||||
'account_number' => null,
|
||||
'country' => 'US',
|
||||
"authorization_type" => 'Online'
|
||||
];
|
||||
|
||||
public array $account;
|
||||
|
||||
public function __construct(array $account) {
|
||||
$this->account = $account;
|
||||
public function __construct(public array $account) {
|
||||
$this->attributes = $this->newAttributeBag(Arr::only($this->account, $this->fields) );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\ViewComposers\Components\Rotessa;
|
||||
|
||||
|
|
@ -25,10 +34,7 @@ class AddressComponent extends Component
|
|||
'country' => 'US'
|
||||
];
|
||||
|
||||
public array $address;
|
||||
|
||||
public function __construct(array $address) {
|
||||
$this->address = $address;
|
||||
public function __construct(public array $address) {
|
||||
if(strlen($this->address['state']) > 2 ) {
|
||||
$this->address['state'] = $this->address['country'] == 'US' ? array_search($this->address['state'], USStates::$states) : CAProvinces::getAbbreviation($this->address['state']);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\ViewComposers\Components\Rotessa;
|
||||
|
||||
|
|
@ -18,9 +27,9 @@ class ContactComponent extends Component
|
|||
|
||||
$contact = collect($contact->client->contacts->firstWhere('is_primary', 1)->toArray())->merge([
|
||||
'home_phone' =>$contact->client->phone,
|
||||
'custom_identifier' => $contact->client->number,
|
||||
'custom_identifier' => $contact->client->client_hash,
|
||||
'name' =>$contact->client->name,
|
||||
'id' => $contact->client->contact_key,
|
||||
'id' => null,
|
||||
] )->all();
|
||||
|
||||
$this->attributes = $this->newAttributeBag(Arr::only($contact, $this->fields) );
|
||||
|
|
|
|||
|
|
@ -1,123 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\ViewComposers\Components;
|
||||
|
||||
use App\DataProviders\CAProvinces;
|
||||
use App\DataProviders\USStates;
|
||||
use Illuminate\View\Component;
|
||||
use App\Models\ClientContact;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\View\View;
|
||||
|
||||
|
||||
// Contact Component
|
||||
class ContactComponent extends Component
|
||||
{
|
||||
|
||||
public function __construct(ClientContact $contact) {
|
||||
$contact = collect($contact->client->contacts->firstWhere('is_primary', 1)->toArray())->merge([
|
||||
'home_phone' =>$contact->client->phone,
|
||||
'custom_identifier' => $contact->client->number,
|
||||
'name' =>$contact->client->name,
|
||||
'id' => null
|
||||
] )->all();
|
||||
|
||||
$this->attributes = $this->newAttributeBag(Arr::only($contact, $this->fields) );
|
||||
}
|
||||
|
||||
private $fields = [
|
||||
'name',
|
||||
'email',
|
||||
'home_phone',
|
||||
'phone',
|
||||
'custom_identifier',
|
||||
'customer_type' ,
|
||||
'id'
|
||||
];
|
||||
|
||||
private $defaults = [
|
||||
'customer_type' => "Business",
|
||||
'customer_identifier' => null,
|
||||
'id' => null
|
||||
];
|
||||
|
||||
public function render()
|
||||
{
|
||||
return render('gateways.rotessa.components.contact', array_merge($this->defaults, $this->attributes->getAttributes() ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Address Component
|
||||
class AddressComponent extends Component
|
||||
{
|
||||
private $fields = [
|
||||
'address_1',
|
||||
'address_2',
|
||||
'city',
|
||||
'postal_code',
|
||||
'province_code',
|
||||
'country'
|
||||
];
|
||||
|
||||
private $defaults = [
|
||||
'country' => 'US'
|
||||
];
|
||||
|
||||
public array $address;
|
||||
|
||||
public function __construct(array $address) {
|
||||
$this->address = $address;
|
||||
if(strlen($this->address['state']) > 2 ) {
|
||||
$this->address['state'] = $this->address['country'] == 'US' ? array_search($this->address['state'], USStates::$states) : CAProvinces::getAbbreviation($this->address['state']);
|
||||
}
|
||||
|
||||
$this->attributes = $this->newAttributeBag(
|
||||
Arr::only(Arr::mapWithKeys($this->address, function ($item, $key) {
|
||||
return in_array($key, ['address1','address2','state'])?[ (['address1'=>'address_1','address2'=>'address_2','state'=>'province_code'])[$key] => $item ] :[ $key => $item ];
|
||||
}),
|
||||
$this->fields) );
|
||||
}
|
||||
|
||||
|
||||
public function render()
|
||||
{
|
||||
return render('gateways.rotessa.components.address',array_merge( $this->defaults, $this->attributes->getAttributes() ) );
|
||||
}
|
||||
}
|
||||
|
||||
// AmericanBankInfo Component
|
||||
class AccountComponent extends Component
|
||||
{
|
||||
private $fields = [
|
||||
'bank_account_type',
|
||||
'routing_number',
|
||||
'institution_number',
|
||||
'transit_number',
|
||||
'bank_name',
|
||||
'country',
|
||||
'account_number'
|
||||
];
|
||||
|
||||
private $defaults = [
|
||||
'bank_account_type' => null,
|
||||
'routing_number' => null,
|
||||
'institution_number' => null,
|
||||
'transit_number' => null,
|
||||
'bank_name' => ' ',
|
||||
'account_number' => null,
|
||||
'country' => 'US',
|
||||
"authorization_type" => 'Online'
|
||||
];
|
||||
|
||||
public array $account;
|
||||
|
||||
public function __construct(array $account) {
|
||||
$this->account = $account;
|
||||
$this->attributes = $this->newAttributeBag(Arr::only($this->account, $this->fields) );
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return render('gateways.rotessa.components.account', array_merge($this->attributes->getAttributes(), $this->defaults) );
|
||||
}
|
||||
}
|
||||
|
|
@ -88,11 +88,11 @@ class PortalComposer
|
|||
$data['sidebar'] = $this->sidebarMenu();
|
||||
$data['header'] = [];
|
||||
$data['footer'] = [];
|
||||
$data['countries'] = TranslationHelper::getCountries();
|
||||
$data['countries'] = app('countries');
|
||||
$data['company'] = auth()->guard('contact')->user()->company;
|
||||
$data['client'] = auth()->guard('contact')->user()->client;
|
||||
$data['settings'] = $this->settings;
|
||||
$data['currencies'] = TranslationHelper::getCurrencies();
|
||||
$data['currencies'] = app('currencies');
|
||||
$data['contact'] = auth()->guard('contact')->user();
|
||||
|
||||
$data['multiple_contacts'] = session()->get('multiple_contacts') ?: collect();
|
||||
|
|
@ -136,11 +136,11 @@ class PortalComposer
|
|||
|
||||
$data[] = ['title' => ctrans('texts.statement'), 'url' => 'client.statement', 'icon' => 'activity'];
|
||||
|
||||
if (Ninja::isHosted() && auth()->guard('contact')->user()->company->id == config('ninja.ninja_default_company_id')) {
|
||||
// if (Ninja::isHosted() && auth()->guard('contact')->user()->company->id == config('ninja.ninja_default_company_id')) {
|
||||
$data[] = ['title' => ctrans('texts.plan'), 'url' => 'client.plan', 'icon' => 'credit-card'];
|
||||
} else {
|
||||
// } else {
|
||||
$data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar'];
|
||||
}
|
||||
// }
|
||||
|
||||
if (auth()->guard('contact')->user()->client->getSetting('client_initiated_payments')) {
|
||||
$data[] = ['title' => ctrans('texts.pre_payment'), 'url' => 'client.pre_payments.index', 'icon' => 'dollar-sign'];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
use Illuminate\Support\Facades\View;
|
||||
use App\DataProviders\CAProvinces;
|
||||
|
|
@ -9,7 +18,6 @@ View::composer(['*.rotessa.components.address','*.rotessa.components.banks.US.ba
|
|||
$view->with('states', $states);
|
||||
});
|
||||
|
||||
// CAProvinces View Composer
|
||||
View::composer(['*.rotessa.components.address','*.rotessa.components.banks.CA.bank','*.rotessa.components.dropdowns.country.CA'], function ($view) {
|
||||
$provinces = CAProvinces::get();
|
||||
$view->with('provinces', $provinces);
|
||||
|
|
|
|||
|
|
@ -695,7 +695,7 @@ class CompanyExport implements ShouldQueue
|
|||
|
||||
$url = Cache::get($this->hash);
|
||||
|
||||
Cache::put($this->hash, $storage_path, now()->addHour());
|
||||
Cache::put($this->hash, $storage_path, 3600);
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
|
|
|
|||
|
|
@ -48,12 +48,12 @@ class RecurringInvoicesCron
|
|||
Auth::logout();
|
||||
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
$recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('recurring_invoices.is_deleted', false)
|
||||
->where('recurring_invoices.remaining_cycles', '!=', '0')
|
||||
->whereNotNull('recurring_invoices.next_send_date')
|
||||
->whereNull('recurring_invoices.deleted_at')
|
||||
->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
|
||||
$recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('is_deleted', false)
|
||||
->where('remaining_cycles', '!=', '0')
|
||||
->whereNotNull('next_send_date')
|
||||
->whereNull('deleted_at')
|
||||
->where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereHas('client', function ($query) {
|
||||
$query->where('is_deleted', 0)
|
||||
->where('deleted_at', null);
|
||||
|
|
@ -87,27 +87,18 @@ class RecurringInvoicesCron
|
|||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('recurring_invoices.is_deleted', false)
|
||||
->where('recurring_invoices.remaining_cycles', '!=', '0')
|
||||
->whereNull('recurring_invoices.deleted_at')
|
||||
->whereNotNull('recurring_invoices.next_send_date')
|
||||
->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
|
||||
// ->whereHas('client', function ($query) {
|
||||
// $query->where('is_deleted', 0)
|
||||
// ->where('deleted_at', null);
|
||||
// })
|
||||
// ->whereHas('company', function ($query) {
|
||||
// $query->where('is_disabled', 0);
|
||||
// })
|
||||
->leftJoin('clients', function ($join) {
|
||||
$join->on('recurring_invoices.client_id', '=', 'clients.id')
|
||||
->where('clients.is_deleted', 0)
|
||||
->whereNull('clients.deleted_at');
|
||||
$recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('is_deleted', false)
|
||||
->where('remaining_cycles', '!=', '0')
|
||||
->whereNull('deleted_at')
|
||||
->whereNotNull('next_send_date')
|
||||
->where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereHas('client', function ($query) {
|
||||
$query->where('is_deleted', 0)
|
||||
->where('deleted_at', null);
|
||||
})
|
||||
->leftJoin('companies', function ($join) {
|
||||
$join->on('recurring_invoices.company_id', '=', 'companies.id')
|
||||
->where('companies.is_disabled', 0);
|
||||
->whereHas('company', function ($query) {
|
||||
$query->where('is_disabled', 0);
|
||||
})
|
||||
->with('company')
|
||||
->cursor();
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Cron;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UpdateCalculatedFields
|
||||
{
|
||||
use Dispatchable;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
nlog("Updating calculated fields");
|
||||
|
||||
Auth::logout();
|
||||
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
|
||||
Project::query()->with('tasks')->whereHas('tasks', function ($query) {
|
||||
$query->where('updated_at', '>', now()->subHours(2));
|
||||
})
|
||||
->cursor()
|
||||
->each(function ($project) {
|
||||
|
||||
$project->current_hours = $this->calculateDuration($project);
|
||||
$project->save();
|
||||
});
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
//multiDB environment, need to
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
|
||||
Project::query()->with('tasks')->whereHas('tasks', function ($query) {
|
||||
$query->where('updated_at', '>', now()->subHours(2));
|
||||
})
|
||||
->cursor()
|
||||
->each(function ($project) {
|
||||
$project->current_hours = $this->calculateDuration($project);
|
||||
$project->save();
|
||||
});
|
||||
|
||||
//Clean password resets table
|
||||
\DB::connection($db)->table('password_resets')->where('created_at', '<', now()->subHour())->delete();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateDuration($project): int
|
||||
{
|
||||
$duration = 0;
|
||||
|
||||
$project->tasks->each(function ($task) use (&$duration) {
|
||||
|
||||
if(is_iterable(json_decode($task->time_log))) {
|
||||
|
||||
foreach(json_decode($task->time_log) as $log) {
|
||||
|
||||
if(!is_array($log))
|
||||
continue;
|
||||
|
||||
$start_time = $log[0];
|
||||
$end_time = $log[1] == 0 ? time() : $log[1];
|
||||
|
||||
$duration += $end_time - $start_time;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return (int) round(($duration / 60 / 60), 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -94,7 +94,9 @@ class ProcessMailgunWebhook implements ShouldQueue
|
|||
}
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($this->request['event-data']['tags'][0]);
|
||||
$company = Company::query()->where('company_key', $this->request['event-data']['tags'][0])->first();
|
||||
|
||||
/** @var \App\Models\Company $company */
|
||||
$company = Company::where('company_key', $this->request['event-data']['tags'][0])->first();
|
||||
|
||||
if ($company && $this->request['event-data']['event'] == 'complained' && config('ninja.notification.slack')) {
|
||||
$company->notification(new EmailSpamNotification($company))->ninja();
|
||||
|
|
@ -195,7 +197,7 @@ class ProcessMailgunWebhook implements ShouldQueue
|
|||
'date' => \Carbon\Carbon::parse($this->request['event-data']['timestamp'])->format('Y-m-d H:i:s') ?? '',
|
||||
];
|
||||
|
||||
if($sl) {
|
||||
if($sl instanceof SystemLog) {
|
||||
$data = $sl->log;
|
||||
$data['history']['events'][] = $event;
|
||||
$this->updateSystemLog($sl, $data);
|
||||
|
|
|
|||
|
|
@ -176,8 +176,8 @@ class SendRecurring implements ShouldQueue
|
|||
private function createRecurringInvitations($invoice): Invoice
|
||||
{
|
||||
if ($this->recurring_invoice->invitations->count() == 0) {
|
||||
$this->recurring_invoice->service()->createInvitations()->save();
|
||||
$this->recurring_invoice = $this->recurring_invoice->fresh();
|
||||
$this->recurring_invoice = $this->recurring_invoice->service()->createInvitations()->save();
|
||||
// $this->recurring_invoice = $this->recurring_invoice->fresh();
|
||||
}
|
||||
|
||||
$this->recurring_invoice->invitations->each(function ($recurring_invitation) use ($invoice) {
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ class CleanStaleInvoiceOrder implements ShouldQueue
|
|||
->each(function ($invoice) {
|
||||
$invoice->service()->removeUnpaidGatewayFees();
|
||||
});
|
||||
|
||||
\DB::connection($db)->table('password_resets')->where('created_at', '<', now()->subHours(12))->delete();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@ class QuoteReminderJob implements ShouldQueue
|
|||
nrlog("Sending quote reminders on ".now()->format('Y-m-d h:i:s'));
|
||||
|
||||
Quote::query()
|
||||
->where('quotes.is_deleted', 0)
|
||||
->whereIn('quotes.status_id', [Invoice::STATUS_SENT])
|
||||
->whereNull('quotes.deleted_at')
|
||||
->where('quotes.next_send_date', '<=', now()->toDateTimeString())
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT])
|
||||
->whereNull('deleted_at')
|
||||
->where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereHas('client', function ($query) {
|
||||
$query->where('is_deleted', 0)
|
||||
->where('deleted_at', null);
|
||||
|
|
@ -88,10 +88,10 @@ class QuoteReminderJob implements ShouldQueue
|
|||
nrlog("Sending quote reminders on db {$db} ".now()->format('Y-m-d h:i:s'));
|
||||
|
||||
Quote::query()
|
||||
->where('quotes.is_deleted', 0)
|
||||
->whereIn('quotes.status_id', [Invoice::STATUS_SENT])
|
||||
->whereNull('quotes.deleted_at')
|
||||
->where('quotes.next_send_date', '<=', now()->toDateTimeString())
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT])
|
||||
->whereNull('deleted_at')
|
||||
->where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereHas('client', function ($query) {
|
||||
$query->where('is_deleted', 0)
|
||||
->where('deleted_at', null);
|
||||
|
|
|
|||
|
|
@ -356,15 +356,14 @@ class BillingPortalPurchase extends Component
|
|||
|
||||
$this->methods = $contact->client->service()->getPaymentMethods($this->price);
|
||||
|
||||
foreach($this->methods as $method){
|
||||
|
||||
if($method['is_paypal'] == '1' && !$this->steps['check_rff']){
|
||||
$this->rff();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
$method_values = array_column($this->methods, 'is_paypal');
|
||||
$is_paypal = in_array('1', $method_values);
|
||||
|
||||
if($is_paypal && !$this->steps['check_rff'])
|
||||
$this->rff();
|
||||
elseif(!$this->steps['check_rff'])
|
||||
$this->steps['fetched_payment_methods'] = true;
|
||||
|
||||
$this->heading_text = ctrans('texts.payment_methods');
|
||||
|
||||
return $this;
|
||||
|
|
|
|||
|
|
@ -484,7 +484,7 @@ class BillingPortalPurchasev2 extends Component
|
|||
$this->methods = [];
|
||||
}
|
||||
|
||||
if ($this->contact && $this->float_amount_total >= 1) {
|
||||
if ($this->contact && $this->float_amount_total >= 0) {
|
||||
$this->methods = $this->contact->client->service()->getPaymentMethods($this->float_amount_total);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ class PersonalAddress extends Component
|
|||
|
||||
public $country_id;
|
||||
|
||||
public $countries;
|
||||
|
||||
public $saved;
|
||||
|
||||
protected $rules = [
|
||||
|
|
@ -33,7 +31,7 @@ class PersonalAddress extends Component
|
|||
'country_id' => ['sometimes'],
|
||||
];
|
||||
|
||||
public function mount($countries)
|
||||
public function mount()
|
||||
{
|
||||
$this->fill([
|
||||
'profile' => auth()->guard('contact')->user()->client,
|
||||
|
|
@ -43,8 +41,6 @@ class PersonalAddress extends Component
|
|||
'state' => auth()->guard('contact')->user()->client->state,
|
||||
'postal_code' => auth()->guard('contact')->user()->client->postal_code,
|
||||
'country_id' => auth()->guard('contact')->user()->client->country_id,
|
||||
|
||||
'countries' => $countries,
|
||||
'saved' => ctrans('texts.save'),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ class ShippingAddress extends Component
|
|||
|
||||
public $shipping_country_id;
|
||||
|
||||
public $countries;
|
||||
|
||||
public $saved;
|
||||
|
||||
protected $rules = [
|
||||
|
|
@ -33,7 +31,7 @@ class ShippingAddress extends Component
|
|||
'shipping_country_id' => ['sometimes'],
|
||||
];
|
||||
|
||||
public function mount($countries)
|
||||
public function mount()
|
||||
{
|
||||
$this->fill([
|
||||
'profile' => auth()->guard('contact')->user()->client,
|
||||
|
|
@ -43,8 +41,6 @@ class ShippingAddress extends Component
|
|||
'shipping_state' => auth()->guard('contact')->user()->client->shipping_state,
|
||||
'shipping_postal_code' => auth()->guard('contact')->user()->client->shipping_postal_code,
|
||||
'shipping_country_id' => auth()->guard('contact')->user()->client->shipping_country_id,
|
||||
|
||||
'countries' => $countries,
|
||||
'saved' => ctrans('texts.save'),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class TemplateEmail extends Mailable
|
|||
}
|
||||
|
||||
$link_string = '<ul>';
|
||||
|
||||
$link_string .= "<li>{ctrans('texts.download_files')}</li>";
|
||||
foreach ($this->build_email->getAttachmentLinks() as $link) {
|
||||
$link_string .= "<li>{$link}</li>";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ class CompanyGateway extends BaseModel
|
|||
'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9' => 322,
|
||||
'80af24a6a691230bbec33e930ab40666' => 323,
|
||||
'vpyfbmdrkqcicpkjqdusgjfluebftuva' => 324, //BTPay
|
||||
'91be24c7b792230bced33e930ac61676' => 325,
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
|
|
|||
|
|
@ -53,4 +53,9 @@ class Currency extends StaticModel
|
|||
'deleted_at' => 'timestamp',
|
||||
'precision' => 'integer',
|
||||
];
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return trans('texts.currency_'.$this->name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use App\Models\CompanyUser;
|
|||
use Illuminate\Support\Carbon;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||
|
||||
/**
|
||||
* App\Models\Task
|
||||
|
|
@ -137,7 +138,7 @@ class Task extends BaseModel
|
|||
// 'project',
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
protected $touches = ['project'];
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
|
|
@ -159,27 +160,55 @@ class Task extends BaseModel
|
|||
return $this->morphMany(Document::class, 'documentable');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function assigned_user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function client()
|
||||
{
|
||||
return $this->belongsTo(Client::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function status()
|
||||
{
|
||||
return $this->belongsTo(TaskStatus::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function stringStatus()
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function invoice()
|
||||
{
|
||||
return $this->belongsTo(Invoice::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function stringStatus(): string
|
||||
{
|
||||
if($this->invoice_id) {
|
||||
return '<h5><span class="badge badge-success">'.ctrans('texts.invoiced').'</span></h5>';
|
||||
|
|
@ -193,16 +222,6 @@ class Task extends BaseModel
|
|||
|
||||
}
|
||||
|
||||
public function invoice()
|
||||
{
|
||||
return $this->belongsTo(Invoice::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function calcStartTime()
|
||||
{
|
||||
$parts = json_decode($this->time_log) ?: [];
|
||||
|
|
@ -230,7 +249,7 @@ class Task extends BaseModel
|
|||
public function calcDuration($start_time_cutoff = 0, $end_time_cutoff = 0)
|
||||
{
|
||||
$duration = 0;
|
||||
$parts = json_decode($this->time_log) ?: [];
|
||||
$parts = json_decode($this->time_log ?? '{}') ?: [];
|
||||
|
||||
foreach ($parts as $part) {
|
||||
$start_time = $part[0];
|
||||
|
|
@ -272,6 +291,26 @@ class Task extends BaseModel
|
|||
return $this->company->settings->default_task_rate ?? 0;
|
||||
}
|
||||
|
||||
public function taskCompanyValue(): float
|
||||
{
|
||||
$client_currency = $this->client->getSetting('currency_id');
|
||||
$company_currency = $this->company->getSetting('currency_id');
|
||||
|
||||
if($client_currency != $company_currency)
|
||||
{
|
||||
$converter = new CurrencyApi();
|
||||
return $converter->convert($this->taskValue(), $client_currency, $company_currency);
|
||||
}
|
||||
|
||||
return $this->taskValue();
|
||||
|
||||
}
|
||||
|
||||
public function taskValue(): float
|
||||
{
|
||||
return round(($this->calcDuration() / 3600) * $this->getRate(),2);
|
||||
}
|
||||
|
||||
public function processLogs()
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ class TaskObserver
|
|||
if ($subscriptions) {
|
||||
WebhookHandler::dispatch(Webhook::EVENT_ARCHIVE_TASK, $task, $task->company)->delay(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ abstract class AbstractPaymentDriver
|
|||
{
|
||||
abstract public function authorizeView(array $data);
|
||||
|
||||
abstract public function authorizeResponse(Request $request);
|
||||
abstract public function authorizeResponse(\App\Http\Requests\Request | Request $request);
|
||||
|
||||
abstract public function processPaymentView(array $data);
|
||||
|
||||
|
|
|
|||
|
|
@ -586,10 +586,6 @@ class BaseDriver extends AbstractPaymentDriver
|
|||
|
||||
$invoices = Invoice::query()->whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->get();
|
||||
|
||||
// $invoices->each(function ($invoice) {
|
||||
// $invoice->service()->deletePdf();
|
||||
// });
|
||||
|
||||
$invoices->first()->invitations->each(function ($invitation) use ($nmo) {
|
||||
if ((bool) $invitation->contact->send_email !== false && $invitation->contact->email) {
|
||||
$nmo->to_user = $invitation->contact;
|
||||
|
|
|
|||
|
|
@ -1,249 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Omnipay\Common\Item;
|
||||
use Omnipay\Omnipay;
|
||||
|
||||
class PayPalExpressPaymentDriver extends BaseDriver
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public $token_billing = false;
|
||||
|
||||
public $can_authorise_credit_card = false;
|
||||
|
||||
private $omnipay_gateway;
|
||||
|
||||
private float $fee = 0;
|
||||
|
||||
public const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYPAL;
|
||||
|
||||
public function gatewayTypes()
|
||||
{
|
||||
return [
|
||||
GatewayType::PAYPAL,
|
||||
];
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Omnipay PayPal_Express gateway.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function initializeOmnipayGateway(): void
|
||||
{
|
||||
$this->omnipay_gateway = Omnipay::create(
|
||||
$this->company_gateway->gateway->provider
|
||||
);
|
||||
|
||||
$this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig());
|
||||
}
|
||||
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
{
|
||||
// PayPal doesn't have multiple ways of paying.
|
||||
// There's just one, off-site redirect.
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function processPaymentView($data)
|
||||
{
|
||||
$this->initializeOmnipayGateway();
|
||||
|
||||
$this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]);
|
||||
$this->payment_hash->save();
|
||||
|
||||
$response = $this->omnipay_gateway
|
||||
->purchase($this->generatePaymentDetails($data))
|
||||
->setItems($this->generatePaymentItems($data))
|
||||
->send();
|
||||
|
||||
if ($response->isRedirect()) {
|
||||
return $response->redirect();
|
||||
}
|
||||
|
||||
// $this->sendFailureMail($response->getMessage() ?: '');
|
||||
|
||||
$message = [
|
||||
'server_response' => $response->getMessage(),
|
||||
'data' => $this->payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_PAYPAL,
|
||||
$this->client,
|
||||
$this->client->company,
|
||||
);
|
||||
|
||||
throw new PaymentFailed($response->getMessage(), $response->getCode());
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
$this->initializeOmnipayGateway();
|
||||
|
||||
$response = $this->omnipay_gateway
|
||||
->completePurchase(['amount' => $this->payment_hash->data->amount, 'currency' => $this->client->getCurrencyCode()])
|
||||
->send();
|
||||
|
||||
if ($response->isCancelled() && $this->client->getSetting('enable_client_portal')) {
|
||||
return redirect()->route('client.invoices.index')->with('warning', ctrans('texts.status_cancelled'));
|
||||
} elseif($response->isCancelled() && !$this->client->getSetting('enable_client_portal')) {
|
||||
redirect()->route('client.invoices.show', ['invoice' => $this->payment_hash->fee_invoice])->with('warning', ctrans('texts.status_cancelled'));
|
||||
}
|
||||
|
||||
if ($response->isSuccessful()) {
|
||||
$data = [
|
||||
'payment_method' => $response->getData()['TOKEN'],
|
||||
'payment_type' => PaymentType::PAYPAL,
|
||||
'amount' => $this->payment_hash->data->amount,
|
||||
'transaction_reference' => $response->getTransactionReference(),
|
||||
'gateway_type_id' => GatewayType::PAYPAL,
|
||||
];
|
||||
|
||||
$payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => (array) $response->getData(), '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)]);
|
||||
}
|
||||
|
||||
if (! $response->isSuccessful()) {
|
||||
$data = $response->getData();
|
||||
|
||||
$this->sendFailureMail($response->getMessage() ?: '');
|
||||
|
||||
$message = [
|
||||
'server_response' => $data['L_LONGMESSAGE0'],
|
||||
'data' => $this->payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_PAYPAL,
|
||||
$this->client,
|
||||
$this->client->company,
|
||||
);
|
||||
|
||||
throw new PaymentFailed($response->getMessage(), $response->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function generatePaymentDetails(array $data)
|
||||
{
|
||||
$_invoice = collect($this->payment_hash->data->invoices)->first();
|
||||
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
|
||||
|
||||
// $this->fee = $this->feeCalc($invoice, $data['total']['amount_with_fee']);
|
||||
|
||||
return [
|
||||
'currency' => $this->client->getCurrencyCode(),
|
||||
'transactionType' => 'Purchase',
|
||||
'clientIp' => request()->getClientIp(),
|
||||
// 'amount' => round(($data['total']['amount_with_fee'] + $this->fee),2),
|
||||
'amount' => round($data['total']['amount_with_fee'], 2),
|
||||
'returnUrl' => route('client.payments.response', [
|
||||
'company_gateway_id' => $this->company_gateway->id,
|
||||
'payment_hash' => $this->payment_hash->hash,
|
||||
'payment_method_id' => GatewayType::PAYPAL,
|
||||
]),
|
||||
'cancelUrl' => $this->client->company->domain()."/client/invoices/{$invoice->hashed_id}",
|
||||
'description' => implode(',', collect($this->payment_hash->data->invoices)
|
||||
->map(function ($invoice) {
|
||||
return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->invoice_number);
|
||||
})->toArray()),
|
||||
'transactionId' => $this->payment_hash->hash.'-'.time(),
|
||||
'ButtonSource' => 'InvoiceNinja_SP',
|
||||
'solutionType' => 'Sole',
|
||||
'no_shipping' => $this->company_gateway->require_shipping_address ? 0 : 1,
|
||||
];
|
||||
}
|
||||
|
||||
public function generatePaymentItems(array $data)
|
||||
{
|
||||
$_invoice = collect($this->payment_hash->data->invoices)->first();
|
||||
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
|
||||
|
||||
$items = [];
|
||||
|
||||
$items[] = new Item([
|
||||
'name' => ' ',
|
||||
'description' => ctrans('texts.invoice_number').'# '.$invoice->number,
|
||||
'price' => $data['total']['amount_with_fee'],
|
||||
'quantity' => 1,
|
||||
]);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function feeCalc($invoice, $invoice_total)
|
||||
{
|
||||
$invoice->service()->removeUnpaidGatewayFees();
|
||||
$invoice = $invoice->fresh();
|
||||
|
||||
$balance = floatval($invoice->balance);
|
||||
|
||||
$_updated_invoice = $invoice->service()->addGatewayFee($this->company_gateway, GatewayType::PAYPAL, $invoice_total)->save();
|
||||
|
||||
if (floatval($_updated_invoice->balance) > $balance) {
|
||||
$fee = floatval($_updated_invoice->balance) - $balance;
|
||||
|
||||
$this->payment_hash->fee_total = $fee;
|
||||
$this->payment_hash->save();
|
||||
|
||||
return $fee;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Rotessa\Jobs;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\PaymentHash;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Jobs\Mail\PaymentFailedMailer;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class TransactionReport implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $tries = 1; //number of retries
|
||||
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
foreach(MultiDB::$dbs as $db)
|
||||
{
|
||||
MultiDB::setDB($db);
|
||||
|
||||
CompanyGateway::query()
|
||||
->where('gateway_key', '91be24c7b792230bced33e930ac61676')
|
||||
->cursor()
|
||||
->each(function ($cg){
|
||||
|
||||
$driver = $cg->driver()->init();
|
||||
|
||||
//Approved Transactions
|
||||
$transactions = $driver->gatewayRequest("get", "transaction_report", ['page' => 1, 'status' => 'Approved', 'start_date' => now()->subMonths(2)->format('Y-m-d')]);
|
||||
|
||||
if($transactions->successful())
|
||||
{
|
||||
$transactions = $transactions->json();
|
||||
nlog($transactions);
|
||||
|
||||
Payment::query()
|
||||
->where('company_id', $cg->company_id)
|
||||
->where('status_id', Payment::STATUS_PENDING)
|
||||
->whereIn('transaction_reference', array_column($transactions, "transaction_schedule_id"))
|
||||
->cursor()
|
||||
->each(function ($payment) use ($transactions) {
|
||||
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->save();
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => collect($transactions)->where('id', $payment->transaction_reference)->first()->toArray(), 'data' => []],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_ROTESSA,
|
||||
$payment->client,
|
||||
$payment->company,
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
//Declined / Charged Back Transactions
|
||||
$declined_transactions = $driver->gatewayRequest("get", "transaction_report", ['page' => 1, 'status' => 'Declined', 'start_date' => now()->subMonths(2)->format('Y-m-d')]);
|
||||
$chargeback_transactions = $driver->gatewayRequest("get", "transaction_report", ['page' => 1, 'status' => 'Chargeback', 'start_date' => now()->subMonths(2)->format('Y-m-d')]);
|
||||
|
||||
if($declined_transactions->successful() && $chargeback_transactions->successful()) {
|
||||
|
||||
$transactions = array_merge($declined_transactions->json(), $chargeback_transactions->json());
|
||||
|
||||
nlog($transactions);
|
||||
|
||||
Payment::query()
|
||||
->where('company_id', $cg->company_id)
|
||||
->where('status_id', Payment::STATUS_PENDING)
|
||||
->whereIn('transaction_reference', array_column($transactions, "transaction_schedule_id"))
|
||||
->cursor()
|
||||
->each(function ($payment) use ($transactions){
|
||||
|
||||
|
||||
$client = $payment->client;
|
||||
|
||||
$payment->service()->deletePayment();
|
||||
|
||||
$payment->status_id = Payment::STATUS_FAILED;
|
||||
$payment->save();
|
||||
|
||||
$payment_hash = PaymentHash::query()->where('payment_id', $payment->id)->first();
|
||||
|
||||
if ($payment_hash) {
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($client->getMergedSettings()));
|
||||
App::setLocale($client->locale());
|
||||
|
||||
$error = ctrans('texts.client_payment_failure_body', [
|
||||
'invoice' => implode(',', $payment->invoices->pluck('number')->toArray()),
|
||||
'amount' => array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total, ]);
|
||||
} else {
|
||||
$error = 'Payment for '.$payment->client->present()->name()." for {$payment->amount} failed";
|
||||
}
|
||||
|
||||
PaymentFailedMailer::dispatch(
|
||||
$payment_hash,
|
||||
$client->company,
|
||||
$client,
|
||||
$error
|
||||
);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => collect($transactions)->where('id', $payment->transaction_reference)->first()->toArray(), 'data' => []],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_ROTESSA,
|
||||
$payment->client,
|
||||
$payment->company,
|
||||
);
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,14 +12,11 @@
|
|||
|
||||
namespace App\PaymentDrivers\Rotessa;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Client;
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
use Illuminate\View\View;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentType;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Exceptions\PaymentFailed;
|
||||
|
|
@ -28,20 +25,20 @@ use App\Models\ClientGatewayToken;
|
|||
use Illuminate\Http\RedirectResponse;
|
||||
use App\PaymentDrivers\RotessaPaymentDriver;
|
||||
use App\PaymentDrivers\Common\MethodInterface;
|
||||
use App\PaymentDrivers\Rotessa\Resources\Customer;
|
||||
use App\PaymentDrivers\Rotessa\Resources\Transaction;
|
||||
use Omnipay\Common\Exception\InvalidRequestException;
|
||||
use Omnipay\Common\Exception\InvalidResponseException;
|
||||
use App\Exceptions\Ninja\ClientPortalAuthorizationException;
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
|
||||
class PaymentMethod implements MethodInterface
|
||||
{
|
||||
protected RotessaPaymentDriver $rotessa;
|
||||
|
||||
public function __construct(RotessaPaymentDriver $rotessa)
|
||||
private array $transaction = [
|
||||
"financial_transactions" => [],
|
||||
"frequency" =>'Once',
|
||||
"installments" =>1
|
||||
];
|
||||
|
||||
public function __construct(protected RotessaPaymentDriver $rotessa)
|
||||
{
|
||||
$this->rotessa = $rotessa;
|
||||
$this->rotessa->init();
|
||||
}
|
||||
|
||||
|
|
@ -53,16 +50,13 @@ class PaymentMethod implements MethodInterface
|
|||
*/
|
||||
public function authorizeView(array $data): View
|
||||
{
|
||||
$data['contact'] = collect($data['client']->contacts->firstWhere('is_primary', 1)->toArray())->merge([
|
||||
$data['contact'] = collect($data['client']->contacts->first()->toArray())->merge([
|
||||
'home_phone' => $data['client']->phone,
|
||||
'custom_identifier' => $data['client']->number,
|
||||
'name' => $data['client']->name,
|
||||
'id' => null
|
||||
] )->all();
|
||||
$data['gateway'] = $this->rotessa;
|
||||
// Set gateway type according to client country
|
||||
// $data['gateway_type_id'] = $data['client']->country->iso_3166_2 == 'US' ? GatewayType::BANK_TRANSFER : ( $data['client']->country->iso_3166_2 == 'CA' ? GatewayType::ACSS : (int) request('method'));
|
||||
// TODO: detect GatewayType based on client country USA vs CAN
|
||||
$data['gateway_type_id'] = GatewayType::ACSS ;
|
||||
$data['account'] = [
|
||||
'routing_number' => $data['client']->routing_id,
|
||||
|
|
@ -78,41 +72,38 @@ class PaymentMethod implements MethodInterface
|
|||
* @param Request $request
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function authorizeResponse(Request $request): RedirectResponse
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
try {
|
||||
$request->validate([
|
||||
'gateway_type_id' => ['required','integer'],
|
||||
'country' => ['required'],
|
||||
'name' => ['required'],
|
||||
'address_1' => ['required'],
|
||||
'address_2' => ['required'],
|
||||
'city' => ['required'],
|
||||
'email' => ['required','email:filter'],
|
||||
'province_code' => ['required','size:2','alpha'],
|
||||
'postal_code' => ['required'],
|
||||
'authorization_type' => ['required'],
|
||||
'account_number' => ['required'],
|
||||
'bank_name' => ['required'],
|
||||
'phone' => ['required'],
|
||||
'home_phone' => ['required'],
|
||||
'bank_account_type'=>['required_if:country,US'],
|
||||
'routing_number'=>['required_if:country,US'],
|
||||
'institution_number'=>['required_if:country,CA','numeric'],
|
||||
'transit_number'=>['required_if:country,CA','numeric'],
|
||||
'custom_identifier'=>['required_without:customer_id'],
|
||||
'customer_id'=>['required_without:custom_identifier','integer'],
|
||||
]);
|
||||
$customer = new Customer( ['address' => $request->only('address_1','address_2','city','postal_code','province_code','country'), 'custom_identifier' => $request->input('custom_identifier') ] + $request->all());
|
||||
$this->rotessa->findOrCreateCustomer($customer->resolve());
|
||||
|
||||
return redirect()->route('client.payment_methods.index')->withMessage(ctrans('texts.payment_method_added'));
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
return $this->rotessa->processInternallyFailedPayment($this->rotessa, new ClientPortalAuthorizationException( get_class( $e) . " : {$e->getMessage()}", (int) $e->getCode() ));
|
||||
}
|
||||
$request->validate([
|
||||
'gateway_type_id' => ['required','integer'],
|
||||
'country' => ['required','in:US,CA,United States,Canada'],
|
||||
'name' => ['required'],
|
||||
'address_1' => ['required'],
|
||||
'city' => ['required'],
|
||||
'email' => ['required','email:filter'],
|
||||
'province_code' => ['required','size:2','alpha'],
|
||||
'postal_code' => ['required'],
|
||||
'authorization_type' => ['required'],
|
||||
'account_number' => ['required'],
|
||||
'bank_name' => ['required'],
|
||||
'phone' => ['required'],
|
||||
'home_phone' => ['required','size:10'],
|
||||
'bank_account_type'=>['required_if:country,US'],
|
||||
'routing_number'=>['required_if:country,US'],
|
||||
'institution_number'=>['required_if:country,CA','numeric','digits:3'],
|
||||
'transit_number'=>['required_if:country,CA','numeric','digits:5'],
|
||||
'custom_identifier'=>['required_without:customer_id'],
|
||||
'customer_id'=>['required_without:custom_identifier','integer'],
|
||||
'customer_type' => ['required', 'in:Personal,Business'],
|
||||
]);
|
||||
|
||||
$customer = array_merge(['address' => $request->only('address_1','address_2','city','postal_code','province_code','country'), 'custom_identifier' => $request->input('custom_identifier') ], $request->all());
|
||||
|
||||
$this->rotessa->findOrCreateCustomer($customer);
|
||||
|
||||
return redirect()->route('client.payment_methods.index')->withMessage(ctrans('texts.payment_method_added'));
|
||||
|
||||
return back()->withMessage(ctrans('texts.unable_to_verify_payment_method'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -128,7 +119,7 @@ class PaymentMethod implements MethodInterface
|
|||
$data['due_date'] = date('Y-m-d', min(max(strtotime($data['invoices']->max('due_date')), strtotime('now')), strtotime('+1 day')));
|
||||
$data['process_date'] = $data['due_date'];
|
||||
$data['currency'] = $this->rotessa->client->getCurrencyCode();
|
||||
$data['frequency'] = Frequencies::getOnePayment();
|
||||
$data['frequency'] = 'Once';
|
||||
$data['installments'] = 1;
|
||||
$data['invoice_nums'] = $data['invoices']->pluck('invoice_number')->join(', ');
|
||||
return render('gateways.rotessa.bank_transfer.pay', $data );
|
||||
|
|
@ -138,34 +129,41 @@ class PaymentMethod implements MethodInterface
|
|||
* Handle payments page for Rotessa.
|
||||
*
|
||||
* @param PaymentResponseRequest $request
|
||||
* @return void
|
||||
*/
|
||||
public function paymentResponse(PaymentResponseRequest $request)
|
||||
{
|
||||
|
||||
$response= null;
|
||||
$customer = null;
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'source' => ['required','string','exists:client_gateway_tokens,token'],
|
||||
'amount' => ['required','numeric'],
|
||||
'process_date'=> ['required','date','after_or_equal:today'],
|
||||
]);
|
||||
|
||||
$customer = ClientGatewayToken::query()
|
||||
->where('company_gateway_id', $this->rotessa->company_gateway->id)
|
||||
->where('client_id', $this->rotessa->client->id)
|
||||
->where('is_deleted', 0)
|
||||
->where('token', $request->input('source'))
|
||||
->first();
|
||||
|
||||
if(!$customer) throw new \Exception('Client gateway token not found!', SystemLog::TYPE_ROTESSA);
|
||||
|
||||
$transaction = new Transaction($request->only('frequency' ,'installments','amount','process_date') + ['comment' => $this->rotessa->getDescription(false) ]);
|
||||
$transaction->additional(['customer_id' => $customer->gateway_customer_reference]);
|
||||
$transaction = array_filter( $transaction->resolve());
|
||||
$response = $this->rotessa->gateway->capture($transaction)->send();
|
||||
if(!$response->isSuccessful()) throw new \Exception($response->getMessage(), (int) $response->getCode());
|
||||
$transaction = array_merge($this->transaction,[
|
||||
'amount' => $request->input('amount'),
|
||||
'process_date' => now()->addSeconds($customer->client->utc_offset())->format('Y-m-d'),
|
||||
'comment' => $this->rotessa->getDescription(false),
|
||||
'customer_id' => $customer->gateway_customer_reference,
|
||||
]);
|
||||
|
||||
$response = $this->rotessa->gatewayRequest('post','transaction_schedules', $transaction);
|
||||
|
||||
return $this->processPendingPayment($response->getParameter('id'), (float) $response->getParameter('amount'), (int) $customer->gateway_type_id , $customer->token);
|
||||
if($response->failed())
|
||||
$response->throw();
|
||||
|
||||
$response = $response->json();
|
||||
|
||||
return $this->processPendingPayment($response['id'], (float) $response['amount'], PaymentType::ACSS , $customer->token);
|
||||
} catch(\Throwable $e) {
|
||||
$this->processUnsuccessfulPayment( new InvalidResponseException($e->getMessage(), (int) $e->getCode()) );
|
||||
$this->processUnsuccessfulPayment( new \Exception($e->getMessage(), (int) $e->getCode()) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -194,7 +192,7 @@ class PaymentMethod implements MethodInterface
|
|||
/**
|
||||
* Handle unsuccessful payment for Rotessa.
|
||||
*
|
||||
* @param Exception $exception
|
||||
* @param \Exception $exception
|
||||
* @throws PaymentFailed
|
||||
* @return void
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\PaymentDrivers\Rotessa\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Omnipay\Rotessa\Model\CustomerModel;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class Customer extends JsonResource
|
||||
{
|
||||
function __construct($resource) {
|
||||
parent::__construct( new CustomerModel($resource));
|
||||
}
|
||||
|
||||
function jsonSerialize() : array {
|
||||
return $this->resource->jsonSerialize();
|
||||
}
|
||||
|
||||
function toArray(Request $request) : array {
|
||||
return $this->additional + parent::toArray($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\PaymentDrivers\Rotessa\Resources;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Omnipay\Rotessa\Model\TransactionScheduleModel;
|
||||
|
||||
class Transaction extends JsonResource
|
||||
{
|
||||
function __construct($resource) {
|
||||
parent::__construct( new TransactionScheduleModel( $resource));
|
||||
}
|
||||
|
||||
function jsonSerialize() : array {
|
||||
return $this->resource->jsonSerialize();
|
||||
}
|
||||
|
||||
function toArray(Request $request) : array {
|
||||
return $this->additional + parent::toArray($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa;
|
||||
|
||||
use Omnipay\Common\AbstractGateway;
|
||||
use Omnipay\Rotessa\ClientInterface;
|
||||
use Omnipay\Rotessa\Message\RequestInterface;
|
||||
|
||||
abstract class AbstractClient extends AbstractGateway implements ClientInterface
|
||||
{
|
||||
|
||||
protected $default_parameters = [];
|
||||
|
||||
public function getDefaultParameters() : array {
|
||||
return $this->default_parameters;
|
||||
}
|
||||
|
||||
public function setDefaultParameters(array $params) {
|
||||
$this->default_parameters = $params;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa;
|
||||
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
trait ApiTrait
|
||||
{
|
||||
public function getCustomers() : RequestInterface {
|
||||
return $this->createRequest('GetCustomers', [] );
|
||||
}
|
||||
public function postCustomers(array $params) : RequestInterface {
|
||||
return $this->createRequest('PostCustomers', $params );
|
||||
}
|
||||
public function getCustomersId(array $params) : RequestInterface {
|
||||
return $this->createRequest('GetCustomersId', $params );
|
||||
}
|
||||
public function patchCustomersId(array $params) : RequestInterface {
|
||||
return $this->createRequest('PatchCustomersId', $params );
|
||||
}
|
||||
public function postCustomersShowWithCustomIdentifier(array $params) : RequestInterface {
|
||||
return $this->createRequest('PostCustomersShowWithCustomIdentifier', $params );
|
||||
}
|
||||
public function getTransactionSchedulesId(array $params) : RequestInterface {
|
||||
return $this->createRequest('GetTransactionSchedulesId', $params );
|
||||
}
|
||||
public function deleteTransactionSchedulesId(array $params) : RequestInterface {
|
||||
return $this->createRequest('DeleteTransactionSchedulesId', $params );
|
||||
}
|
||||
public function patchTransactionSchedulesId(array $params) : RequestInterface {
|
||||
return $this->createRequest('PatchTransactionSchedulesId', $params );
|
||||
}
|
||||
public function postTransactionSchedules(array $params) : RequestInterface {
|
||||
return $this->createRequest('PostTransactionSchedules', $params );
|
||||
}
|
||||
public function postTransactionSchedulesCreateWithCustomIdentifier(array $params) : RequestInterface {
|
||||
return $this->createRequest('PostTransactionSchedulesCreateWithCustomIdentifier', $params );
|
||||
}
|
||||
public function postTransactionSchedulesUpdateViaPost(array $params) : RequestInterface {
|
||||
return $this->createRequest('PostTransactionSchedulesUpdateViaPost', $params );
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa;
|
||||
|
||||
use Omnipay\Common\GatewayInterface;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
interface ClientInterface extends GatewayInterface
|
||||
{
|
||||
public function getDefaultParameters(): array;
|
||||
public function setDefaultParameters(array $data);
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Exception;
|
||||
|
||||
class BadRequestException extends \Exception {
|
||||
protected $message = "Your request includes invalid parameters";
|
||||
protected $code = 400;
|
||||
}
|
||||
|
||||
class UnauthorizedException extends \Exception {
|
||||
protected $message = "Your API key is not valid or is missing";
|
||||
protected $code = 401;
|
||||
}
|
||||
|
||||
class NotFoundException extends \Exception {
|
||||
protected $message = "The specified resource could not be found";
|
||||
protected $code = 404;
|
||||
}
|
||||
|
||||
class NotAcceptableException extends \Exception {
|
||||
protected $message = "You requested a format that isn’t json";
|
||||
protected $code = 406;
|
||||
}
|
||||
|
||||
class UnprocessableEntityException extends \Exception {
|
||||
protected $message = "Your request results in invalid data";
|
||||
protected $code = 422;
|
||||
}
|
||||
|
||||
class InternalServerErrorException extends \Exception {
|
||||
protected $message = "We had a problem with our server. Try again later";
|
||||
protected $code = 500;
|
||||
}
|
||||
|
||||
class ServiceUnavailableException extends \Exception {
|
||||
protected $message = "We're temporarily offline for maintenance. Please try again later";
|
||||
protected $code = 503;
|
||||
}
|
||||
|
||||
class ValidationException extends \Exception {
|
||||
protected $message = "A validation error has occured";
|
||||
protected $code = 600;
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa;
|
||||
|
||||
use Omnipay\Rotessa\ApiTrait;
|
||||
use Omnipay\Rotessa\AbstractClient;
|
||||
use Omnipay\Rotessa\ClientInterface;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class Gateway extends AbstractClient implements ClientInterface {
|
||||
|
||||
use ApiTrait;
|
||||
|
||||
protected $default_parameters = ['api_key' => 1234567890 ];
|
||||
|
||||
protected $test_mode = true;
|
||||
|
||||
protected $api_key;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'Rotessa';
|
||||
}
|
||||
|
||||
public function getDefaultParameters() : array
|
||||
{
|
||||
return array_merge($this->default_parameters, array('api_key' => $this->api_key, 'test_mode' => $this->test_mode ) );
|
||||
}
|
||||
|
||||
public function setTestMode($value) {
|
||||
$this->test_mode = $value;
|
||||
}
|
||||
|
||||
public function getTestMode() {
|
||||
return $this->test_mode;
|
||||
}
|
||||
|
||||
protected function createRequest($class_name, ?array $parameters = [] ) :RequestInterface {
|
||||
$class = null;
|
||||
$class_name = "Omnipay\\Rotessa\\Message\\Request\\$class_name";
|
||||
$parameters = $class_name::hasModel() ? (($parameters = ($class_name::getModel($parameters)))->validate() ? $parameters->jsonSerialize() : null ) : $parameters;
|
||||
try {
|
||||
$class = new $class_name($this->httpClient, $this->httpRequest, $this->getDefaultParameters() + $parameters );
|
||||
} catch (\Throwable $th) {
|
||||
throw $th;
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
function setApiKey($value) {
|
||||
$this->api_key = $value;
|
||||
}
|
||||
|
||||
function getApiKey() {
|
||||
return $this->api_key;
|
||||
}
|
||||
|
||||
function authorize(array $options = []) : RequestInterface {
|
||||
return $this->postCustomers($options);
|
||||
}
|
||||
|
||||
function capture(array $options = []) : RequestInterface {
|
||||
return array_key_exists('customer_id', $options)? $this->postTransactionSchedules($options) : $this->postTransactionSchedulesCreateWithCustomIdentifier($options) ;
|
||||
}
|
||||
|
||||
function updateCustomer(array $options) : RequestInterface {
|
||||
return $this->patchCustomersId($options);
|
||||
}
|
||||
|
||||
function fetchTransaction($id = null) : RequestInterface {
|
||||
return $this->getTransactionSchedulesId(compact('id'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Http;
|
||||
|
||||
use Omnipay\Common\Http\Client as HttpClient;
|
||||
use Omnipay\Common\Http\Exception\NetworkException;
|
||||
use Omnipay\Common\Http\Exception\RequestException;
|
||||
use Http\Discovery\HttpClientDiscovery;
|
||||
use Http\Discovery\MessageFactoryDiscovery;
|
||||
use Http\Message\RequestFactory;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class Client extends HttpClient
|
||||
{
|
||||
/**
|
||||
* The Http Client which implements `public function sendRequest(RequestInterface $request)`
|
||||
* Note: Will be changed to PSR-18 when released
|
||||
*
|
||||
* @var HttpClient
|
||||
*/
|
||||
private $httpClient;
|
||||
|
||||
/**
|
||||
* @var RequestFactory
|
||||
*/
|
||||
private $requestFactory;
|
||||
|
||||
public function __construct($httpClient = null, RequestFactory $requestFactory = null)
|
||||
{
|
||||
$this->httpClient = $httpClient ?: HttpClientDiscovery::find();
|
||||
$this->requestFactory = $requestFactory ?: MessageFactoryDiscovery::find();
|
||||
parent::__construct($httpClient, $requestFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $method
|
||||
* @param $uri
|
||||
* @param array $headers
|
||||
* @param string|array|resource|StreamInterface|null $body
|
||||
* @param string $protocolVersion
|
||||
* @return ResponseInterface
|
||||
* @throws \Http\Client\Exception
|
||||
*/
|
||||
public function request(
|
||||
$method,
|
||||
$uri,
|
||||
array $headers = [],
|
||||
$body = null,
|
||||
$protocolVersion = '1.1'
|
||||
) {
|
||||
return $this->sendRequest($method, $uri, $headers, $body, $protocolVersion);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @return ResponseInterface
|
||||
* @throws \Http\Client\Exception
|
||||
*/
|
||||
private function sendRequest( $method,
|
||||
$uri,
|
||||
array $headers = [],
|
||||
$body = null,
|
||||
$protocolVersion = '1.1')
|
||||
{
|
||||
|
||||
$response = null;
|
||||
|
||||
try {
|
||||
if( method_exists($this->httpClient, 'sendRequest'))
|
||||
$response = $this->httpClient->sendRequest( $this->requestFactory->createRequest($method, $uri, $headers, $body, $protocolVersion));
|
||||
else $response = $this->httpClient->request($method, $uri, compact('body','headers'));
|
||||
} catch (\Http\Client\Exception\NetworkException $networkException) {
|
||||
throw new NetworkException($networkException->getMessage(), $request, $networkException);
|
||||
} catch (\Exception $exception) {
|
||||
throw new RequestException($exception->getMessage(), $request, $exception);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Http\Response;
|
||||
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
class Response extends JsonResponse
|
||||
{
|
||||
|
||||
protected $reason_phrase = '';
|
||||
protected $reason_code = '';
|
||||
|
||||
public function __construct(mixed $data = null, int $status = 200, array $headers = [])
|
||||
{
|
||||
|
||||
parent::__construct($data , $status, $headers, true);
|
||||
|
||||
if(array_key_exists('errors',$data = json_decode( $this->content, true) )) {
|
||||
$data = $data['errors'][0];
|
||||
$this->reason_phrase = $data['error_message'] ;
|
||||
$this->reason_code = $data['error_message'] ;
|
||||
}
|
||||
}
|
||||
|
||||
public function getReasonPhrase() {
|
||||
return $this->reason_phrase;
|
||||
}
|
||||
|
||||
public function getReasonCode() {
|
||||
return $this->reason_code;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa;
|
||||
|
||||
trait IsValidTypeTrait {
|
||||
|
||||
public static function isValid(string $value) {
|
||||
return in_array($value, self::getTypes());
|
||||
}
|
||||
|
||||
abstract public static function getTypes() : array;
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
use Omnipay\Common\Message\AbstractRequest as Request;
|
||||
use Omnipay\Rotessa\Message\Response\ResponseInterface;
|
||||
|
||||
abstract class AbstractRequest extends Request implements RequestInterface
|
||||
{
|
||||
|
||||
protected $test_mode = false;
|
||||
protected $api_version;
|
||||
protected $method = 'GET';
|
||||
protected $endpoint;
|
||||
protected $api_key;
|
||||
|
||||
public function setApiKey(string $value) {
|
||||
$this->api_key = $value;
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
try {
|
||||
if(empty($this->api_key)) throw new \Exception('No Api Key Found!');
|
||||
$this->validate( ...array_keys($data = $this->getParameters()));
|
||||
} catch (\Throwable $th) {
|
||||
throw new \Omnipay\Rotessa\Exception\ValidationException($th->getMessage() , 600, $th);
|
||||
}
|
||||
|
||||
return (array) $data;
|
||||
}
|
||||
|
||||
abstract public function sendData($data) : ResponseInterface;
|
||||
|
||||
abstract protected function sendRequest(string $method, string $endpoint, array $headers = [], array $data = [] );
|
||||
|
||||
abstract protected function createResponse(array $data) : ResponseInterface;
|
||||
|
||||
abstract public function getEndpointUrl(): string;
|
||||
|
||||
public function getEndpoint() : string {
|
||||
return $this->endpoint;
|
||||
}
|
||||
|
||||
public function getTestMode() {
|
||||
return $this->test_mode;
|
||||
}
|
||||
|
||||
public function setTestMode($mode) {
|
||||
$this->test_mode = $mode;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
|
||||
use Omnipay\Common\Http\ClientInterface;
|
||||
use Omnipay\Rotessa\Http\Response\Response;
|
||||
use Omnipay\Rotessa\Message\Response\BaseResponse;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
use Omnipay\Rotessa\Message\Response\ResponseInterface;
|
||||
use Symfony\Component\HttpFoundation\Request as HttpRequest;
|
||||
|
||||
class BaseRequest extends AbstractRequest implements RequestInterface
|
||||
{
|
||||
protected $base_url = 'rotessa.com';
|
||||
protected $api_version = 1;
|
||||
protected $endpoint = '';
|
||||
|
||||
const ENVIRONMENT_SANDBOX = 'sandbox-api';
|
||||
const ENVIRONMENT_LIVE = 'api';
|
||||
|
||||
function __construct(ClientInterface $http_client = null, HttpRequest $http_request, $model ) {
|
||||
parent::__construct($http_client, $http_request );
|
||||
$this->initialize($model);
|
||||
}
|
||||
|
||||
protected function sendRequest(string $method, string $endpoint, array $headers = [], array $data = [])
|
||||
{
|
||||
/**
|
||||
* @param $method
|
||||
* @param $uri
|
||||
* @param array $headers
|
||||
* @param string|resource|StreamInterface|null $body
|
||||
* @param string $protocolVersion
|
||||
* @return ResponseInterface
|
||||
* @throws \Http\Client\Exception
|
||||
*/
|
||||
$response = $this->httpClient->request($method, $endpoint, $headers, json_encode($data) ) ;
|
||||
$this->response = new Response ($response->getBody()->getContents(), $response->getStatusCode(), $response->getHeaders(), true);
|
||||
}
|
||||
|
||||
|
||||
protected function createResponse(array $data): ResponseInterface {
|
||||
|
||||
return new BaseResponse($this, $data, $this->response->getStatusCode(), $this->response->getReasonPhrase());
|
||||
}
|
||||
|
||||
protected function replacePlaceholder($string, $array) {
|
||||
$pattern = "/\{([^}]+)\}/";
|
||||
$replacement = function($matches) use($array) {
|
||||
$key = $matches[1];
|
||||
if (array_key_exists($key, $array)) {
|
||||
return $array[$key];
|
||||
} else {
|
||||
return $matches[0];
|
||||
}
|
||||
};
|
||||
|
||||
return preg_replace_callback($pattern, $replacement, $string);
|
||||
}
|
||||
|
||||
public function sendData($data) :ResponseInterface {
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'Authorization' => "Token token={$this->api_key}"
|
||||
];
|
||||
|
||||
$this->sendRequest(
|
||||
$this->method,
|
||||
$this->getEndpointUrl(),
|
||||
$headers,
|
||||
$data);
|
||||
|
||||
return $this->createResponse(json_decode($this->response->getContent(), true));
|
||||
}
|
||||
|
||||
public function getEndpoint() : string {
|
||||
return $this->replacePlaceholder($this->endpoint, $this->getParameters());
|
||||
}
|
||||
|
||||
public function getEndpointUrl() : string {
|
||||
return sprintf('https://%s.%s/v%d%s',$this->test_mode ? self::ENVIRONMENT_SANDBOX : self::ENVIRONMENT_LIVE ,$this->base_url, $this->api_version, $this->getEndpoint());
|
||||
}
|
||||
|
||||
public static function hasModel() : bool {
|
||||
return (bool) static::$model;
|
||||
}
|
||||
|
||||
public static function getModel($parameters = []) {
|
||||
$class_name = static::$model;
|
||||
$class_name = "Omnipay\\Rotessa\\Model\\{$class_name}Model";
|
||||
return new $class_name($parameters);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class DeleteTransactionSchedulesId extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/transaction_schedules/{id}';
|
||||
protected $method = 'DELETE';
|
||||
protected static $model = '';
|
||||
|
||||
public function setId(string $value) {
|
||||
$this->setParameter('id',$value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class GetCustomers extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/customers';
|
||||
protected $method = 'GET';
|
||||
protected static $model = '';
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class GetCustomersId extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/customers/{id}';
|
||||
protected $method = 'GET';
|
||||
protected static $model = '';
|
||||
|
||||
|
||||
|
||||
public function setId(int $value) {
|
||||
$this->setParameter('id',$value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class GetTransactionSchedulesId extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/transaction_schedules/{id}';
|
||||
protected $method = 'GET';
|
||||
protected static $model = '';
|
||||
|
||||
public function setId(int $value) {
|
||||
$this->setParameter('id',$value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class PatchCustomersId extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/customers/{id}';
|
||||
protected $method = 'PATCH';
|
||||
protected static $model = 'CustomerPatch';
|
||||
|
||||
public function setId(string $value) {
|
||||
$this->setParameter('id',$value);
|
||||
}
|
||||
public function setCustomIdentifier(string $value) {
|
||||
$this->setParameter('custom_identifier',$value);
|
||||
}
|
||||
public function setName(string $value) {
|
||||
$this->setParameter('name',$value);
|
||||
}
|
||||
public function setEmail(string $value) {
|
||||
$this->setParameter('email',$value);
|
||||
}
|
||||
public function setCustomerType(string $value) {
|
||||
$this->setParameter('customer_type',$value);
|
||||
}
|
||||
public function setHomePhone(string $value) {
|
||||
$this->setParameter('home_phone',$value);
|
||||
}
|
||||
public function setPhone(string $value) {
|
||||
$this->setParameter('phone',$value);
|
||||
}
|
||||
public function setBankName(string $value) {
|
||||
$this->setParameter('bank_name',$value);
|
||||
}
|
||||
public function setInstitutionNumber(string $value) {
|
||||
$this->setParameter('institution_number',$value);
|
||||
}
|
||||
public function setTransitNumber(string $value) {
|
||||
$this->setParameter('transit_number',$value);
|
||||
}
|
||||
public function setBankAccountType(string $value) {
|
||||
$this->setParameter('bank_account_type',$value);
|
||||
}
|
||||
public function setAuthorizationType(string $value) {
|
||||
$this->setParameter('authorization_type',$value);
|
||||
}
|
||||
public function setRoutingNumber(string $value) {
|
||||
$this->setParameter('routing_number',$value);
|
||||
}
|
||||
public function setAccountNumber(string $value) {
|
||||
$this->setParameter('account_number',$value);
|
||||
}
|
||||
public function setAddress(array $value) {
|
||||
$this->setParameter('address',$value);
|
||||
}
|
||||
public function setTransactionSchedules(array $value) {
|
||||
$this->setParameter('transaction_schedules',$value);
|
||||
}
|
||||
public function setFinancialTransactions(array $value) {
|
||||
$this->setParameter('financial_transactions',$value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class PatchTransactionSchedulesId extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/transaction_schedules/{id}';
|
||||
protected $method = 'PATCH';
|
||||
|
||||
public function setId(int $value) {
|
||||
$this->setParameter('id',$value);
|
||||
}
|
||||
public function setAmount(int $value) {
|
||||
$this->setParameter('amount',$value);
|
||||
}
|
||||
public function setComment(string $value) {
|
||||
$this->setParameter('comment',$value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class PostCustomers extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/customers';
|
||||
protected $method = 'POST';
|
||||
protected static $model = 'Customer';
|
||||
|
||||
|
||||
public function setId(string $value) {
|
||||
$this->setParameter('id',$value);
|
||||
}
|
||||
public function setCustomIdentifier(string $value) {
|
||||
$this->setParameter('custom_identifier',$value);
|
||||
}
|
||||
public function setName(string $value) {
|
||||
$this->setParameter('name',$value);
|
||||
}
|
||||
public function setEmail(string $value) {
|
||||
$this->setParameter('email',$value);
|
||||
}
|
||||
public function setCustomerType(string $value) {
|
||||
$this->setParameter('customer_type',$value);
|
||||
}
|
||||
public function setHomePhone(string $value) {
|
||||
$this->setParameter('home_phone',$value);
|
||||
}
|
||||
public function setPhone(string $value) {
|
||||
$this->setParameter('phone',$value);
|
||||
}
|
||||
public function setBankName(string $value) {
|
||||
$this->setParameter('bank_name',$value);
|
||||
}
|
||||
public function setInstitutionNumber(string $value = '') {
|
||||
$this->setParameter('institution_number',$value);
|
||||
}
|
||||
public function setTransitNumber(string $value = '') {
|
||||
$this->setParameter('transit_number',$value);
|
||||
}
|
||||
public function setBankAccountType(string $value) {
|
||||
$this->setParameter('bank_account_type',$value);
|
||||
}
|
||||
public function setAuthorizationType(string $value = '') {
|
||||
$this->setParameter('authorization_type',$value);
|
||||
}
|
||||
public function setRoutingNumber(string $value = '') {
|
||||
$this->setParameter('routing_number',$value);
|
||||
}
|
||||
public function setAccountNumber(string $value) {
|
||||
$this->setParameter('account_number',$value);
|
||||
}
|
||||
public function setAddress(array $value) {
|
||||
$this->setParameter('address',$value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class PostCustomersShowWithCustomIdentifier extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/customers/show_with_custom_identifier';
|
||||
protected $method = 'POST';
|
||||
protected static $model = null;
|
||||
|
||||
|
||||
public function setCustomIdentifier(string $value) {
|
||||
$this->setParameter('custom_identifier',$value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class PostTransactionSchedules extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/transaction_schedules';
|
||||
protected $method = 'POST';
|
||||
protected static $model = 'TransactionSchedule';
|
||||
|
||||
|
||||
|
||||
public function setCustomerId(string $value) {
|
||||
$this->setParameter('customer_id',$value);
|
||||
}
|
||||
public function setProcessDate(string $value) {
|
||||
$this->setParameter('process_date',$value);
|
||||
}
|
||||
public function setFrequency(string $value) {
|
||||
$this->setParameter('frequency',$value);
|
||||
}
|
||||
public function setInstallments(int $value) {
|
||||
$this->setParameter('installments',$value);
|
||||
}
|
||||
public function setComment(string $value) {
|
||||
$this->setParameter('comment',$value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class PostTransactionSchedulesCreateWithCustomIdentifier extends PostTransactionSchedules implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/transaction_schedules/create_with_custom_identifier';
|
||||
protected $method = 'POST';
|
||||
|
||||
public function setCustomIdentifier(string $value) {
|
||||
$this->setParameter('custom_identifier',$value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
// You will need to create this BaseRequest class as abstracted from the AbstractRequest;
|
||||
use Omnipay\Rotessa\Message\Request\BaseRequest;
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
|
||||
class PostTransactionSchedulesUpdateViaPost extends BaseRequest implements RequestInterface
|
||||
{
|
||||
|
||||
protected $endpoint = '/transaction_schedules/update_via_post';
|
||||
protected $method = 'POST';
|
||||
|
||||
|
||||
|
||||
public function setId(int $value) {
|
||||
$this->setParameter('id',$value);
|
||||
}
|
||||
public function setAmount(int $value) {
|
||||
$this->setParameter('amount',$value);
|
||||
}
|
||||
public function setComment(string $value) {
|
||||
$this->setParameter('comment',$value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Request;
|
||||
|
||||
use Omnipay\Rotessa\Message\Response\ResponseInterface;
|
||||
use Omnipay\Common\Message\RequestInterface as MessageInterface;
|
||||
|
||||
interface RequestInterface extends MessageInterface
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Response;
|
||||
|
||||
use Omnipay\Common\Message\AbstractResponse as Response;
|
||||
|
||||
abstract class AbstractResponse extends Response implements ResponseInterface
|
||||
{
|
||||
|
||||
abstract public function getData();
|
||||
|
||||
abstract public function getCode();
|
||||
|
||||
abstract public function getMessage();
|
||||
|
||||
abstract public function getParameter(string $key);
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Response;
|
||||
|
||||
use Omnipay\Rotessa\Message\Request\RequestInterface;
|
||||
use Omnipay\Rotessa\Message\Response\ResponseInterface;
|
||||
use Omnipay\Common\Message\AbstractResponse as Response;
|
||||
|
||||
class BaseResponse extends Response implements ResponseInterface
|
||||
{
|
||||
|
||||
protected $code = 0;
|
||||
protected $message = null;
|
||||
|
||||
function __construct(RequestInterface $request, array $data = [], int $code = 200, string $message = null ) {
|
||||
parent::__construct($request, $data);
|
||||
|
||||
$this->code = $code;
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
return $this->getParameters();
|
||||
}
|
||||
|
||||
public function getCode() {
|
||||
return (int) $this->code;
|
||||
}
|
||||
|
||||
public function isSuccessful() {
|
||||
return $this->code < 300;
|
||||
}
|
||||
|
||||
public function getMessage() {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
protected function getParameters() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getParameter(string $key) {
|
||||
return $this->getParameters()[$key];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Message\Response;
|
||||
|
||||
use Omnipay\Common\Message\ResponseInterface as MessageInterface;
|
||||
|
||||
interface ResponseInterface extends MessageInterface
|
||||
{
|
||||
public function getParameter(string $key) ;
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Model;
|
||||
|
||||
use Omnipay\Common\ParametersTrait;
|
||||
use Omnipay\Rotessa\Model\ModelInterface;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
use Omnipay\Rotessa\Exception\ValidationException;
|
||||
|
||||
abstract class AbstractModel implements ModelInterface {
|
||||
|
||||
use ParametersTrait;
|
||||
|
||||
abstract public function jsonSerialize() : array;
|
||||
|
||||
public function validate() : bool {
|
||||
$required = array_diff_key( array_flip($this->required), array_filter($this->getParameters()) );
|
||||
if(!empty($required)) throw new ValidationException("Could not validate " . implode(",", array_keys($required)) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function __get($key) {
|
||||
return array_key_exists($key, $this->attributes) ? $this->getParameter($key) : null;
|
||||
}
|
||||
|
||||
public function __set($key, $value) {
|
||||
if(array_key_exists($key, $this->attributes)) $this->setParameter($key, $value);
|
||||
}
|
||||
|
||||
public function __toString() : string {
|
||||
return json_encode($this);
|
||||
}
|
||||
|
||||
public function toString() : string {
|
||||
return $this->__toString();
|
||||
}
|
||||
|
||||
public function __toArray() : array {
|
||||
return $this->getParameters();
|
||||
}
|
||||
|
||||
|
||||
public function toArray() : array {
|
||||
return $this->__toArray();
|
||||
}
|
||||
|
||||
public function initialize(array $params = []) {
|
||||
$this->parameters = new ParameterBag;
|
||||
$parameters = array_merge($this->defaults, $params);
|
||||
if ($parameters) {
|
||||
foreach ($this->attributes as $param => $type) {
|
||||
$value = @$parameters[$param];
|
||||
if($value){
|
||||
settype($value, $type);
|
||||
$this->setParameter($param, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Model;
|
||||
|
||||
use \DateTime;
|
||||
use Omnipay\Rotessa\Model\AbstractModel;
|
||||
use Omnipay\Rotessa\Model\ModelInterface;
|
||||
|
||||
class BaseModel extends AbstractModel implements ModelInterface {
|
||||
|
||||
protected $attributes = [
|
||||
"id" => "string"
|
||||
];
|
||||
protected $required = ['id'];
|
||||
protected $defaults = ['id' => 0 ];
|
||||
|
||||
public function __construct($parameters = array()) {
|
||||
$this->initialize($parameters);
|
||||
}
|
||||
|
||||
public function jsonSerialize() : array {
|
||||
return array_intersect_key($this->toArray(), array_flip($this->required) );
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Model;
|
||||
|
||||
use Omnipay\Rotessa\Object\Country;
|
||||
use Omnipay\Rotessa\Object\Address;
|
||||
use Omnipay\Rotessa\Model\BaseModel;
|
||||
use Omnipay\Rotessa\Object\CustomerType;
|
||||
use Omnipay\Rotessa\Model\ModelInterface;
|
||||
use Omnipay\Rotessa\Object\BankAccountType;
|
||||
use Omnipay\Rotessa\Object\AuthorizationType;
|
||||
use Omnipay\Rotessa\Exception\ValidationException;
|
||||
|
||||
class CustomerModel extends BaseModel implements ModelInterface {
|
||||
|
||||
protected $attributes = [
|
||||
"id" => "string",
|
||||
"custom_identifier" => "string",
|
||||
"name" => "string",
|
||||
"email" => "string",
|
||||
"customer_type" => "string",
|
||||
"home_phone" => "string",
|
||||
"phone" => "string",
|
||||
"bank_name" => "string",
|
||||
"institution_number" => "string",
|
||||
"transit_number" => "string",
|
||||
"bank_account_type" => "string",
|
||||
"authorization_type" => "string",
|
||||
"routing_number" => "string",
|
||||
"account_number" => "string",
|
||||
"address" => "object",
|
||||
"transaction_schedules" => "array",
|
||||
"financial_transactions" => "array",
|
||||
"active" => "bool"
|
||||
];
|
||||
|
||||
protected $defaults = ["active" => false,"customer_type" =>'Business',"bank_account_type" =>'Savings',"authorization_type" =>'Online',];
|
||||
protected $required = ["name","email","customer_type","home_phone","phone","bank_name","institution_number","transit_number","bank_account_type","authorization_type","routing_number","account_number","address",'custom_identifier'];
|
||||
|
||||
public function validate() : bool {
|
||||
try {
|
||||
$country = $this->address->country;
|
||||
if(!self::isValidCountry($country)) throw new \Exception("Invalid country!");
|
||||
|
||||
$this->required = array_diff($this->required, Country::isAmerican($country) ? ["institution_number", "transit_number"] : ["bank_account_type", "routing_number"]);
|
||||
parent::validate();
|
||||
if(Country::isCanadian($country) ) {
|
||||
if(!self::isValidTransitNumber($this->getParameter('transit_number'))) throw new \Exception("Invalid transit number!");
|
||||
if(!self::isValidInstitutionNumber($this->getParameter('institution_number'))) throw new \Exception("Invalid institution number!");
|
||||
}
|
||||
if(!self::isValidCustomerType($this->getParameter('customer_type'))) throw new \Exception("Invalid customer type!");
|
||||
if(!self::isValidBankAccountType($this->getParameter('bank_account_type'))) throw new \Exception("Invalid bank account type!");
|
||||
if(!self::isValidAuthorizationType($this->getParameter('authorization_type'))) throw new \Exception("Invalid authorization type!");
|
||||
} catch (\Throwable $th) {
|
||||
throw new ValidationException($th->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function isValidCountry(string $country ) : bool {
|
||||
return Country::isValidCountryCode($country) || Country::isValidCountryName($country);
|
||||
}
|
||||
|
||||
public static function isValidTransitNumber(string $value ) : bool {
|
||||
return strlen($value) == 5;
|
||||
}
|
||||
|
||||
public static function isValidInstitutionNumber(string $value ) : bool {
|
||||
return strlen($value) == 3;
|
||||
}
|
||||
|
||||
public static function isValidCustomerType(string $value ) : bool {
|
||||
return CustomerType::isValid($value);
|
||||
}
|
||||
|
||||
public static function isValidBankAccountType(string $value ) : bool {
|
||||
return BankAccountType::isValid($value);
|
||||
}
|
||||
|
||||
public static function isValidAuthorizationType(string $value ) : bool {
|
||||
return AuthorizationType::isValid($value);
|
||||
}
|
||||
|
||||
public function toArray() : array {
|
||||
return [ 'address' => (array) $this->getParameter('address') ] + parent::toArray();
|
||||
}
|
||||
|
||||
public function jsonSerialize() : array {
|
||||
$address = (array) $this->getParameter('address');
|
||||
unset($address['country']);
|
||||
|
||||
return compact('address') + parent::jsonSerialize();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Model;
|
||||
|
||||
use Omnipay\Rotessa\Object\Country;
|
||||
use Omnipay\Rotessa\Object\Address;
|
||||
use Omnipay\Rotessa\Object\CustomerType;
|
||||
use Omnipay\Rotessa\Model\ModelInterface;
|
||||
use Omnipay\Rotessa\Object\BankAccountType;
|
||||
use Omnipay\Rotessa\Object\AuthorizationType;
|
||||
use Omnipay\Rotessa\Exception\ValidationException;
|
||||
|
||||
class CustomerPatchModel extends CustomerModel implements ModelInterface {
|
||||
|
||||
protected $required = ["id","custom_identifier","name","email","customer_type","home_phone","phone","bank_name","institution_number","transit_number","bank_account_type","authorization_type","routing_number","account_number","address"];
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Model;
|
||||
|
||||
interface ModelInterface extends \JsonSerializable
|
||||
{
|
||||
public function __toArray();
|
||||
public function __toString();
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Model;
|
||||
|
||||
use \DateTime;
|
||||
use Omnipay\Rotessa\Model\BaseModel;
|
||||
use Omnipay\Rotessa\Object\Frequency;
|
||||
use Omnipay\Rotessa\Model\ModelInterface;
|
||||
use Omnipay\Rotessa\Exception\ValidationException;
|
||||
|
||||
class TransactionScheduleModel extends BaseModel implements ModelInterface {
|
||||
|
||||
protected $properties;
|
||||
|
||||
protected $attributes = [
|
||||
"id" => "string",
|
||||
"amount" => "float",
|
||||
"comment" => "string",
|
||||
"created_at" => "date",
|
||||
"financial_transactions" => "array",
|
||||
"frequency" => "string",
|
||||
"installments" => "integer",
|
||||
"next_process_date" => "date",
|
||||
"process_date" => "date",
|
||||
"updated_at" => "date",
|
||||
"customer_id" => "string",
|
||||
"custom_identifier" => "string",
|
||||
];
|
||||
|
||||
public const DATE_FORMAT = 'F j, Y';
|
||||
|
||||
protected $defaults = ["amount" =>0.00,"comment" =>' ',"financial_transactions" =>0,"frequency" =>'Once',"installments" =>1];
|
||||
|
||||
protected $required = ["amount","comment","frequency","installments","process_date"];
|
||||
|
||||
public function validate() : bool {
|
||||
try {
|
||||
parent::validate();
|
||||
if(!self::isValidDate($this->process_date)) throw new \Exception("Could not validate date ");
|
||||
if(!self::isValidFrequency($this->frequency)) throw new \Exception("Invalid frequency");
|
||||
if(is_null($this->customer_id) && is_null($this->custom_identifier)) throw new \Exception("customer id or custom identifier is invalid");
|
||||
} catch (\Throwable $th) {
|
||||
throw new ValidationException($th->getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function jsonSerialize() : array {
|
||||
return ['customer_id' => $this->getParameter('customer_id'), 'custom_identifier' => $this->getParameter('custom_identifier') ] + parent::jsonSerialize() ;
|
||||
}
|
||||
|
||||
public function __toArray() : array {
|
||||
return parent::__toArray() ;
|
||||
}
|
||||
|
||||
public function initialize(array $params = [] ) {
|
||||
$o_params = array_intersect_key(
|
||||
$params = array_intersect_key($params, $this->attributes),
|
||||
($attr = array_filter($this->attributes, fn($p) => $p != "date"))
|
||||
);
|
||||
parent::initialize($o_params);
|
||||
$d_params = array_diff_key($params, $attr);
|
||||
array_walk($d_params, function($v,$k) {
|
||||
$this->setParameter($k, self::formatDate( $v) );
|
||||
}, );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function isValidDate($date) : bool {
|
||||
$d = DateTime::createFromFormat(self::DATE_FORMAT, $date);
|
||||
// Check if the date is valid and matches the format
|
||||
return $d && $d->format(self::DATE_FORMAT) === $date;
|
||||
}
|
||||
|
||||
public static function isValidFrequency($value) : bool {
|
||||
return Frequency::isValid($value);
|
||||
}
|
||||
|
||||
protected static function formatDate($date) : string {
|
||||
$d = new DateTime($date);
|
||||
return $d->format(self::DATE_FORMAT);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Model;
|
||||
|
||||
use Omnipay\Rotessa\Model\BaseModel;
|
||||
use Omnipay\Rotessa\Model\ModelInterface;
|
||||
|
||||
class TransactionSchedulesIdBodyModel extends BaseModel implements ModelInterface {
|
||||
|
||||
protected $properties;
|
||||
|
||||
protected $attributes = [
|
||||
"amount" => "int",
|
||||
"comment" => "string",
|
||||
];
|
||||
|
||||
public const DATE_FORMAT = 'Y-m-d H:i:s';
|
||||
|
||||
private $_is_error = false;
|
||||
|
||||
protected $defaults = ["amount" =>0,"comment" =>'0',];
|
||||
|
||||
protected $required = ["amount","comment",];
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Model;
|
||||
|
||||
use Omnipay\Rotessa\Model\BaseModel;
|
||||
use Omnipay\Rotessa\Model\ModelInterface;
|
||||
|
||||
class TransactionSchedulesUpdateViaPostBodyModel extends BaseModel implements ModelInterface {
|
||||
|
||||
protected $properties;
|
||||
|
||||
protected $attributes = [
|
||||
"id" => "int",
|
||||
"amount" => "int",
|
||||
"comment" => "string",
|
||||
];
|
||||
|
||||
public const DATE_FORMAT = 'Y-m-d H:i:s';
|
||||
|
||||
private $_is_error = false;
|
||||
|
||||
protected $defaults = ["amount" =>0,"comment" =>'0',];
|
||||
|
||||
protected $required = ["amount","comment",];
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
<?php
|
||||
namespace Omnipay\Rotessa\Object;
|
||||
|
||||
use Omnipay\Common\ParametersTrait;
|
||||
|
||||
final class Address implements \JsonSerializable {
|
||||
|
||||
use ParametersTrait;
|
||||
|
||||
protected $attributes = [
|
||||
"address_1" => "string",
|
||||
"address_2" => "string",
|
||||
"city" => "string",
|
||||
"id" => "int",
|
||||
"postal_code" => "string",
|
||||
"province_code" => "string",
|
||||
"country" => "string"
|
||||
];
|
||||
|
||||
protected $required = ["address_1","address_2","city","postal_code","province_code",];
|
||||
|
||||
public function jsonSerialize() {
|
||||
return array_intersect_key($this->getParameters(), array_flip($this->required));
|
||||
}
|
||||
|
||||
public function getCountry() : string {
|
||||
return $this->getParameter('country');
|
||||
}
|
||||
|
||||
public function initialize(array $parameters) {
|
||||
foreach($this->attributes as $param => $type) {
|
||||
$value = @$parameters[$param] ;
|
||||
settype($value, $type);
|
||||
$value = $value ?? null;
|
||||
$this->parameters->set($param, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function __toArray() : array {
|
||||
return $this->getParameters();
|
||||
}
|
||||
|
||||
public function __toString() : string {
|
||||
return $this->getFullAddress();
|
||||
}
|
||||
|
||||
public function getFullAddress() :string {
|
||||
$full_address = $this->getParameters();
|
||||
extract($full_address);
|
||||
|
||||
return "$address_1 $address_2, $city, $postal_code $province_code, $country";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Object;
|
||||
|
||||
use Omnipay\Rotessa\IsValidTypeTrait;
|
||||
|
||||
final class AuthorizationType {
|
||||
|
||||
use isValidTypeTrait;
|
||||
|
||||
const IN_PERSON = "In Person";
|
||||
const ONLINE = "Online";
|
||||
|
||||
public static function isInPerson($value) {
|
||||
return $value === self::IN_PERSON;
|
||||
}
|
||||
|
||||
public static function isOnline($value) {
|
||||
return $value === self::ONLINE;
|
||||
}
|
||||
|
||||
public static function getTypes() : array {
|
||||
return [
|
||||
self::IN_PERSON,
|
||||
self::ONLINE
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Object;
|
||||
|
||||
use Omnipay\Rotessa\IsValidTypeTrait;
|
||||
|
||||
final class BankAccountType {
|
||||
|
||||
use IsValidTypeTrait;
|
||||
|
||||
const SAVINGS = "Savings";
|
||||
const CHECKING = "Checking";
|
||||
|
||||
public static function isSavings($value) {
|
||||
return $value === self::SAVINGS;
|
||||
}
|
||||
|
||||
public static function isChecking($value) {
|
||||
return $value === self::Checking;
|
||||
}
|
||||
|
||||
public static function getTypes() : array {
|
||||
return [
|
||||
self::SAVINGS,
|
||||
self::CHECKING
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Object;
|
||||
|
||||
use Omnipay\Rotessa\IsValidTypeTrait;
|
||||
|
||||
final class Country {
|
||||
|
||||
use IsValidTypeTrait;
|
||||
|
||||
protected static $codes = ['CA','US'];
|
||||
protected static $names = ['United States', 'Canada'];
|
||||
|
||||
public static function isValidCountryName(string $value) {
|
||||
return in_array($value, self::$names);
|
||||
}
|
||||
|
||||
public static function isValidCountryCode(string $value) {
|
||||
return in_array($value, self::$codes);
|
||||
}
|
||||
|
||||
public static function isAmerican(string $value) : bool {
|
||||
return $value == 'US' || $value == 'United States';
|
||||
}
|
||||
|
||||
public static function isCanadian(string $value) : bool {
|
||||
return $value == 'CA' || $value == 'Canada';
|
||||
}
|
||||
|
||||
public static function getTypes() : array {
|
||||
return $codes + $names;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Object;
|
||||
|
||||
use Omnipay\Rotessa\IsValidTypeTrait;
|
||||
|
||||
final class CustomerType {
|
||||
|
||||
use IsValidTypeTrait;
|
||||
|
||||
const PERSONAL = "Personal";
|
||||
const BUSINESS = "Business";
|
||||
|
||||
public static function isPersonal($value) {
|
||||
return $value === self::PERSONAL;
|
||||
}
|
||||
|
||||
public static function isBusiness($value) {
|
||||
return $value === self::BUSINESS;
|
||||
}
|
||||
|
||||
public static function getTypes() : array {
|
||||
return [
|
||||
self::PERSONAL,
|
||||
self::BUSINESS
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Omnipay\Rotessa\Object;
|
||||
|
||||
use Omnipay\Rotessa\IsValidTypeTrait;
|
||||
|
||||
final class Frequency {
|
||||
|
||||
use IsValidTypeTrait;
|
||||
|
||||
const ONCE = "Once";
|
||||
const WEEKLY = "Weekly";
|
||||
const OTHER_WEEK = "Every Other Week";
|
||||
const MONTHLY= "Monthly";
|
||||
const OTHER_MONTH = "Every Other Month";
|
||||
const QUARTERLY = "Quarterly";
|
||||
const SEMI_ANNUALLY = "Semi-Annually";
|
||||
const YEARLY = "Yearly";
|
||||
|
||||
public static function isOnce($value) {
|
||||
return $value === self::ONCE;
|
||||
}
|
||||
|
||||
public static function isWeekly($value) {
|
||||
return $value === self::WEEKLY;
|
||||
}
|
||||
|
||||
public static function isOtherWeek($value) {
|
||||
return $value === self::OTHER_WEEK;
|
||||
}
|
||||
|
||||
public static function isMonthly($value) {
|
||||
return $value === self::MONTHLY;
|
||||
}
|
||||
|
||||
public static function isOtherMonth($value) {
|
||||
return $value === self::OTHER_MONTH;
|
||||
}
|
||||
|
||||
public static function isQuarterly($value) {
|
||||
return $value === self::QUARTERLY;
|
||||
}
|
||||
|
||||
public static function isSemiAnnually($value) {
|
||||
return $value === self::SEMI_ANNUALLY;
|
||||
}
|
||||
|
||||
public static function isYearly($value) {
|
||||
return $value === self::YEARLY;
|
||||
}
|
||||
|
||||
public static function getTypes() : array {
|
||||
return [
|
||||
self::ONCE,
|
||||
self::WEEKLY,
|
||||
self::OTHER_WEEK,
|
||||
self::MONTHLY,
|
||||
self::OTHER_MONTH,
|
||||
self::QUARTERLY,
|
||||
self::SEMI_ANNUALLY,
|
||||
self::YEARLY
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -11,24 +11,19 @@
|
|||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use Omnipay\Omnipay;
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\Models\Client;
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\PaymentHash;
|
||||
use Illuminate\Support\Arr;
|
||||
use App\Models\GatewayType;
|
||||
use Omnipay\Rotessa\Gateway;
|
||||
use App\Models\ClientContact;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\PaymentDrivers\BaseDriver;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\PaymentDrivers\Rotessa\Resources\Customer;
|
||||
use App\PaymentDrivers\Rotessa\PaymentMethod as Acss;
|
||||
use App\PaymentDrivers\Rotessa\PaymentMethod as BankTransfer;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class RotessaPaymentDriver extends BaseDriver
|
||||
{
|
||||
|
|
@ -40,24 +35,15 @@ class RotessaPaymentDriver extends BaseDriver
|
|||
|
||||
public $can_authorise_credit_card = true;
|
||||
|
||||
public Gateway $gateway;
|
||||
|
||||
public $payment_method;
|
||||
|
||||
public static $methods = [
|
||||
GatewayType::BANK_TRANSFER => BankTransfer::class,
|
||||
//GatewayType::BACS => Bacs::class,
|
||||
GatewayType::ACSS => Acss::class,
|
||||
// GatewayType::DIRECT_DEBIT => DirectDebit::class
|
||||
];
|
||||
|
||||
public function init(): self
|
||||
{
|
||||
|
||||
$this->gateway = Omnipay::create(
|
||||
$this->company_gateway->gateway->provider
|
||||
);
|
||||
$this->gateway->initialize((array) $this->company_gateway->getConfig());
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
@ -116,73 +102,61 @@ class RotessaPaymentDriver extends BaseDriver
|
|||
}
|
||||
|
||||
public function importCustomers() {
|
||||
$this->init();
|
||||
|
||||
try {
|
||||
if(!$result = Cache::has("rotessa-import_customers-{$this->company_gateway->company->company_key}")) {
|
||||
$result = $this->gateway->getCustomers()->send();
|
||||
if(!$result->isSuccessful()) throw new \Exception($result->getMessage(), (int) $result->getCode());
|
||||
// cache results
|
||||
Cache::put("rotessa-import_customers-{$this->company_gateway->company->company_key}", $result->getData(), 60 * 60 * 24);
|
||||
}
|
||||
|
||||
$result = Cache::get("rotessa-import_customers-{$this->company_gateway->company->company_key}");
|
||||
$customers = collect($result)->unique('email');
|
||||
$result = $this->gatewayRequest('get','customers',[]); //Rotessa customers
|
||||
|
||||
if($result->failed())
|
||||
$result->throw();
|
||||
|
||||
$customers = collect($result->json())->unique('email'); //Rotessa customer emails
|
||||
|
||||
$client_emails = $customers->pluck('email')->all();
|
||||
|
||||
$company_id = $this->company_gateway->company->id;
|
||||
// get existing customers
|
||||
$client_contacts = ClientContact::where('company_id', $company_id)->whereIn('email', $client_emails )->whereNull('deleted_at')->get();
|
||||
$client_contacts = ClientContact::where('company_id', $company_id)
|
||||
->whereIn('email', $client_emails )
|
||||
->whereHas('client', function ($q){
|
||||
$q->where('is_deleted', false);
|
||||
})
|
||||
->whereNull('deleted_at')
|
||||
->get();
|
||||
|
||||
$client_contacts = $client_contacts->map(function($item, $key) use ($customers) {
|
||||
return array_merge([], (array) $customers->firstWhere("email", $item->email) , ['custom_identifier' => $item->client->number, 'identifier' => $item->client->number, 'client_id' => $item->client->id ]);
|
||||
return array_merge($customers->firstWhere("email", $item->email),['custom_identifier' => $item->client->number, 'identifier' => $item->client->number, 'client_id' => $item->client->id ]);
|
||||
} );
|
||||
|
||||
// create payment methods
|
||||
$client_contacts->each(
|
||||
function($contact) use ($customers) {
|
||||
$result = $this->gateway->getCustomersId(['id' => ($contact = (object) $contact)->id])->send();
|
||||
$this->client = Client::find($contact->client_id);
|
||||
$customer = (new Customer($result->getData()))->additional(['id' => $contact->id, 'custom_identifier' => $contact->custom_identifier ] );
|
||||
$this->findOrCreateCustomer($customer->additional + $customer->jsonSerialize());
|
||||
collect($client_contacts)->each(
|
||||
function($contact) {
|
||||
|
||||
$contact = (object)$contact;
|
||||
|
||||
$result = $this->gatewayRequest("get","customers/{$contact->id}");
|
||||
$result = $result->json();
|
||||
|
||||
$this->client = Client::query()->find($contact->client_id);
|
||||
|
||||
$customer = array_merge($result, ['id' => $contact->id, 'custom_identifier' => $contact->custom_identifier ]);
|
||||
|
||||
$this->findOrCreateCustomer($customer);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
// create new clients from rotessa customers
|
||||
$client_emails = $client_contacts->pluck('email')->all();
|
||||
|
||||
$client_contacts = $customers->filter(function ($value, $key) use ($client_emails) {
|
||||
return !in_array(((object) $value)->email, $client_emails);
|
||||
})->each( function($customer) use ($company_id) {
|
||||
// create new client contact from rotess customer
|
||||
$customer = (object) $this->gateway->getCustomersId(['id' => ($customer = (object) $customer)->id])->send()->getData();
|
||||
/**
|
||||
{
|
||||
"account_number": "11111111"
|
||||
"active": true,
|
||||
"address": {
|
||||
"address_1": "123 Main Street",
|
||||
"address_2": "Unit 4",
|
||||
"city": "Birmingham",
|
||||
"id": 114397,
|
||||
"postal_code": "36016",
|
||||
"province_code": "AL"
|
||||
},
|
||||
"authorization_type": "Online",
|
||||
"bank_account_type": "Checking",
|
||||
"bank_name": "Scotiabank",
|
||||
"created_at": "2015-02-10T23:50:45.000-06:00",
|
||||
"custom_identifier": "Mikey",
|
||||
"customer_type": "Personal",
|
||||
"email": "mikesmith@test.com",
|
||||
"financial_transactions": [],
|
||||
"home_phone": "(204) 555 5555",
|
||||
"id": 1,
|
||||
"identifier": "Mikey",
|
||||
"institution_number": "",
|
||||
"name": "Mike Smith",
|
||||
"phone": "(204) 555 4444",
|
||||
"routing_number": "111111111",
|
||||
"transaction_schedules": [],
|
||||
"transit_number": "",
|
||||
"updated_at": "2015-02-10T23:50:45.000-06:00"
|
||||
}
|
||||
*/
|
||||
|
||||
$customer = $this->gatewayRequest("get", "customers/{$customer['id']}")->json();
|
||||
|
||||
$settings = ClientSettings::defaults();
|
||||
$settings->currency_id = $this->company_gateway->company->getSetting('currency_id');
|
||||
$customer = (object)$customer;
|
||||
$client = (\App\Factory\ClientFactory::create($this->company_gateway->company_id, $this->company_gateway->user_id))->fill(
|
||||
[
|
||||
'address1' => $customer->address['address_1'] ?? '',
|
||||
|
|
@ -192,7 +166,8 @@ class RotessaPaymentDriver extends BaseDriver
|
|||
'state' => $customer->address['province_code'] ?? '',
|
||||
'country_id' => empty($customer->transit_number) ? 840 : 124,
|
||||
'routing_id' => empty(($r = $customer->routing_number))? null : $r,
|
||||
"number" => str_pad($customer->account_number,3,'0',STR_PAD_LEFT)
|
||||
"number" => str_pad($customer->account_number,3,'0',STR_PAD_LEFT),
|
||||
"settings" => $settings,
|
||||
]
|
||||
);
|
||||
$client->saveQuietly();
|
||||
|
|
@ -207,8 +182,7 @@ class RotessaPaymentDriver extends BaseDriver
|
|||
$client->contacts()->saveMany([$contact]);
|
||||
$contact = $client->contacts()->first();
|
||||
$this->client = $client;
|
||||
$customer = (new Customer((array) $customer))->additional(['id' => $customer->id, 'custom_identifier' => $customer->custom_identifier ?? $contact->id ] );
|
||||
$this->findOrCreateCustomer($customer->additional + $customer->jsonSerialize());
|
||||
|
||||
});
|
||||
} catch (\Throwable $th) {
|
||||
$data = [
|
||||
|
|
@ -228,40 +202,46 @@ class RotessaPaymentDriver extends BaseDriver
|
|||
|
||||
public function findOrCreateCustomer(array $data)
|
||||
{
|
||||
nlog($data);
|
||||
|
||||
$result = null;
|
||||
try {
|
||||
|
||||
$existing = ClientGatewayToken::query()
|
||||
->where('company_gateway_id', $this->company_gateway->id)
|
||||
->where('client_id', $this->client->id)
|
||||
->orWhere(function (Builder $query) use ($data) {
|
||||
$query->where('token', encrypt(join(".", Arr::only($data, 'id','custom_identifier'))) )
|
||||
->where('gateway_customer_reference', Arr::only($data,'id'));
|
||||
})
|
||||
->where('is_deleted',0)
|
||||
->where('gateway_customer_reference', Arr::only($data,'id'))
|
||||
->exists();
|
||||
if ($existing) return true;
|
||||
else if(!Arr::has($data,'id')) {
|
||||
$result = $this->gateway->authorize($data)->send();
|
||||
if (!$result->isSuccessful()) throw new \Exception($result->getMessage(), (int) $result->getCode());
|
||||
|
||||
$customer = new Customer($result->getData());
|
||||
$data = array_filter($customer->resolve());
|
||||
if ($existing)
|
||||
return true;
|
||||
|
||||
if(!isset($data['id'])) {
|
||||
|
||||
nlog("no id, lets goo");
|
||||
$result = $this->gatewayRequest('post', 'customers', $data);
|
||||
|
||||
if($result->failed())
|
||||
$result->throw();
|
||||
|
||||
$data = $result->json();
|
||||
nlog($data);
|
||||
}
|
||||
|
||||
// $payment_method_id = Arr::has($data,'address.postal_code') && ((int) $data['address']['postal_code'])? GatewayType::BANK_TRANSFER: GatewayType::ACSS;
|
||||
// TODO: Check/ Validate postal code between USA vs CAN
|
||||
$payment_method_id = GatewayType::ACSS;
|
||||
|
||||
$gateway_token = $this->storeGatewayToken( [
|
||||
'payment_meta' => $data + ['brand' => 'Rotessa', 'last4' => $data['bank_name'], 'type' => $data['bank_account_type'] ],
|
||||
'token' => encrypt(join(".", Arr::only($data, 'id','custom_identifier'))),
|
||||
'payment_meta' => ['brand' => 'Bank Transfer', 'last4' => substr($data['account_number'], -4), 'type' => GatewayType::ACSS ],
|
||||
'token' => join(".", Arr::only($data, ['id','custom_identifier'])),
|
||||
'payment_method_id' => $payment_method_id ,
|
||||
], ['gateway_customer_reference' =>
|
||||
$data['id']
|
||||
, 'routing_number' => Arr::has($data,'routing_number') ? $data['routing_number'] : $data['transit_number'] ]);
|
||||
], [
|
||||
'gateway_customer_reference' => $data['id'],
|
||||
'routing_number' => Arr::has($data,'routing_number') ? $data['routing_number'] : $data['transit_number']
|
||||
]);
|
||||
|
||||
return $data['id'];
|
||||
|
||||
throw new \Exception($result->getMessage(), (int) $result->getCode());
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
$data = [
|
||||
|
|
@ -269,7 +249,7 @@ class RotessaPaymentDriver extends BaseDriver
|
|||
'transaction_response' => $th->getMessage(),
|
||||
'success' => false,
|
||||
'description' => $th->getMessage(),
|
||||
'code' =>(int) $th->getCode()
|
||||
'code' => 500
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(['server_response' => is_null($result) ? '' : $result->getMessage(), 'data' => $data], SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, 880 , $this->client, $this->company_gateway->company);
|
||||
|
|
@ -277,4 +257,20 @@ class RotessaPaymentDriver extends BaseDriver
|
|||
throw $th;
|
||||
}
|
||||
}
|
||||
|
||||
public function gatewayRequest($verb, $uri, $payload = [])
|
||||
{
|
||||
$r = Http::withToken($this->company_gateway->getConfigField('apiKey'))
|
||||
->{$verb}($this->getUrl().$uri, $payload);
|
||||
|
||||
nlog($r->body());
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
private function getUrl(): string
|
||||
{
|
||||
return $this->company_gateway->getConfigField('testMode') ? 'https://sandbox-api.rotessa.com/v1/' : 'https://api.rotessa.com/v1/';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,11 @@ class ChargeRefunded implements ShouldQueue
|
|||
|
||||
$payment_hash_key = $source['metadata']['payment_hash'] ?? null;
|
||||
|
||||
if(is_null($payment_hash_key)){
|
||||
nlog("charge.refunded not found");
|
||||
return;
|
||||
}
|
||||
|
||||
$payment_hash = PaymentHash::query()->where('hash', $payment_hash_key)->first();
|
||||
$company_gateway = $payment_hash->payment->company_gateway;
|
||||
|
||||
|
|
|
|||
|
|
@ -59,38 +59,22 @@ class PaymentIntentProcessingWebhook implements ShouldQueue
|
|||
/* Stub processing payment intents with a pending payment */
|
||||
public function handle()
|
||||
{
|
||||
nlog($this->stripe_request);
|
||||
// The first payment will always be a PI payment - subsequent are PY
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
||||
|
||||
$company = Company::query()->where('company_key', $this->company_key)->first();
|
||||
|
||||
foreach ($this->stripe_request as $transaction) {
|
||||
|
||||
|
||||
$payment = Payment::query()
|
||||
->where('company_id', $company->id)
|
||||
->where(function ($query) use ($transaction) {
|
||||
|
||||
if(isset($transaction['payment_intent'])) {
|
||||
$query->where('transaction_reference', $transaction['payment_intent']);
|
||||
}
|
||||
|
||||
if(isset($transaction['payment_intent']) && isset($transaction['id'])) {
|
||||
$query->orWhere('transaction_reference', $transaction['id']);
|
||||
}
|
||||
|
||||
if(!isset($transaction['payment_intent']) && isset($transaction['id'])) {
|
||||
$query->where('transaction_reference', $transaction['id']);
|
||||
}
|
||||
|
||||
})
|
||||
->where('transaction_reference', $transaction['id'])
|
||||
->first();
|
||||
|
||||
|
||||
|
||||
if ($payment) {
|
||||
$payment->status_id = Payment::STATUS_PENDING;
|
||||
$payment->save();
|
||||
|
||||
nlog("found payment");
|
||||
$this->payment_completed = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ class ComposerServiceProvider extends ServiceProvider
|
|||
$view->with('states', $states);
|
||||
});
|
||||
|
||||
// CAProvinces View Composer
|
||||
view()->composer(['*.rotessa.components.address','*.rotessa.components.banks.CA.bank','*.rotessa.components.dropdowns.country.CA'], function ($view) {
|
||||
$provinces = CAProvinces::get();
|
||||
$view->with('provinces', $provinces);
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Http\Middleware\ThrottleRequestsWithPredis;
|
||||
use App\Models\Scheduler;
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Scheduler;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use App\Http\Middleware\ThrottleRequestsWithPredis;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
|
@ -65,7 +65,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||
if (Ninja::isSelfHost()) {
|
||||
return Limit::none();
|
||||
} else {
|
||||
return Limit::perMinute(300)->by($request->ip());
|
||||
return Limit::perMinute(500)->by($request->ip());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -46,16 +46,16 @@ class TaskRepository extends BaseRepository
|
|||
$this->new_task = false;
|
||||
}
|
||||
|
||||
if(isset($data['assigned_user_id']) && $data['assigned_user_id'] != $task->assigned_user_id){
|
||||
TaskAssigned::dispatch($task, $task->company->db)->delay(2);
|
||||
}
|
||||
|
||||
if(!is_numeric($task->rate) && !isset($data['rate']))
|
||||
$data['rate'] = 0;
|
||||
|
||||
$task->fill($data);
|
||||
$task->saveQuietly();
|
||||
|
||||
if(isset($data['assigned_user_id']) && $data['assigned_user_id'] != $task->assigned_user_id) {
|
||||
TaskAssigned::dispatch($task, $task->company->db)->delay(2);
|
||||
}
|
||||
|
||||
$this->init($task);
|
||||
|
||||
if ($this->new_task && ! $task->status_id) {
|
||||
|
|
@ -155,6 +155,8 @@ class TaskRepository extends BaseRepository
|
|||
$this->saveDocuments($data['documents'], $task);
|
||||
}
|
||||
|
||||
$this->calculateProjectDuration($task);
|
||||
|
||||
return $task;
|
||||
}
|
||||
|
||||
|
|
@ -261,6 +263,8 @@ class TaskRepository extends BaseRepository
|
|||
$task->saveQuietly();
|
||||
}
|
||||
|
||||
$this->calculateProjectDuration($task);
|
||||
|
||||
return $task;
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +306,10 @@ class TaskRepository extends BaseRepository
|
|||
$task->saveQuietly();
|
||||
}
|
||||
|
||||
$this->calculateProjectDuration($task);
|
||||
|
||||
return $task;
|
||||
|
||||
}
|
||||
|
||||
public function triggeredActions($request, $task)
|
||||
|
|
@ -348,4 +355,67 @@ class TaskRepository extends BaseRepository
|
|||
|
||||
return $task->number;
|
||||
}
|
||||
|
||||
private function calculateProjectDuration(Task $task)
|
||||
{
|
||||
|
||||
if($task->project) {
|
||||
|
||||
$duration = 0;
|
||||
|
||||
$task->project->tasks->each(function ($task) use (&$duration) {
|
||||
|
||||
if(is_iterable(json_decode($task->time_log))) {
|
||||
|
||||
foreach(json_decode($task->time_log) as $log) {
|
||||
|
||||
if(!is_array($log)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$start_time = $log[0];
|
||||
$end_time = $log[1] == 0 ? time() : $log[1];
|
||||
|
||||
$duration += $end_time - $start_time;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$task->project->current_hours = (int) round(($duration / 60 / 60), 0);
|
||||
$task->push();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
*/
|
||||
public function restore($task)
|
||||
{
|
||||
if (!$task->trashed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::restore($task);
|
||||
|
||||
$this->calculateProjectDuration($task);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
*/
|
||||
public function delete($task)
|
||||
{
|
||||
if ($task->is_deleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::delete($task);
|
||||
|
||||
$this->calculateProjectDuration($task);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,12 @@
|
|||
|
||||
namespace App\Services\Chart;
|
||||
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Task;
|
||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* Class ChartCalculations.
|
||||
|
|
@ -170,4 +173,215 @@ trait ChartCalculations
|
|||
return $result;
|
||||
|
||||
}
|
||||
|
||||
public function getLoggedTasks($data): int|float
|
||||
{
|
||||
|
||||
$q = $this->taskQuery($data);
|
||||
|
||||
return $this->taskCalculations($q, $data);
|
||||
|
||||
}
|
||||
|
||||
public function getPaidTasks($data): int|float
|
||||
{
|
||||
$q = $this->taskQuery($data);
|
||||
$q->whereHas('invoice', function ($query){
|
||||
$query->where('status_id', 4)->where('is_deleted', 0);
|
||||
});
|
||||
|
||||
return $this->taskCalculations($q, $data);
|
||||
|
||||
}
|
||||
|
||||
public function getInvoicedTasks($data): int|float
|
||||
{
|
||||
|
||||
$q = $this->taskQuery($data);
|
||||
$q->whereHas('invoice');
|
||||
|
||||
return $this->taskCalculations($q, $data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* All Expenses
|
||||
*/
|
||||
public function getLoggedExpenses($data): int|float
|
||||
{
|
||||
$q = $this->expenseQuery($data);
|
||||
|
||||
return $this->expenseCalculations($q, $data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Expenses that should be invoiced - but are not yet invoiced.
|
||||
*/
|
||||
public function getPendingExpenses($data): int|float
|
||||
{
|
||||
|
||||
$q = $this->expenseQuery($data);
|
||||
$q->where('should_be_invoiced', true)->whereNull('invoice_id');
|
||||
return $this->expenseCalculations($q, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoiced.
|
||||
*/
|
||||
public function getInvoicedExpenses($data): int|float
|
||||
{
|
||||
|
||||
$q = $this->expenseQuery($data);
|
||||
$q->whereNotNull('invoice_id');
|
||||
return $this->expenseCalculations($q, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Paid.
|
||||
*/
|
||||
public function getPaidExpenses($data): int|float
|
||||
{
|
||||
|
||||
$q = $this->expenseQuery($data);
|
||||
$q->whereNotNull('payment_date');
|
||||
return $this->expenseCalculations($q, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Paid.
|
||||
*/
|
||||
public function getInvoicedPaidExpenses($data): int|float
|
||||
{
|
||||
|
||||
$q = $this->expenseQuery($data);
|
||||
$q->whereNotNull('invoice_id')->whereNotNull('payment_date');
|
||||
return $this->expenseCalculations($q, $data);
|
||||
}
|
||||
|
||||
private function expenseCalculations(Builder $query, array $data): int|float
|
||||
{
|
||||
|
||||
$result = 0;
|
||||
$calculated = $this->expenseCalculator($query, $data);
|
||||
|
||||
match ($data['calculation']) {
|
||||
'sum' => $result = $calculated->sum(),
|
||||
'avg' => $result = $calculated->avg(),
|
||||
'count' => $result = $query->count(),
|
||||
default => $result = 0,
|
||||
};
|
||||
|
||||
return $result;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function expenseCalculator(Builder $query, array $data)
|
||||
{
|
||||
|
||||
return $query->get()
|
||||
->when($data['currency_id'] == '999', function ($collection) {
|
||||
$collection->map(function ($e) {
|
||||
/** @var \App\Models\Expense $e */
|
||||
return $e->amount * $e->exchange_rate;
|
||||
});
|
||||
})
|
||||
->when($data['currency_id'] != '999', function ($collection) {
|
||||
|
||||
$collection->map(function ($e) {
|
||||
|
||||
/** @var \App\Models\Expense $e */
|
||||
return $e->amount;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private function expenseQuery($data): Builder
|
||||
{
|
||||
$query = Expense::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
|
||||
if(in_array($data['period'], ['current,previous'])) {
|
||||
$query->whereBetween('date', [$data['start_date'], $data['end_date']]);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
private function taskMoneyCalculator($query, $data)
|
||||
{
|
||||
|
||||
return $query->get()
|
||||
->when($data['currency_id'] == '999', function ($collection) {
|
||||
$collection->map(function ($t) {
|
||||
return $t->taskCompanyValue();
|
||||
});
|
||||
})
|
||||
->when($data['currency_id'] != '999', function ($collection) {
|
||||
|
||||
$collection->map(function ($t) {
|
||||
return $t->taskValue();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private function taskQuery($data): Builder
|
||||
{
|
||||
$q = Task::query()
|
||||
->withTrashed()
|
||||
->where('company_id', $this->company->id)
|
||||
->where('is_deleted', 0);
|
||||
|
||||
if(in_array($data['period'], ['current,previous'])) {
|
||||
$q->whereBetween('calculated_start_date', [$data['start_date'], $data['end_date']]);
|
||||
}
|
||||
|
||||
return $q;
|
||||
|
||||
}
|
||||
|
||||
private function taskCalculations(Builder $q, array $data): int|float
|
||||
{
|
||||
|
||||
$result = 0;
|
||||
$calculated = collect();
|
||||
|
||||
if($data['calculation'] != 'count' && $data['format'] == 'money') {
|
||||
if($data['currency_id'] != '999') {
|
||||
|
||||
$q->whereHas('client', function ($query) use ($data) {
|
||||
$query->where('settings->currency_id', $data['currency_id']);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$calculated = $this->taskMoneyCalculator($q, $data);
|
||||
|
||||
}
|
||||
|
||||
if($data['calculation'] != 'count' && $data['format'] == 'time') {
|
||||
$calculated = $q->get()->map(function ($t) {
|
||||
return $t->calcDuration();
|
||||
});
|
||||
}
|
||||
|
||||
match ($data['calculation']) {
|
||||
'sum' => $result = $calculated->sum(),
|
||||
'avg' => $result = $calculated->avg(),
|
||||
'count' => $result = $q->count(),
|
||||
default => $result = 0,
|
||||
};
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -224,6 +224,8 @@ class ChartService
|
|||
* period - current/previous
|
||||
* calculation - sum/count/avg
|
||||
*
|
||||
* May require currency_id
|
||||
*
|
||||
* date_range - this_month
|
||||
* or
|
||||
* start_date - end_date
|
||||
|
|
@ -234,18 +236,18 @@ class ChartService
|
|||
|
||||
match($data['field']){
|
||||
'active_invoices' => $results = $this->getActiveInvoices($data),
|
||||
'outstanding_invoices' => $results = 0,
|
||||
'completed_payments' => $results = 0,
|
||||
'refunded_payments' => $results = 0,
|
||||
'active_quotes' => $results = 0,
|
||||
'unapproved_quotes' => $results = 0,
|
||||
'logged_tasks' => $results = 0,
|
||||
'invoiced_tasks' => $results = 0,
|
||||
'paid_tasks' => $results = 0,
|
||||
'logged_expenses' => $results = 0,
|
||||
'pending_expenses' => $results = 0,
|
||||
'invoiced_expenses' => $results = 0,
|
||||
'invoice_paid_expenses' => $results = 0,
|
||||
'outstanding_invoices' => $results = $this->getOutstandingInvoices($data),
|
||||
'completed_payments' => $results = $this->getCompletedPayments($data),
|
||||
'refunded_payments' => $results = $this->getRefundedPayments($data),
|
||||
'active_quotes' => $results = $this->getActiveQuotes($data),
|
||||
'unapproved_quotes' => $results = $this->getUnapprovedQuotes($data),
|
||||
'logged_tasks' => $results = $this->getLoggedTasks($data),
|
||||
'invoiced_tasks' => $results = $this->getInvoicedTasks($data),
|
||||
'paid_tasks' => $results = $this->getPaidTasks($data),
|
||||
'logged_expenses' => $results = $this->getLoggedExpenses($data),
|
||||
'pending_expenses' => $results = $this->getPendingExpenses($data),
|
||||
'invoiced_expenses' => $results = $this->getInvoicedExpenses($data),
|
||||
'invoice_paid_expenses' => $results = $this->getInvoicedPaidExpenses($data),
|
||||
default => $results = 0,
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -124,7 +124,7 @@ class TemplateService
|
|||
$this->twig->addFilter($filter);
|
||||
|
||||
$allowedTags = ['if', 'for', 'set', 'filter'];
|
||||
$allowedFilters = ['escape', 'e', 'upper', 'lower', 'capitalize', 'filter', 'length', 'merge','format_currency', 'format_number','format_percent_number','map', 'join', 'first', 'date', 'sum', 'number_format'];
|
||||
$allowedFilters = ['replace', 'escape', 'e', 'upper', 'lower', 'capitalize', 'filter', 'length', 'merge','format_currency', 'format_number','format_percent_number','map', 'join', 'first', 'date', 'sum', 'number_format','nl2br'];
|
||||
$allowedFunctions = ['range', 'cycle', 'constant', 'date',];
|
||||
$allowedProperties = ['type_id'];
|
||||
$allowedMethods = ['img','t'];
|
||||
|
|
@ -323,6 +323,9 @@ class TemplateService
|
|||
$template = $template->render($this->data);
|
||||
|
||||
$f = $this->document->createDocumentFragment();
|
||||
|
||||
$template = htmlspecialchars($template, ENT_XML1, 'UTF-8');
|
||||
|
||||
$f->appendXML(html_entity_decode($template));
|
||||
|
||||
$replacements[] = $f;
|
||||
|
|
@ -482,6 +485,8 @@ class TemplateService
|
|||
default => $processed = [],
|
||||
};
|
||||
|
||||
// nlog(json_encode($processed));
|
||||
|
||||
return $processed;
|
||||
|
||||
})->toArray();
|
||||
|
|
|
|||
|
|
@ -17,77 +17,67 @@ use Illuminate\Support\Str;
|
|||
|
||||
class TranslationHelper
|
||||
{
|
||||
public static function getIndustries()
|
||||
{
|
||||
// public static function getIndustries()
|
||||
// {
|
||||
|
||||
/** @var \Illuminate\Support\Collection<\App\Models\Currency> */
|
||||
$industries = app('industries');
|
||||
// /** @var \Illuminate\Support\Collection<\App\Models\Currency> */
|
||||
// $industries = app('industries');
|
||||
|
||||
return $industries->each(function ($industry) {
|
||||
$industry->name = ctrans('texts.industry_'.$industry->name);
|
||||
})->sortBy(function ($industry) {
|
||||
return $industry->name;
|
||||
});
|
||||
}
|
||||
// return $industries->each(function ($industry) {
|
||||
// $industry->name = ctrans('texts.industry_'.$industry->name);
|
||||
// })->sortBy(function ($industry) {
|
||||
// return $industry->name;
|
||||
// });
|
||||
// }
|
||||
|
||||
public static function getCountries()
|
||||
{
|
||||
|
||||
/** @var \Illuminate\Support\Collection<\App\Models\Country> */
|
||||
// $countries = app('countries');
|
||||
return app('countries');
|
||||
|
||||
return \App\Models\Country::all()->each(function ($country) {
|
||||
$country->name = ctrans('texts.country_'.$country->name);
|
||||
})->sortBy(function ($country) {
|
||||
return $country->iso_3166_2;
|
||||
});
|
||||
}
|
||||
|
||||
public static function getPaymentTypes()
|
||||
{
|
||||
// public static function getPaymentTypes()
|
||||
// {
|
||||
|
||||
/** @var \Illuminate\Support\Collection<\App\Models\PaymentType> */
|
||||
// $payment_types = app('payment_types');
|
||||
// /** @var \Illuminate\Support\Collection<\App\Models\PaymentType> */
|
||||
// // $payment_types = app('payment_types');
|
||||
|
||||
return \App\Models\PaymentType::all()->each(function ($pType) {
|
||||
$pType->name = ctrans('texts.payment_type_'.$pType->name);
|
||||
})->sortBy(function ($pType) {
|
||||
return $pType->name;
|
||||
});
|
||||
}
|
||||
// return \App\Models\PaymentType::all()->each(function ($pType) {
|
||||
// $pType->name = ctrans('texts.payment_type_'.$pType->name);
|
||||
// })->sortBy(function ($pType) {
|
||||
// return $pType->name;
|
||||
// });
|
||||
// }
|
||||
|
||||
public static function getLanguages()
|
||||
{
|
||||
// public static function getLanguages()
|
||||
// {
|
||||
|
||||
/** @var \Illuminate\Support\Collection<\App\Models\Language> */
|
||||
// $languages = app('languages');
|
||||
// /** @var \Illuminate\Support\Collection<\App\Models\Language> */
|
||||
// // $languages = app('languages');
|
||||
|
||||
return \App\Models\Language::all()->each(function ($lang) {
|
||||
$lang->name = ctrans('texts.lang_'.$lang->name);
|
||||
})->sortBy(function ($lang) {
|
||||
return $lang->name;
|
||||
});
|
||||
}
|
||||
// return \App\Models\Language::all()->each(function ($lang) {
|
||||
// $lang->name = ctrans('texts.lang_'.$lang->name);
|
||||
// })->sortBy(function ($lang) {
|
||||
// return $lang->name;
|
||||
// });
|
||||
// }
|
||||
|
||||
public static function getCurrencies()
|
||||
{
|
||||
|
||||
/** @var \Illuminate\Support\Collection<\App\Models\Currency> */
|
||||
// $currencies = app('currencies');
|
||||
return app('currencies');
|
||||
|
||||
return \App\Models\Currency::all()->each(function ($currency) {
|
||||
$currency->name = ctrans('texts.currency_'.Str::slug($currency->name, '_'));
|
||||
})->sortBy(function ($currency) {
|
||||
return $currency->name;
|
||||
});
|
||||
}
|
||||
|
||||
public static function getPaymentTerms()
|
||||
{
|
||||
return PaymentTerm::getCompanyTerms()->map(function ($term) {
|
||||
$term['name'] = ctrans('texts.payment_terms_net').' '.$term['num_days'];
|
||||
// public static function getPaymentTerms()
|
||||
// {
|
||||
// return PaymentTerm::getCompanyTerms()->map(function ($term) {
|
||||
// $term['name'] = ctrans('texts.payment_terms_net').' '.$term['num_days'];
|
||||
|
||||
return $term;
|
||||
});
|
||||
}
|
||||
// return $term;
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,14 +74,12 @@
|
|||
"league/csv": "^9.6",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
"league/fractal": "^0.20.0",
|
||||
"league/omnipay": "^3.1",
|
||||
"livewire/livewire": "^3",
|
||||
"microsoft/microsoft-graph": "^1.69",
|
||||
"mollie/mollie-api-php": "^2.36",
|
||||
"nelexa/zip": "^4.0",
|
||||
"nordigen/nordigen-php": "^1.1",
|
||||
"nwidart/laravel-modules": "^11.0",
|
||||
"omnipay/paypal": "^3.0",
|
||||
"phpoffice/phpspreadsheet": "^1.29",
|
||||
"pragmarx/google2fa": "^8.0",
|
||||
"predis/predis": "^2",
|
||||
|
|
@ -133,7 +131,8 @@
|
|||
"app/Helpers/Generic.php",
|
||||
"app/Helpers/ClientPortal.php"
|
||||
],
|
||||
"classmap": ["app/PaymentDrivers/Rotessa/vendor/karneaud/omnipay-rotessa/src/Omnipay/Rotessa/"]
|
||||
"classmap": [
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
|
@ -186,7 +185,7 @@
|
|||
"url": "https://github.com/turbo124/apple"
|
||||
},
|
||||
{
|
||||
"type":"vcs",
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/invoiceninja/einvoice"
|
||||
},
|
||||
{
|
||||
|
|
@ -204,4 +203,4 @@
|
|||
],
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -17,8 +17,8 @@ return [
|
|||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => env('APP_VERSION', '5.10.16'),
|
||||
'app_tag' => env('APP_TAG', '5.10.16'),
|
||||
'app_version' => env('APP_VERSION', '5.10.19'),
|
||||
'app_tag' => env('APP_TAG', '5.10.19'),
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
|
|
|
|||
|
|
@ -12,18 +12,18 @@ return new class extends Migration
|
|||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Company::whereNotNull('tax_data')
|
||||
->cursor()
|
||||
->each(function($company){
|
||||
// Company::whereNotNull('tax_data')
|
||||
// ->cursor()
|
||||
// ->each(function($company){
|
||||
|
||||
if($company->tax_data?->version == 'alpha')
|
||||
{
|
||||
// if($company->tax_data?->version == 'alpha' && ($company->tax_data->seller_subregion ?? false))
|
||||
// {
|
||||
|
||||
$company->update(['tax_data' => new \App\DataMapper\Tax\TaxModel($company->tax_data)]);
|
||||
// $company->update(['tax_data' => new \App\DataMapper\Tax\TaxModel($company->tax_data)]);
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
});
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if(Ninja::isSelfHost())
|
||||
{
|
||||
|
||||
Company::whereNotNull('tax_data')
|
||||
->cursor()
|
||||
->each(function ($company) {
|
||||
|
||||
if($company->tax_data?->version == 'alpha' && ($company->tax_data->seller_subregion ?? false)) {
|
||||
|
||||
$company->update(['tax_data' => new \App\DataMapper\Tax\TaxModel($company->tax_data)]);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
||||
|
|
@ -5309,7 +5309,8 @@ $lang = array(
|
|||
'account_holder_information' => 'Account Holder Information',
|
||||
'enter_information_for_the_account_holder' => 'Enter Information for the Account Holder',
|
||||
'customer_type' => 'Customer Type',
|
||||
'process_date' => 'Process Date'
|
||||
'process_date' => 'Process Date',
|
||||
'forever_free' => 'Forever Free',
|
||||
);
|
||||
|
||||
return $lang;
|
||||
|
|
|
|||
1425
lang/nl/texts.php
1425
lang/nl/texts.php
File diff suppressed because it is too large
Load Diff
13780
openapi/api-docs.yaml
13780
openapi/api-docs.yaml
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue