mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge branch 'v5-develop' into v5-2805-tracking-campaign-source
This commit is contained in:
commit
bd97b916e4
@ -1 +1 @@
|
||||
5.1.65
|
||||
5.1.70
|
@ -12,12 +12,12 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\Ninja\SendReminders;
|
||||
use App\Jobs\Util\WebHookHandler;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Webhook;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Jobs\Util\WebhookHandler;
|
||||
|
||||
class SendRemindersCron extends Command
|
||||
{
|
||||
@ -54,8 +54,8 @@ class SendRemindersCron extends Command
|
||||
{
|
||||
SendReminders::dispatchNow();
|
||||
|
||||
$this->webHookOverdueInvoices();
|
||||
$this->webHookExpiredQuotes();
|
||||
$this->webHookOverdueInvoices();
|
||||
$this->webHookExpiredQuotes();
|
||||
}
|
||||
|
||||
private function webHookOverdueInvoices()
|
||||
@ -90,6 +90,7 @@ class SendRemindersCron extends Command
|
||||
|
||||
$invoices->each(function ($invoice) {
|
||||
WebHookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
|
||||
|
||||
});
|
||||
|
||||
$quotes = Quote::where('is_deleted', 0)
|
||||
|
@ -68,7 +68,7 @@ class CompanySettings extends BaseSettings
|
||||
public $inclusive_taxes = false; //@implemented
|
||||
public $quote_footer = ''; //@implmented
|
||||
|
||||
public $translations; //@TODO not used anywhere
|
||||
public $translations;
|
||||
|
||||
public $counter_number_applied = 'when_saved'; // when_saved , when_sent //@implemented
|
||||
public $quote_number_applied = 'when_saved'; // when_saved , when_sent //@implemented
|
||||
@ -245,8 +245,8 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $hide_paid_to_date = false; //@TODO where?
|
||||
public $embed_documents = false; //@TODO where?
|
||||
public $all_pages_header = false; //@implemented
|
||||
public $all_pages_footer = false; //@implemented
|
||||
public $all_pages_header = false; //@deprecated 31-05-2021
|
||||
public $all_pages_footer = false; //@deprecated 31-05-2021
|
||||
public $pdf_variables = ''; //@implemented
|
||||
|
||||
public $portal_custom_head = ''; //@TODO @BEN
|
||||
|
@ -132,8 +132,11 @@ class ForgotPasswordController extends Controller
|
||||
: $this->sendResetLinkFailedResponse($request, $response);
|
||||
}
|
||||
|
||||
public function showLinkRequestForm()
|
||||
public function showLinkRequestForm(Request $request)
|
||||
{
|
||||
return $this->render('auth.passwords.request', ['root' => 'themes']);
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
|
||||
return $this->render('auth.passwords.request', ['root' => 'themes', 'account' => $account]);
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
|
||||
@ -489,49 +490,37 @@ class LoginController extends BaseController
|
||||
if (request()->has('code')) {
|
||||
return $this->handleProviderCallback($provider);
|
||||
} else {
|
||||
return Socialite::driver($provider)->scopes($scopes)->redirect();
|
||||
return Socialite::driver($provider)->with(['redirect_uri' => config('ninja.app_url')."/auth/google"])->scopes($scopes)->redirect();
|
||||
}
|
||||
}
|
||||
|
||||
public function handleProviderCallback(string $provider)
|
||||
{
|
||||
$socialite_user = Socialite::driver($provider)
|
||||
->stateless()
|
||||
->user();
|
||||
|
||||
// if($user = OAuth::handleAuth($socialite_user, $provider))
|
||||
// {
|
||||
// Auth::login($user, true);
|
||||
if($user = OAuth::handleAuth($socialite_user, $provider))
|
||||
{
|
||||
|
||||
// return redirect($this->redirectTo);
|
||||
// }
|
||||
// else if(MultiDB::checkUserEmailExists($socialite_user->getEmail()))
|
||||
// {
|
||||
// Session::flash('error', 'User exists in system, but not with this authentication method'); //todo add translations
|
||||
nlog('found user and updating their user record');
|
||||
|
||||
// return view('auth.login');
|
||||
// }
|
||||
// else {
|
||||
// //todo
|
||||
// $name = OAuth::splitName($socialite_user->getName());
|
||||
$update_user = [
|
||||
'first_name' => $name[0],
|
||||
'last_name' => $name[1],
|
||||
'password' => '',
|
||||
'email' => $socialite_user->getEmail(),
|
||||
'oauth_user_id' => $socialite_user->getId(),
|
||||
'oauth_provider_id' => $provider,
|
||||
'oauth_user_token' => $socialite_user->refreshToken,
|
||||
];
|
||||
|
||||
// $new_account = [
|
||||
// 'first_name' => $name[0],
|
||||
// 'last_name' => $name[1],
|
||||
// 'password' => '',
|
||||
// 'email' => $socialite_user->getEmail(),
|
||||
// 'oauth_user_id' => $socialite_user->getId(),
|
||||
// 'oauth_provider_id' => $provider
|
||||
// ];
|
||||
$user->update($update_user);
|
||||
|
||||
// $account = CreateAccount::dispatchNow($new_account);
|
||||
|
||||
// Auth::login($account->default_company->owner(), true);
|
||||
|
||||
// $cookie = cookie('db', $account->default_company->db);
|
||||
|
||||
// return redirect($this->redirectTo)->withCookie($cookie);
|
||||
// }
|
||||
}
|
||||
else {
|
||||
nlog("user not found for oauth");
|
||||
}
|
||||
|
||||
return redirect('/#/');
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
@ -52,7 +53,10 @@ class ResetPasswordController extends Controller
|
||||
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
{
|
||||
return $this->render('auth.passwords.reset', ['root' => 'themes', 'token' => $token]);
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
|
||||
return $this->render('auth.passwords.reset', ['root' => 'themes', 'token' => $token, 'account' => $account]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,6 +15,7 @@ use App\Events\Client\ClientWasCreated;
|
||||
use App\Events\Client\ClientWasUpdated;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Filters\ClientFilters;
|
||||
use App\Http\Requests\Client\AdjustClientLedgerRequest;
|
||||
use App\Http\Requests\Client\CreateClientRequest;
|
||||
use App\Http\Requests\Client\DestroyClientRequest;
|
||||
use App\Http\Requests\Client\EditClientRequest;
|
||||
@ -585,4 +586,64 @@ class ClientController extends BaseController
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UploadClientRequest $request
|
||||
* @param Client $client
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Put(
|
||||
* path="/api/v1/clients/{id}/adjust_ledger",
|
||||
* operationId="adjustLedger",
|
||||
* tags={"clients"},
|
||||
* summary="Adjust the client ledger to rebalance",
|
||||
* description="Adjust the client ledger to rebalance",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Client Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the client object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/Client"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
//@deprecated - not available
|
||||
public function adjustLedger(AdjustClientLedgerRequest $request, Client $client)
|
||||
{
|
||||
// $adjustment = $request->input('adjustment');
|
||||
// $notes = $request->input('notes');
|
||||
|
||||
// $client->service()->updateBalance
|
||||
}
|
||||
|
||||
}
|
||||
|
42
app/Http/Controllers/Gateways/Checkout3dsController.php
Normal file
42
app/Http/Controllers/Gateways/Checkout3dsController.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Gateways;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Gateways\Checkout3ds\Checkout3dsRequest;
|
||||
|
||||
class Checkout3dsController extends Controller
|
||||
{
|
||||
public function index(Checkout3dsRequest $request, string $company_key, string $company_gateway_id, string $hash)
|
||||
{
|
||||
if (!$request->getCompany()) {
|
||||
return response()->json(['message' => 'Company record not found.', 'company_key' => $company_key]);
|
||||
}
|
||||
|
||||
if (!$request->getCompanyGateway()) {
|
||||
return response()->json(['message' => 'Company gateway record not found.', 'company_gateway_id' => $company_gateway_id]);
|
||||
}
|
||||
|
||||
if (!$request->getPaymentHash()) {
|
||||
return response()->json(['message' => 'Hash record not found.', 'hash' => $hash]);
|
||||
}
|
||||
|
||||
if (!$request->getClient()) {
|
||||
return response()->json(['message' => 'Client record not found.']);
|
||||
}
|
||||
|
||||
return $request->getCompanyGateway()
|
||||
->driver($request->getClient())
|
||||
->process3dsConfirmation($request);
|
||||
}
|
||||
}
|
96
app/Http/Controllers/ImportJsonController.php
Normal file
96
app/Http/Controllers/ImportJsonController.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Import\ImportJsonRequest;
|
||||
use App\Jobs\Company\CompanyExport;
|
||||
use App\Jobs\Company\CompanyImport;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ImportJsonController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/import_json",
|
||||
* operationId="getImportJson",
|
||||
* tags={"import"},
|
||||
* summary="Import data from the system",
|
||||
* description="Import data from the system",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="success",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function index(ImportJsonRequest $request)
|
||||
{
|
||||
|
||||
$import_file = $request->file('files');
|
||||
|
||||
$contents = $this->unzipFile($import_file->getPathname());
|
||||
|
||||
$hash = Str::random(32);
|
||||
|
||||
Cache::put( $hash, base64_encode( $contents ), 3600 );
|
||||
|
||||
CompanyImport::dispatch(auth()->user()->getCompany(), auth()->user(), $hash, $request->all());
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
}
|
||||
|
||||
private function unzipFile($file_contents)
|
||||
{
|
||||
$zip = new ZipArchive();
|
||||
$archive = $zip->open($file_contents);
|
||||
|
||||
$filename = pathinfo($file_contents, PATHINFO_FILENAME);
|
||||
$zip->extractTo(public_path("storage/backups/{$filename}"));
|
||||
$zip->close();
|
||||
$file_location = public_path("storage/backups/$filename/backup.json");
|
||||
|
||||
if (! file_exists($file_location))
|
||||
throw new NonExistingMigrationFile('Backup file does not exist, or is corrupted.');
|
||||
|
||||
$data = json_decode(file_get_contents($file_location));
|
||||
|
||||
unlink($file_contents);
|
||||
unlink($file_location);
|
||||
|
||||
return $data
|
||||
}
|
||||
}
|
@ -845,13 +845,11 @@ class InvoiceController extends BaseController
|
||||
*/
|
||||
public function deliveryNote(ShowInvoiceRequest $request, Invoice $invoice)
|
||||
{
|
||||
|
||||
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||
|
||||
try {
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
} catch (\Exception $e) {
|
||||
return response(['message' => 'Oops, something went wrong. Make sure you have symlink to storage/ in public/ directory.'], 500);
|
||||
}
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -99,9 +99,10 @@ class PreviewController extends BaseController
|
||||
|
||||
$entity_obj->load('client');
|
||||
|
||||
App::setLocale($entity_obj->client->primary_contact()->preferredLocale());
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
|
||||
$t = app('translator');
|
||||
App::setLocale($entity_obj->client->primary_contact()->preferredLocale());
|
||||
$t->replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
|
||||
|
||||
$html = new HtmlEngine($entity_obj->invitations()->first());
|
||||
|
||||
@ -151,7 +152,8 @@ class PreviewController extends BaseController
|
||||
private function blankEntity()
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations(auth()->user()->company()->settings));
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations(auth()->user()->company()->settings));
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
|
@ -163,7 +163,7 @@ class SetupController extends Controller
|
||||
|
||||
/* Create the first account. */
|
||||
if (Account::count() == 0) {
|
||||
CreateAccount::dispatchNow($request->all());
|
||||
CreateAccount::dispatchNow($request->all(), $request->getClientIp());
|
||||
}
|
||||
|
||||
VersionCheck::dispatchNow();
|
||||
|
@ -112,6 +112,7 @@ class Kernel extends HttpKernel
|
||||
VerifyCsrfToken::class,
|
||||
SubstituteBindings::class,
|
||||
QueryLogging::class,
|
||||
Cors::class,
|
||||
],
|
||||
'shop' => [
|
||||
'throttle:120,1',
|
||||
|
@ -16,7 +16,7 @@ class Cors
|
||||
// ALLOW OPTIONS METHOD
|
||||
$headers = [
|
||||
'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE',
|
||||
'Access-Control-Allow-Headers'=> 'X-API-COMPANY-KEY,X-CLIENT-VERSION,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range',
|
||||
'Access-Control-Allow-Headers'=> 'X-API-COMPANY-KEY,X-CLIENT-VERSION,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,X-CSRF-TOKEN,X-LIVEWIRE',
|
||||
];
|
||||
|
||||
return Response::make('OK', 200, $headers);
|
||||
@ -27,7 +27,7 @@ class Cors
|
||||
$response->headers->set('Access-Control-Allow-Origin', '*');
|
||||
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
$response->headers->set('Access-Control-Allow-Headers', 'X-API-COMPANY-KEY,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range');
|
||||
$response->headers->set('Access-Control-Expose-Headers', 'X-APP-VERSION,X-MINIMUM-CLIENT-VERSION');
|
||||
$response->headers->set('Access-Control-Expose-Headers', 'X-APP-VERSION,X-MINIMUM-CLIENT-VERSION,X-CSRF-TOKEN,X-LIVEWIRE');
|
||||
$response->headers->set('X-APP-VERSION', config('ninja.app_version'));
|
||||
$response->headers->set('X-MINIMUM-CLIENT-VERSION', config('ninja.minimum_client_version'));
|
||||
|
||||
|
55
app/Http/Requests/Client/AdjustClientLedgerRequest.php
Normal file
55
app/Http/Requests/Client/AdjustClientLedgerRequest.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Client;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class AdjustClientLedgerRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->client);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
|
||||
$rules = [];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Gateways\Checkout3ds;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class Checkout3dsRequest extends FormRequest
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public function getCompany()
|
||||
{
|
||||
return Company::where('company_key', $this->company_key)->first();
|
||||
}
|
||||
|
||||
public function getCompanyGateway()
|
||||
{
|
||||
return CompanyGateway::find($this->decodePrimaryKey($this->company_gateway_id));
|
||||
}
|
||||
|
||||
public function getPaymentHash()
|
||||
{
|
||||
return PaymentHash::where('hash', $this->hash)->first();
|
||||
}
|
||||
|
||||
public function getClient()
|
||||
{
|
||||
return Client::find($this->getPaymentHash()->data->client_id);
|
||||
}
|
||||
}
|
39
app/Http/Requests/Import/ImportJsonRequest.php
Normal file
39
app/Http/Requests/Import/ImportJsonRequest.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Import;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class ImportJsonRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
// 'import_type' => 'required',
|
||||
// 'files' => 'required_without:hash|array|min:1|max:6',
|
||||
// 'hash' => 'nullable|string',
|
||||
// 'column_map' => 'required_with:hash|array',
|
||||
// 'skip_header' => 'required_with:hash|boolean',
|
||||
// 'files.*' => 'file|mimes:csv,txt',
|
||||
];
|
||||
}
|
||||
}
|
@ -100,14 +100,15 @@ class PaymentWebhookRequest extends Request
|
||||
/**
|
||||
* Resolve client from payment hash.
|
||||
*
|
||||
* @return null|\App\Models\Client
|
||||
* @return null|\App\Models\Client|bool
|
||||
*/
|
||||
public function getClient()
|
||||
{
|
||||
$hash = $this->getPaymentHash();
|
||||
|
||||
if($hash)
|
||||
if($hash) {
|
||||
return Client::find($hash->data->client_id)->firstOrFail();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ class AttachableUser implements Rule
|
||||
if(!$user)
|
||||
return true;
|
||||
|
||||
$user_already_attached = CompanyUser::query()
|
||||
$user_already_attached = CompanyUser::query()
|
||||
->where('user_id', $user->id)
|
||||
->where('account_id',$user->account_id)
|
||||
->where('company_id', auth()->user()->company()->id)
|
||||
|
@ -13,6 +13,7 @@ namespace App\Http\ViewComposers;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\TranslationHelper;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\View\View;
|
||||
|
||||
@ -34,7 +35,9 @@ class PortalComposer
|
||||
$view->with($this->portalData());
|
||||
|
||||
if (auth()->user()) {
|
||||
Lang::replace(Ninja::transformTranslations(auth()->user()->client->getMergedSettings()));
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations(auth()->user()->client->getMergedSettings()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ use App\Models\RecurringInvoice;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
use App\Models\User;
|
||||
use App\Models\VendorContact;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@ -121,7 +122,7 @@ class CompanyExport implements ShouldQueue
|
||||
$user->account_id = $this->encodePrimaryKey($user->account_id);
|
||||
// $user->id = $this->encodePrimaryKey($user->id);
|
||||
|
||||
return $user;
|
||||
return $user->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -205,26 +206,27 @@ class CompanyExport implements ShouldQueue
|
||||
$credit = $this->transformBasicEntities($credit);
|
||||
$credit = $this->transformArrayOfKeys($credit, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id','invoice_id']);
|
||||
|
||||
return $credit;
|
||||
return $credit->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['credit_invitations'] = CreditInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($credit){
|
||||
|
||||
$credit = $this->transformArrayOfKeys($credit, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
|
||||
$credit = $this->transformArrayOfKeys($credit, ['company_id', 'user_id', 'client_contact_id', 'credit_id']);
|
||||
|
||||
return $credit;
|
||||
return $credit->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['designs'] = $this->company->user_designs->makeHidden(['id'])->all();
|
||||
|
||||
$this->export_data['documents'] = $this->company->documents->map(function ($document){
|
||||
$this->export_data['documents'] = $this->company->all_documents->map(function ($document){
|
||||
|
||||
$document = $this->transformArrayOfKeys($document, ['user_id', 'assigned_user_id', 'company_id', 'project_id', 'vendor_id']);
|
||||
$document = $this->transformArrayOfKeys($document, ['user_id', 'assigned_user_id', 'company_id', 'project_id', 'vendor_id','documentable_id']);
|
||||
$document->hashed_id = $this->encodePrimaryKey($document->id);
|
||||
|
||||
return $document;
|
||||
return $document->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -232,7 +234,7 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
$expense_category = $this->transformArrayOfKeys($expense_category, ['user_id', 'company_id']);
|
||||
|
||||
return $expense_category;
|
||||
return $expense_category->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -242,7 +244,7 @@ class CompanyExport implements ShouldQueue
|
||||
$expense = $this->transformBasicEntities($expense);
|
||||
$expense = $this->transformArrayOfKeys($expense, ['vendor_id', 'invoice_id', 'client_id', 'category_id', 'recurring_expense_id','project_id']);
|
||||
|
||||
return $expense;
|
||||
return $expense->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -250,7 +252,7 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
$gs = $this->transformArrayOfKeys($gs, ['user_id', 'company_id']);
|
||||
|
||||
return $gs;
|
||||
return $gs->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -260,16 +262,20 @@ class CompanyExport implements ShouldQueue
|
||||
$invoice = $this->transformBasicEntities($invoice);
|
||||
$invoice = $this->transformArrayOfKeys($invoice, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
||||
|
||||
return $invoice;
|
||||
return $invoice->makeVisible(['id',
|
||||
'private_notes',
|
||||
'user_id',
|
||||
'client_id',
|
||||
'company_id',]);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['invoice_invitations'] = InvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($invoice){
|
||||
|
||||
$invoice = $this->transformArrayOfKeys($invoice, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
|
||||
$invoice = $this->transformArrayOfKeys($invoice, ['company_id', 'user_id', 'client_contact_id', 'invoice_id']);
|
||||
|
||||
return $invoice;
|
||||
return $invoice->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -281,19 +287,14 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
})->makeHidden(['id'])->all();
|
||||
|
||||
$this->export_data['paymentables'] = $this->company->payments()->with('paymentables')->cursor()->map(function ($paymentable){
|
||||
|
||||
$paymentable = $this->transformArrayOfKeys($paymentable, ['payment_id','paymentable_id']);
|
||||
|
||||
return $paymentable;
|
||||
|
||||
})->all();
|
||||
|
||||
$this->export_data['payments'] = $this->company->payments->map(function ($payment){
|
||||
|
||||
$payment = $this->transformBasicEntities($payment);
|
||||
$payment = $this->transformArrayOfKeys($payment, ['client_id','project_id', 'vendor_id', 'client_contact_id', 'invitation_id', 'company_gateway_id']);
|
||||
|
||||
$payment->paymentables = $this->transformPaymentable($payment);
|
||||
|
||||
return $payment->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
@ -312,7 +313,7 @@ class CompanyExport implements ShouldQueue
|
||||
$project = $this->transformBasicEntities($project);
|
||||
$project = $this->transformArrayOfKeys($project, ['client_id']);
|
||||
|
||||
return $project;
|
||||
return $project->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -321,25 +322,26 @@ class CompanyExport implements ShouldQueue
|
||||
$quote = $this->transformBasicEntities($quote);
|
||||
$quote = $this->transformArrayOfKeys($quote, ['invoice_id','recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
||||
|
||||
return $quote;
|
||||
return $quote->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['quote_invitations'] = QuoteInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($quote){
|
||||
|
||||
$quote = $this->transformArrayOfKeys($quote, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
|
||||
$quote = $this->transformArrayOfKeys($quote, ['company_id', 'user_id', 'client_contact_id', 'quote_id']);
|
||||
|
||||
return $quote;
|
||||
return $quote->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
|
||||
$this->export_data['recurring_invoices'] = $this->company->recurring_invoices->map(function ($ri){
|
||||
$this->export_data['recurring_invoices'] = $this->company->recurring_invoices->makeVisible(['id'])->map(function ($ri){
|
||||
|
||||
$ri = $this->transformBasicEntities($ri);
|
||||
$ri = $this->transformArrayOfKeys($ri, ['client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
|
||||
return $ri;
|
||||
|
||||
return $ri->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -357,7 +359,13 @@ class CompanyExport implements ShouldQueue
|
||||
$subscription = $this->transformBasicEntities($subscription);
|
||||
$subscription->group_id = $this->encodePrimaryKey($subscription->group_id);
|
||||
|
||||
return $subscription;
|
||||
return $subscription->makeVisible([ 'id',
|
||||
'user_id',
|
||||
'assigned_user_id',
|
||||
'company_id',
|
||||
'product_ids',
|
||||
'recurring_product_ids',
|
||||
'group_id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -376,7 +384,7 @@ class CompanyExport implements ShouldQueue
|
||||
$task = $this->transformBasicEntities($task);
|
||||
$task = $this->transformArrayOfKeys($task, ['client_id', 'invoice_id', 'project_id', 'status_id']);
|
||||
|
||||
return $task;
|
||||
return $task->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -401,7 +409,7 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
$this->export_data['vendors'] = $this->company->vendors->map(function ($vendor){
|
||||
|
||||
return $this->transformBasicEntities($vendor);
|
||||
return $this->transformBasicEntities($vendor)->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -411,7 +419,7 @@ class CompanyExport implements ShouldQueue
|
||||
$vendor = $this->transformBasicEntities($vendor);
|
||||
$vendor->vendor_id = $this->encodePrimaryKey($vendor->vendor_id);
|
||||
|
||||
return $vendor;
|
||||
return $vendor->makeVisible(['id']);
|
||||
|
||||
})->all();
|
||||
|
||||
@ -425,7 +433,7 @@ class CompanyExport implements ShouldQueue
|
||||
})->makeHidden(['id'])->all();
|
||||
|
||||
//write to tmp and email to owner();
|
||||
|
||||
|
||||
$this->zipAndSend();
|
||||
|
||||
return true;
|
||||
@ -434,7 +442,7 @@ class CompanyExport implements ShouldQueue
|
||||
private function transformBasicEntities($model)
|
||||
{
|
||||
|
||||
return $this->transformArrayOfKeys($model, ['id', 'user_id', 'assigned_user_id', 'company_id']);
|
||||
return $this->transformArrayOfKeys($model, ['user_id', 'assigned_user_id', 'company_id']);
|
||||
|
||||
}
|
||||
|
||||
@ -449,6 +457,24 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
private function transformPaymentable($payment)
|
||||
{
|
||||
|
||||
$new_arr = [];
|
||||
|
||||
foreach($payment->paymentables as $paymentable)
|
||||
{
|
||||
|
||||
$paymentable->payment_id = $this->encodePrimaryKey($paymentable->payment_id);
|
||||
$paymentable->paymentable_id = $this->encodePrimaryKey($paymentable->paymentable_id);
|
||||
|
||||
$new_arr[] = $paymentable;
|
||||
}
|
||||
|
||||
return $new_arr;
|
||||
|
||||
}
|
||||
|
||||
private function zipAndSend()
|
||||
{
|
||||
|
||||
@ -464,6 +490,10 @@ class CompanyExport implements ShouldQueue
|
||||
$zip->addFromString("backup.json", json_encode($this->export_data));
|
||||
$zip->close();
|
||||
|
||||
if(Ninja::isHosted()) {
|
||||
Storage::disk(config('filesystems.default'))->put('backups/'.$file_name, file_get_contents($zip_path));
|
||||
}
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadBackup(Storage::disk(config('filesystems.default'))->url('backups/'.$file_name), $this->company);
|
||||
$nmo->to_user = $this->user;
|
||||
@ -473,6 +503,7 @@ class CompanyExport implements ShouldQueue
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
UnlinkFile::dispatch(config('filesystems.default'), 'backups/'.$file_name)->delay(now()->addHours(1));
|
||||
UnlinkFile::dispatch('public', 'backups/'.$file_name)->delay(now()->addHours(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -93,17 +93,19 @@ class CreateEntityPdf implements ShouldQueue
|
||||
|
||||
public function handle()
|
||||
{
|
||||
/* Set the locale*/
|
||||
App::setLocale($this->contact->preferredLocale());
|
||||
|
||||
/* Forget the singleton*/
|
||||
App::forgetInstance('translator');
|
||||
|
||||
/* Init a new copy of the translator*/
|
||||
$t = app('translator');
|
||||
/* Set the locale*/
|
||||
App::setLocale($this->contact->preferredLocale());
|
||||
|
||||
// nlog($this->entity->client->getMergedSettings());
|
||||
|
||||
/* Set customized translations _NOW_ */
|
||||
Lang::replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
|
||||
$t->replace(Ninja::transformTranslations($this->entity->client->getMergedSettings()));
|
||||
|
||||
$this->entity->service()->deletePdf();
|
||||
|
||||
|
@ -154,9 +154,9 @@ class NinjaMailerJob implements ShouldQueue
|
||||
App::forgetInstance('mail.manager'); //singletons must be destroyed!
|
||||
App::forgetInstance('mailer');
|
||||
App::forgetInstance('laravelgmail');
|
||||
|
||||
$t = app('translator');
|
||||
/* Inject custom translations if any exist */
|
||||
Lang::replace(Ninja::transformTranslations($this->nmo->settings));
|
||||
$t->replace(Ninja::transformTranslations($this->nmo->settings));
|
||||
|
||||
switch ($this->nmo->settings->email_sending_method) {
|
||||
case 'default':
|
||||
|
@ -35,9 +35,9 @@ class WebhookHandler implements ShouldQueue
|
||||
|
||||
private $company;
|
||||
|
||||
public $tries = 5; //number of retries
|
||||
public $tries = 3; //number of retries
|
||||
|
||||
public $backoff = 5; //seconds to wait until retry
|
||||
public $backoff = 10; //seconds to wait until retry
|
||||
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
|
@ -44,7 +44,8 @@ class CreditEmailEngine extends BaseEmailEngine
|
||||
public function build()
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
||||
|
||||
if (is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
|
||||
$body_template = $this->template_data['body'];
|
||||
|
@ -47,7 +47,8 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
||||
{
|
||||
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
||||
|
||||
if (is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
|
||||
$body_template = $this->template_data['body'];
|
||||
|
@ -45,7 +45,8 @@ class QuoteEmailEngine extends BaseEmailEngine
|
||||
public function build()
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->client->getMergedSettings()));
|
||||
|
||||
if (is_array($this->template_data) && array_key_exists('body', $this->template_data) && strlen($this->template_data['body']) > 0) {
|
||||
$body_template = $this->template_data['body'];
|
||||
|
@ -41,8 +41,8 @@ class MigrationCompleted extends Mailable
|
||||
$result = $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->view('email.import.completed', $data);
|
||||
|
||||
if($this->company->invoices->count() >=1)
|
||||
$result->attach($this->company->invoices->first()->pdf_file_path());
|
||||
// if($this->company->invoices->count() >=1)
|
||||
// $result->attach($this->company->invoices->first()->pdf_file_path());
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
@ -103,6 +103,15 @@ class Activity extends StaticModel
|
||||
'deleted_at' => 'timestamp',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'hashed_id',
|
||||
];
|
||||
|
||||
public function getHashedIdAttribute()
|
||||
{
|
||||
return $this->encodePrimaryKey($this->id);
|
||||
}
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
|
@ -131,6 +131,11 @@ class Company extends BaseModel
|
||||
return $this->morphMany(Document::class, 'documentable');
|
||||
}
|
||||
|
||||
public function all_documents()
|
||||
{
|
||||
return $this->HasMany(Document::class);
|
||||
}
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
|
@ -71,10 +71,6 @@ class Quote extends BaseModel
|
||||
'custom_surcharge2',
|
||||
'custom_surcharge3',
|
||||
'custom_surcharge4',
|
||||
// 'custom_surcharge_tax1',
|
||||
// 'custom_surcharge_tax2',
|
||||
// 'custom_surcharge_tax3',
|
||||
// 'custom_surcharge_tax4',
|
||||
'design_id',
|
||||
'assigned_user_id',
|
||||
'exchange_rate',
|
||||
|
@ -144,7 +144,7 @@ class CreditCard
|
||||
if ($this->checkout->client->currency()->code == 'EUR' || $this->checkout->company_gateway->getConfigField('threeds')) {
|
||||
$payment->{'3ds'} = ['enabled' => true];
|
||||
|
||||
$payment->{'success_url'} = route('payment_webhook', [
|
||||
$payment->{'success_url'} = route('checkout.3ds_redirect', [
|
||||
'company_key' => $this->checkout->client->company->company_key,
|
||||
'company_gateway_id' => $this->checkout->company_gateway->hashed_id,
|
||||
'hash' => $this->checkout->payment_hash->hash,
|
||||
|
@ -13,6 +13,7 @@
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Http\Requests\Gateways\Checkout3ds\Checkout3dsRequest;
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
@ -287,6 +288,11 @@ class CheckoutComPaymentDriver extends BaseDriver
|
||||
}
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function process3dsConfirmation(Checkout3dsRequest $request)
|
||||
{
|
||||
$this->init();
|
||||
$this->setPaymentHash($request->getPaymentHash());
|
||||
|
@ -193,7 +193,8 @@ class ACH
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_STRIPE,
|
||||
$this->stripe->client
|
||||
$this->stripe->client,
|
||||
$this->stripe->client->company,
|
||||
);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->stripe->encodePrimaryKey($payment->id)]);
|
||||
|
@ -390,6 +390,13 @@ class StripePaymentDriver extends BaseDriver
|
||||
$payment->save();
|
||||
}
|
||||
|
||||
if ($request->type == 'charge.succeeded') {
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment->save();
|
||||
}
|
||||
|
||||
// charge.failed, charge.refunded
|
||||
|
||||
return response([], 200);
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@ class PaymentMethod
|
||||
|
||||
$fee_label = $gateway->calcGatewayFeeLabel($this->amount, $this->client, $gateway_type_id);
|
||||
|
||||
if(!$gateway_type_id){
|
||||
if(!$gateway_type_id || (GatewayType::CUSTOM == $gateway_type_id)){
|
||||
|
||||
$this->payment_urls[] = [
|
||||
'label' => $gateway->getConfigField('name') . $fee_label,
|
||||
|
@ -346,6 +346,8 @@ class Design extends BaseDesign
|
||||
|
||||
$items = $this->transformLineItems($this->entity->line_items, $type);
|
||||
|
||||
$this->processMarkdownOnLineItems($items);
|
||||
|
||||
if (count($items) == 0) {
|
||||
return [];
|
||||
}
|
||||
|
@ -321,10 +321,27 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
public static function parseMarkdownToHtml(string $markdown): ?string
|
||||
{
|
||||
// Use setting to determinate if parsing should be done.
|
||||
// 'parse_markdown_on_pdfs'
|
||||
|
||||
$converter = new CommonMarkConverter([
|
||||
'allow_unsafe_links' => false,
|
||||
]);
|
||||
|
||||
return $converter->convertToHtml($markdown);
|
||||
}
|
||||
|
||||
public function processMarkdownOnLineItems(array &$items)
|
||||
{
|
||||
// Use setting to determinate if parsing should be done.
|
||||
// 'parse_markdown_on_pdfs'
|
||||
|
||||
foreach ($items as $key => $item) {
|
||||
foreach ($item as $variable => $value) {
|
||||
$item[$variable] = DesignHelpers::parseMarkdownToHtml($value ?? '');
|
||||
}
|
||||
|
||||
$items[$key] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +92,10 @@ trait PdfMakerUtilities
|
||||
$contains_html = false;
|
||||
|
||||
if (isset($child['content'])) {
|
||||
$child['content'] = nl2br($child['content']);
|
||||
// Commented cause it keeps adding <br> at the end, if markdown parsing is turned on.
|
||||
// Should update with 'parse_markdown_on_pdfs' setting.
|
||||
|
||||
// $child['content'] = nl2br($child['content']);
|
||||
}
|
||||
|
||||
// "/\/[a-z]*>/i" -> checks for HTML-like tags:
|
||||
|
@ -485,8 +485,9 @@ class SubscriptionService
|
||||
|
||||
/**
|
||||
* Handle a plan change where no payment is required
|
||||
*
|
||||
*
|
||||
* @param array $data
|
||||
* @deprecated - no usage found
|
||||
*/
|
||||
public function handlePlanChangeNoPayment($data)
|
||||
{
|
||||
@ -654,7 +655,7 @@ class SubscriptionService
|
||||
$status = $response->getStatusCode();
|
||||
|
||||
//$response_body = $response->getReasonPhrase();
|
||||
$body = array_merge($body, ['status' => $status, 'response_body' => $response_body]);
|
||||
//$body = array_merge($body, ['status' => $status, 'response_body' => $response_body]);
|
||||
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ use App\Models\CreditInvitation;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\App;
|
||||
@ -121,7 +122,7 @@ class HtmlEngine
|
||||
if ($this->entity_string == 'invoice' || $this->entity_string == 'recurring_invoice') {
|
||||
$data['$entity'] = ['value' => '', 'label' => ctrans('texts.invoice')];
|
||||
$data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
|
||||
$data['$entity.terms'] = ['value' => $this->entity->terms ?: '', 'label' => ctrans('texts.invoice_terms')];
|
||||
$data['$entity.terms'] = ['value' => DesignHelpers::parseMarkdownToHtml($this->entity->terms ?: '') ?: '', 'label' => ctrans('texts.invoice_terms')];
|
||||
$data['$terms'] = &$data['$entity.terms'];
|
||||
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_invoice').'</a>', 'label' => ctrans('texts.view_invoice')];
|
||||
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_invoice')];
|
||||
@ -200,7 +201,7 @@ class HtmlEngine
|
||||
$data['$invoice.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
|
||||
$data['$invoice.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
|
||||
$data['$invoice.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
|
||||
$data['$invoice.public_notes'] = ['value' => $this->entity->public_notes ?: '', 'label' => ctrans('texts.public_notes')];
|
||||
$data['$invoice.public_notes'] = ['value' => DesignHelpers::parseMarkdownToHtml($this->entity->public_notes ?: '') ?: '', 'label' => ctrans('texts.public_notes')];
|
||||
$data['$entity.public_notes'] = &$data['$invoice.public_notes'];
|
||||
$data['$public_notes'] = &$data['$invoice.public_notes'];
|
||||
|
||||
@ -374,7 +375,7 @@ class HtmlEngine
|
||||
$data['$description'] = ['value' => '', 'label' => ctrans('texts.description')];
|
||||
|
||||
//$data['$entity_footer'] = ['value' => $this->client->getSetting("{$this->entity_string}_footer"), 'label' => ''];
|
||||
$data['$entity_footer'] = ['value' => $this->entity->footer, 'label' => ''];
|
||||
$data['$entity_footer'] = ['value' => DesignHelpers::parseMarkdownToHtml($this->entity->footer ?: ''), 'label' => ''];
|
||||
|
||||
$data['$page_size'] = ['value' => $this->settings->page_size, 'label' => ''];
|
||||
$data['$page_layout'] = ['value' => property_exists($this->settings, 'page_layout') ? $this->settings->page_layout : 'Portrait', 'label' => ''];
|
||||
|
@ -25,7 +25,7 @@ use Illuminate\Support\Facades\Queue;
|
||||
class SystemHealth
|
||||
{
|
||||
private static $extensions = [
|
||||
'mysqli',
|
||||
// 'mysqli',
|
||||
'gd',
|
||||
'curl',
|
||||
'zip',
|
||||
@ -34,7 +34,7 @@ class SystemHealth
|
||||
'mbstring',
|
||||
'xml',
|
||||
'bcmath',
|
||||
'mysqlnd',
|
||||
// 'mysqlnd',
|
||||
//'intl', //todo double check whether we need this for email dns validation
|
||||
];
|
||||
|
||||
|
@ -106,7 +106,8 @@ class TemplateEngine
|
||||
}
|
||||
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations($this->settings));
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->settings));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.1.65',
|
||||
'app_tag' => '5.1.65-release',
|
||||
'app_version' => '5.1.70',
|
||||
'app_tag' => '5.1.70-release',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Document;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class MakeDocumentsAssignedUserNullable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('documents', function (Blueprint $table){
|
||||
$table->unsignedInteger('assigned_user_id')->nullable()->change();
|
||||
});
|
||||
|
||||
Document::where('assigned_user_id', 0)->update(['assigned_user_id' => null]);
|
||||
|
||||
if(config('ninja.db.multi_db_enabled')){
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
Document::on($db)->where('assigned_user_id', 0)->update(['assigned_user_id' => null]);
|
||||
}
|
||||
}
|
||||
else{
|
||||
Document::where('assigned_user_id', 0)->update(['assigned_user_id' => null]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
2
public/flutter_service_worker.js
vendored
2
public/flutter_service_worker.js
vendored
@ -5,7 +5,7 @@ const CACHE_NAME = 'flutter-app-cache';
|
||||
const RESOURCES = {
|
||||
"version.json": "9fe5b22a16f39b766c8fdc35a24b3efa",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"main.dart.js": "99c1cdf29ca54a505582ef971ac4f97f",
|
||||
"main.dart.js": "4ed39edfd85c4ac1e202b7c83715f20d",
|
||||
"/": "23224b5e03519aaa87594403d54412cf",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||
"assets/AssetManifest.json": "659dcf9d1baf3aed3ab1b9c42112bf8f",
|
||||
|
BIN
public/images/client-portal-new-image.jpg
Normal file
BIN
public/images/client-portal-new-image.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 622 KiB |
100701
public/main.dart.js
vendored
100701
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
99471
public/main.foss.dart.js
vendored
99471
public/main.foss.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5",
|
||||
"/css/app.css": "/css/app.css?id=8bf94fe817c8702670c9",
|
||||
"/css/app.css": "/css/app.css?id=14a824656f32eec8c2b1",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
|
||||
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7",
|
||||
@ -19,7 +19,5 @@
|
||||
"/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=5c35d28cf0a3286e7c45",
|
||||
"/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=fc3055d6a099f523ea98",
|
||||
"/js/setup/setup.js": "/js/setup/setup.js?id=8d454e7090f119552a6c",
|
||||
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad",
|
||||
"/js/admin.js": "/js/admin.js?id=003930085af69b13a86a",
|
||||
"/css/admin.css": "/css/admin.css?id=0f5a9ba733ae0e1f9cbf"
|
||||
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad"
|
||||
}
|
||||
|
@ -159,6 +159,10 @@
|
||||
#footer {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
[data-ref="totals_table-outstanding"] {
|
||||
color: var(--primary-color)
|
||||
}
|
||||
</style>
|
||||
<div id="header"></div>
|
||||
|
||||
|
@ -10,52 +10,53 @@
|
||||
<div class="grid grid-cols-6 gap-6">
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<label for="address1" class="input-label">{{ ctrans('texts.address1') }}</label>
|
||||
<input id="address1" class="input w-full" name="address1" />
|
||||
<input id="address1" class="input w-full" name="address1"/>
|
||||
@error('address1')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="address2" class="input-label">{{ ctrans('texts.address2') }}</label>
|
||||
<input id="address2" class="input w-full" name="address2" />
|
||||
<input id="address2" class="input w-full" name="address2"/>
|
||||
@error('address2')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="city" class="input-label">{{ ctrans('texts.city') }}</label>
|
||||
<input id="city" class="input w-full" name="city" />
|
||||
<input id="city" class="input w-full" name="city"/>
|
||||
@error('city')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-2">
|
||||
<label for="state" class="input-label">{{ ctrans('texts.state') }}</label>
|
||||
<input id="state" class="input w-full" name="state" />
|
||||
<input id="state" class="input w-full" name="state"/>
|
||||
@error('state')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-2">
|
||||
<label for="postal_code" class="input-label">{{ ctrans('texts.postal_code') }}</label>
|
||||
<input id="postal_code" class="input w-full" name="postal_code" />
|
||||
<input id="postal_code" class="input w-full" name="postal_code"/>
|
||||
@error('postal_code')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-2">
|
||||
<label for="country" class="input-label">{{ ctrans('texts.country') }}</label>
|
||||
<select id="country" class="input w-full form-select" name="country">
|
||||
<option value="none"></option>
|
||||
@foreach(App\Utils\TranslationHelper::getCountries() as $country)
|
||||
<option value="{{ $country->id }}">
|
||||
{{ $country->iso_3166_2 }} ({{ $country->name }})
|
||||
@ -63,11 +64,11 @@
|
||||
@endforeach
|
||||
</select>
|
||||
@error('country')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,74 +1,88 @@
|
||||
<!-- Client shipping address -->
|
||||
<h3 class="text-lg font-medium leading-6 text-gray-900 mt-8">{{ ctrans('texts.shipping_address') }}</h3>
|
||||
<!-- Client shipping address -->
|
||||
<h3 class="text-lg font-medium leading-6 text-gray-900 mt-8">{{ ctrans('texts.shipping_address') }}</h3>
|
||||
|
||||
<p class="mt-1 text-sm leading-5 text-gray-500">
|
||||
{{ ctrans('texts.enter_your_shipping_address') }}
|
||||
</p>
|
||||
<p class="mt-1 text-sm leading-5 text-gray-500">
|
||||
{{ ctrans('texts.enter_your_shipping_address') }}
|
||||
</p>
|
||||
|
||||
<div class="shadow overflow-hidden rounded mt-4">
|
||||
<div class="px-4 py-5 bg-white sm:p-6">
|
||||
<div class="grid grid-cols-6 gap-6">
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<label for="shipping_address1" class="input-label">{{ ctrans('texts.shipping_address1') }}</label>
|
||||
<input id="shipping_address1" class="input w-full {{ in_array('shipping_address1', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="shipping_address1" />
|
||||
@error('shipping_address1')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="shipping_address2" class="input-label">{{ ctrans('texts.shipping_address2') }}</label>
|
||||
<input id="shipping_address2 {{ in_array('shipping_address2', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" class="input w-full" name="shipping_address2" />
|
||||
@error('shipping_address2')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="shipping_city" class="input-label">{{ ctrans('texts.shipping_city') }}</label>
|
||||
<input id="shipping_city" class="input w-full {{ in_array('shipping_city', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="shipping_city" />
|
||||
@error('shipping_city')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-2">
|
||||
<label for="shipping_state" class="input-label">{{ ctrans('texts.shipping_state') }}</label>
|
||||
<input id="shipping_state" class="input w-ful {{ in_array('shipping_state', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}l" name="shipping_state" />
|
||||
@error('shipping_state')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-2">
|
||||
<label for="shipping_postal_code" class="input-label">{{ ctrans('texts.shipping_postal_code') }}</label>
|
||||
<input id="shipping_postal_code" class="input w-full {{ in_array('shipping_postal_code', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="shipping_postal_code" />
|
||||
@error('shipping_postal_code')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-4 sm:col-span-2">
|
||||
<label for="shipping_country" class="input-label">{{ ctrans('texts.shipping_country') }}</label>
|
||||
<select id="shipping_country" class="input w-full form-select {{ in_array('shipping_country', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}" name="shipping_country">
|
||||
@foreach(App\Utils\TranslationHelper::getCountries() as $country)
|
||||
<option {{ $country == isset(auth()->user()->client->shipping_country->id) ? 'selected' : null }} value="{{ $country->id }}">
|
||||
<div class="shadow overflow-hidden rounded mt-4">
|
||||
<div class="px-4 py-5 bg-white sm:p-6">
|
||||
<div class="grid grid-cols-6 gap-6">
|
||||
<div class="col-span-6 sm:col-span-4">
|
||||
<label for="shipping_address1" class="input-label">{{ ctrans('texts.shipping_address1') }}</label>
|
||||
<input id="shipping_address1"
|
||||
class="input w-full {{ in_array('shipping_address1', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}"
|
||||
name="shipping_address1"/>
|
||||
@error('shipping_address1')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="shipping_address2" class="input-label">{{ ctrans('texts.shipping_address2') }}</label>
|
||||
<input
|
||||
id="shipping_address2 {{ in_array('shipping_address2', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}"
|
||||
class="input w-full" name="shipping_address2"/>
|
||||
@error('shipping_address2')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="shipping_city" class="input-label">{{ ctrans('texts.shipping_city') }}</label>
|
||||
<input id="shipping_city"
|
||||
class="input w-full {{ in_array('shipping_city', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}"
|
||||
name="shipping_city"/>
|
||||
@error('shipping_city')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-2">
|
||||
<label for="shipping_state" class="input-label">{{ ctrans('texts.shipping_state') }}</label>
|
||||
<input id="shipping_state"
|
||||
class="input w-ful {{ in_array('shipping_state', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}l"
|
||||
name="shipping_state"/>
|
||||
@error('shipping_state')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-2">
|
||||
<label for="shipping_postal_code" class="input-label">{{ ctrans('texts.shipping_postal_code') }}</label>
|
||||
<input id="shipping_postal_code"
|
||||
class="input w-full {{ in_array('shipping_postal_code', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}"
|
||||
name="shipping_postal_code"/>
|
||||
@error('shipping_postal_code')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-span-4 sm:col-span-2">
|
||||
<label for="shipping_country" class="input-label">{{ ctrans('texts.shipping_country') }}</label>
|
||||
<select id="shipping_country"
|
||||
class="input w-full form-select {{ in_array('shipping_country', (array) session('missing_required_fields')) ? 'border border-red-400' : '' }}"
|
||||
name="shipping_country">
|
||||
@foreach(App\Utils\TranslationHelper::getCountries() as $country)
|
||||
<option value="none"></option>
|
||||
<option
|
||||
{{ $country == isset(auth()->user()->client->shipping_country->id) ? 'selected' : null }} value="{{ $country->id }}">
|
||||
{{ $country->iso_3166_2 }}
|
||||
({{ $country->name }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('country')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('country')
|
||||
<div class="validation validation-fail">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -10,14 +10,14 @@
|
||||
<div class="grid lg:grid-cols-3">
|
||||
@if($account && !$account->isPaid())
|
||||
<div class="hidden lg:block col-span-1 bg-red-100 h-screen">
|
||||
<img src="{{ asset('images/bg-home2018b.jpg') }}"
|
||||
<img src="{{ asset('images/client-portal-new-image.jpg') }}"
|
||||
class="w-full h-screen object-cover"
|
||||
alt="Background image">
|
||||
</div>
|
||||
@endif
|
||||
<div class="col-span-2 h-screen flex">
|
||||
<div class="m-auto md:w-1/2 lg:w-1/4">
|
||||
|
||||
|
||||
@if($account && !$account->isPaid())
|
||||
<div>
|
||||
<img src="{{ asset('images/invoiceninja-black-logo-2.png') }}" class="border-b border-gray-100 h-18 pb-4" alt="Invoice Ninja logo">
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
@section('body')
|
||||
<div class="grid lg:grid-cols-12 py-8">
|
||||
<div class="lg:col-span-4 lg:col-start-5 px-6">
|
||||
<div class="col-span-12 lg:col-span-6 lg:col-start-4 xl:col-span-4 xl:col-start-5 px-6">
|
||||
<div class="flex justify-center">
|
||||
<img class="h-32 w-auto" src="{{ $company->present()->logo() }}" alt="{{ ctrans('texts.logo') }}">
|
||||
</div>
|
||||
|
@ -30,7 +30,7 @@
|
||||
{{ ctrans('texts.credit_card') }}
|
||||
@endcomponent
|
||||
|
||||
@include('portal.ninja2020.gateways.stripe.includes.card_widget')
|
||||
@include('portal.ninja2020.gateways.stripe.includes.card_widget', ['show_save_method' => false])
|
||||
|
||||
@component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'authorize-card'])
|
||||
{{ ctrans('texts.add_payment_method') }}
|
||||
@ -40,4 +40,4 @@
|
||||
@section('gateway_footer')
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
<script src="{{ asset('js/clients/payments/stripe-credit-card.js') }}"></script>
|
||||
@endsection
|
||||
@endsection
|
||||
|
@ -12,4 +12,6 @@
|
||||
@endunless
|
||||
</div>
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.save_card')
|
||||
@unless(isset($show_save_method) && $show_save_method == false)
|
||||
@include('portal.ninja2020.gateways.includes.save_card')
|
||||
@endunless
|
||||
|
@ -43,7 +43,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="@yield('meta_description')"/>
|
||||
|
||||
|
||||
<!-- CSRF Token -->
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
|
@ -34,6 +34,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
||||
Route::post('claim_license', 'LicenseController@index')->name('license.index');
|
||||
|
||||
Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::put('clients/{client}/adjust_ledger', 'ClientController@adjustLedger')->name('clients.adjust_ledger');
|
||||
Route::put('clients/{client}/upload', 'ClientController@upload')->name('clients.upload');
|
||||
Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
|
||||
|
||||
@ -84,6 +85,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
||||
Route::post('group_settings/bulk', 'GroupSettingController@bulk');
|
||||
|
||||
Route::post('import', 'ImportController@import')->name('import.import');
|
||||
Route::post('import_json', 'ImportJsonController@import')->name('import.import_json');
|
||||
Route::post('preimport', 'ImportController@preimport')->name('import.preimport');
|
||||
|
||||
Route::resource('invoices', 'InvoiceController'); // name = (invoices. index / create / show / update / destroy / edit
|
||||
|
@ -15,9 +15,9 @@ Route::post('setup/check_db', 'SetupController@checkDB')->middleware('guest');
|
||||
Route::post('setup/check_mail', 'SetupController@checkMail')->middleware('guest');
|
||||
Route::post('setup/check_pdf', 'SetupController@checkPdf')->middleware('guest');
|
||||
|
||||
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
|
||||
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->middleware('domain_db')->name('password.request');
|
||||
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
|
||||
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->middleware('email_db')->name('password.reset');
|
||||
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->middleware(['domain_db','email_db'])->name('password.reset');
|
||||
Route::post('password/reset', 'Auth\ResetPasswordController@reset')->middleware('email_db')->name('password.update');
|
||||
|
||||
/*
|
||||
@ -36,4 +36,6 @@ Route::group(['middleware' => ['url_db']], function () {
|
||||
});
|
||||
|
||||
Route::get('stripe/signup/{token}', 'StripeConnectController@initialize')->name('stripe_connect.initialization');
|
||||
Route::get('stripe/completed', 'StripeConnectController@completed')->name('stripe_connect.return');
|
||||
Route::get('stripe/completed', 'StripeConnectController@completed')->name('stripe_connect.return');
|
||||
|
||||
Route::get('checkout/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', 'Gateways\Checkout3dsController@index')->name('checkout.3ds_redirect');
|
||||
|
@ -12,24 +12,43 @@ namespace Tests\Feature\Import;
|
||||
|
||||
use App\Jobs\Import\CSVImport;
|
||||
use App\Models\Account;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\CompanyLedger;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\Credit;
|
||||
use App\Models\CreditInvitation;
|
||||
use App\Models\Design;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\GroupSetting;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentTerm;
|
||||
use App\Models\Paymentable;
|
||||
use App\Models\Product;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
@ -45,6 +64,7 @@ use Tests\TestCase;
|
||||
class ImportCompanyTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use DatabaseTransactions;
|
||||
|
||||
public $account;
|
||||
public $company;
|
||||
@ -81,6 +101,13 @@ class ImportCompanyTest extends TestCase
|
||||
|
||||
$this->backup_json_object = json_decode(file_get_contents($backup_json_file));
|
||||
|
||||
Credit::all()->each(function($credit){
|
||||
$credit->forceDelete();
|
||||
});
|
||||
|
||||
CreditInvitation::all()->each(function($credit){
|
||||
$credit->forceDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function testBackupJsonRead()
|
||||
@ -256,7 +283,6 @@ class ImportCompanyTest extends TestCase
|
||||
$obj_array = (array)$obj;
|
||||
unset($obj_array['user_id']);
|
||||
unset($obj_array['company_id']);
|
||||
unset($obj_array['account_id']);
|
||||
unset($obj_array['hashed_id']);
|
||||
unset($obj_array['id']);
|
||||
unset($obj_array['tax_rate_id']);
|
||||
@ -288,10 +314,8 @@ class ImportCompanyTest extends TestCase
|
||||
$obj_array = (array)$obj;
|
||||
unset($obj_array['user_id']);
|
||||
unset($obj_array['company_id']);
|
||||
unset($obj_array['account_id']);
|
||||
unset($obj_array['hashed_id']);
|
||||
unset($obj_array['id']);
|
||||
unset($obj_array['tax_rate_id']);
|
||||
|
||||
$new_obj = ExpenseCategory::firstOrNew(
|
||||
['name' => $obj->name, 'company_id' => $this->company->id],
|
||||
@ -321,10 +345,8 @@ class ImportCompanyTest extends TestCase
|
||||
$obj_array = (array)$obj;
|
||||
unset($obj_array['user_id']);
|
||||
unset($obj_array['company_id']);
|
||||
unset($obj_array['account_id']);
|
||||
unset($obj_array['hashed_id']);
|
||||
unset($obj_array['id']);
|
||||
unset($obj_array['tax_rate_id']);
|
||||
|
||||
$new_obj = TaskStatus::firstOrNew(
|
||||
['name' => $obj->name, 'company_id' => $this->company->id],
|
||||
@ -416,79 +438,605 @@ class ImportCompanyTest extends TestCase
|
||||
$this->assertEquals(1, ClientContact::count());
|
||||
/***************************** Client Contacts *****************************/
|
||||
|
||||
/* Generic */
|
||||
|
||||
|
||||
/* Generic */
|
||||
|
||||
//vendors!
|
||||
/* Generic */
|
||||
$this->assertEquals(1, count($this->backup_json_object->vendors));
|
||||
|
||||
$this->genericImport(Vendor::class,
|
||||
['user_id', 'assigned_user_id', 'company_id', 'id', 'hashed_id'],
|
||||
[['users' => 'user_id'], ['users' =>'assigned_user_id']],
|
||||
'vendors',
|
||||
'number');
|
||||
|
||||
$this->assertEquals(1, Vendor::count());
|
||||
|
||||
/* Generic */
|
||||
|
||||
$this->assertEquals(1, count($this->backup_json_object->projects));
|
||||
//$class, $unset, $transforms, $object_property, $match_key
|
||||
$this->genericImport(Project::class,
|
||||
['user_id', 'assigned_user_id', 'company_id', 'id', 'hashed_id','client_id'],
|
||||
[['users' => 'user_id'], ['users' =>'assigned_user_id'], ['clients' => 'client_id']],
|
||||
'projects',
|
||||
'number');
|
||||
|
||||
$this->assertEquals(1, Project::count());
|
||||
|
||||
//projects!
|
||||
|
||||
/***************************** Products *****************************/
|
||||
// Product::unguard();
|
||||
//products!
|
||||
|
||||
// $this->assertEquals(1, count($this->backup_json_object->products));
|
||||
$this->assertEquals(1, count($this->backup_json_object->products));
|
||||
|
||||
// foreach($this->backup_json_object->products as $obj)
|
||||
// {
|
||||
$this->genericNewClassImport(Product::class,
|
||||
['user_id', 'company_id', 'hashed_id', 'id'],
|
||||
[['users' => 'user_id'], ['users' =>'assigned_user_id'], ['vendors' => 'vendor_id'], ['projects' => 'project_id']],
|
||||
'products'
|
||||
);
|
||||
$this->assertEquals(1, Product::count());
|
||||
|
||||
// $user_id = $this->transformId('users', $obj->user_id);
|
||||
// $assigned_user_id = $this->transformId('users', $obj->assigned_user_id);
|
||||
// $vendor_id = $this->transformId('vendors', $obj->vendor_id);
|
||||
// $project_id = $this->transformId('projects', $obj->project_id);
|
||||
//company gateways
|
||||
|
||||
// $obj_array = (array)$obj;
|
||||
// unset($obj_array['user_id']);
|
||||
// unset($obj_array['company_id']);
|
||||
// unset($obj_array['account_id']);
|
||||
// unset($obj_array['hashed_id']);
|
||||
// unset($obj_array['id']);
|
||||
|
||||
$this->assertEquals(1, count($this->backup_json_object->company_gateways));
|
||||
|
||||
// $new_obj = new Product();
|
||||
// $new_obj->company_id = $this->company->id;
|
||||
// $new_obj->user_id = $user_id;
|
||||
// $new_obj->assigned_user_id = $assigned_user_id;
|
||||
// $new_obj->vendor_id = $vendor_id;
|
||||
// $new_obj->project_id = $project_id;
|
||||
// $new_obj->fill($obj_array);
|
||||
$this->genericNewClassImport(CompanyGateway::class,
|
||||
['user_id', 'company_id', 'hashed_id', 'id'],
|
||||
[['users' => 'user_id']],
|
||||
'company_gateways'
|
||||
);
|
||||
|
||||
// $new_obj->save(['timestamps' => false]);
|
||||
|
||||
// $this->ids['products']["{$obj->hashed_id}"] = $new_obj->id;
|
||||
$this->assertEquals(1, CompanyGateway::count());
|
||||
|
||||
// }
|
||||
//company gateways
|
||||
|
||||
// Product::reguard();
|
||||
|
||||
// $this->assertEquals(1, Product::count());
|
||||
/***************************** Products *****************************/
|
||||
|
||||
//client gateway tokens
|
||||
|
||||
$this->genericNewClassImport(ClientGatewayToken::class,
|
||||
['company_id', 'id', 'hashed_id','client_id'],
|
||||
[['clients' => 'client_id']],
|
||||
'client_gateway_tokens');
|
||||
|
||||
//client gateway tokens
|
||||
|
||||
//Group Settings
|
||||
$this->genericImport(GroupSetting::class,
|
||||
['user_id', 'company_id', 'id', 'hashed_id',],
|
||||
[['users' => 'user_id']],
|
||||
'group_settings',
|
||||
'name');
|
||||
//Group Settings
|
||||
|
||||
//Subscriptions
|
||||
$this->assertEquals(1, count($this->backup_json_object->subscriptions));
|
||||
|
||||
$this->genericImport(Subscription::class,
|
||||
['user_id', 'assigned_user_id', 'company_id', 'id', 'hashed_id',],
|
||||
[['group_settings' => 'group_id'], ['users' => 'user_id'], ['users' => 'assigned_user_id']],
|
||||
'subscriptions',
|
||||
'name');
|
||||
|
||||
$this->assertEquals(1, Subscription::count());
|
||||
|
||||
//Subscriptions
|
||||
|
||||
// Recurring Invoices
|
||||
|
||||
$this->assertEquals(2, count($this->backup_json_object->recurring_invoices));
|
||||
|
||||
$this->genericImport(RecurringInvoice::class,
|
||||
['user_id', 'assigned_user_id', 'company_id', 'id', 'hashed_id', 'client_id','subscription_id','project_id','vendor_id','status'],
|
||||
[
|
||||
['subscriptions' => 'subscription_id'],
|
||||
['users' => 'user_id'],
|
||||
['users' => 'assigned_user_id'],
|
||||
['clients' => 'client_id'],
|
||||
['projects' => 'project_id'],
|
||||
['vendors' => 'vendor_id'],
|
||||
['clients' => 'client_id'],
|
||||
],
|
||||
'recurring_invoices',
|
||||
'number');
|
||||
|
||||
$this->assertEquals(2, RecurringInvoice::count());
|
||||
|
||||
// Recurring Invoices
|
||||
|
||||
|
||||
// Recurring Invoice Invitations
|
||||
|
||||
$this->assertEquals(2, count($this->backup_json_object->recurring_invoice_invitations));
|
||||
|
||||
$this->genericImport(RecurringInvoiceInvitation::class,
|
||||
['user_id', 'client_contact_id', 'company_id', 'id', 'hashed_id', 'recurring_invoice_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['recurring_invoices' => 'recurring_invoice_id'],
|
||||
['client_contacts' => 'client_contact_id'],
|
||||
],
|
||||
'recurring_invoice_invitations',
|
||||
'key');
|
||||
|
||||
$this->assertEquals(2, RecurringInvoiceInvitation::count());
|
||||
|
||||
// Recurring Invoice Invitations
|
||||
|
||||
// Invoices
|
||||
|
||||
$this->assertEquals(2, count($this->backup_json_object->invoices));
|
||||
|
||||
$this->genericImport(Invoice::class,
|
||||
['user_id', 'client_id', 'company_id', 'id', 'hashed_id', 'recurring_id','status'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['users' => 'assigned_user_id'],
|
||||
['recurring_invoices' => 'recurring_id'],
|
||||
['clients' => 'client_id'],
|
||||
['subscriptions' => 'subscription_id'],
|
||||
['projects' => 'project_id'],
|
||||
['vendors' => 'vendor_id'],
|
||||
],
|
||||
'invoices',
|
||||
'number');
|
||||
|
||||
$this->assertEquals(2, Invoice::count());
|
||||
|
||||
|
||||
// Invoices
|
||||
|
||||
// Invoice Invitations
|
||||
|
||||
$this->assertEquals(2, count($this->backup_json_object->invoice_invitations));
|
||||
|
||||
$this->genericImport(InvoiceInvitation::class,
|
||||
['user_id', 'client_contact_id', 'company_id', 'id', 'hashed_id', 'invoice_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['invoices' => 'invoice_id'],
|
||||
['client_contacts' => 'client_contact_id'],
|
||||
],
|
||||
'invoice_invitations',
|
||||
'key');
|
||||
|
||||
$this->assertEquals(2, InvoiceInvitation::count());
|
||||
|
||||
// Invoice Invitations
|
||||
|
||||
|
||||
// Quotes
|
||||
$this->assertEquals(2, count($this->backup_json_object->quotes));
|
||||
|
||||
$this->genericImport(Quote::class,
|
||||
['user_id', 'client_id', 'company_id', 'id', 'hashed_id', 'recurring_id','status'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['users' => 'assigned_user_id'],
|
||||
['recurring_invoices' => 'recurring_id'],
|
||||
['clients' => 'client_id'],
|
||||
['subscriptions' => 'subscription_id'],
|
||||
['projects' => 'project_id'],
|
||||
['vendors' => 'vendor_id'],
|
||||
],
|
||||
'quotes',
|
||||
'number');
|
||||
|
||||
$this->assertEquals(2, Quote::count());
|
||||
|
||||
|
||||
// Quotes
|
||||
|
||||
// Quotes Invitations
|
||||
|
||||
$this->assertEquals(2, count($this->backup_json_object->quote_invitations));
|
||||
|
||||
$this->genericImport(QuoteInvitation::class,
|
||||
['user_id', 'client_contact_id', 'company_id', 'id', 'hashed_id', 'quote_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['quotes' => 'quote_id'],
|
||||
['client_contacts' => 'client_contact_id'],
|
||||
],
|
||||
'quote_invitations',
|
||||
'key');
|
||||
|
||||
$this->assertEquals(2, QuoteInvitation::count());
|
||||
|
||||
// Quotes Invitations
|
||||
|
||||
// Credits
|
||||
$this->assertEquals(2, count($this->backup_json_object->credits));
|
||||
|
||||
$this->genericImport(Credit::class,
|
||||
['user_id', 'client_id', 'company_id', 'id', 'hashed_id', 'recurring_id','status'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['users' => 'assigned_user_id'],
|
||||
['recurring_invoices' => 'recurring_id'],
|
||||
['clients' => 'client_id'],
|
||||
['subscriptions' => 'subscription_id'],
|
||||
['projects' => 'project_id'],
|
||||
['vendors' => 'vendor_id'],
|
||||
],
|
||||
'credits',
|
||||
'number');
|
||||
|
||||
$this->assertEquals(2, Credit::count());
|
||||
|
||||
|
||||
// Credits
|
||||
|
||||
// Credits Invitations
|
||||
|
||||
$this->assertEquals(2, count($this->backup_json_object->credit_invitations));
|
||||
|
||||
$this->genericImport(CreditInvitation::class,
|
||||
['user_id', 'client_contact_id', 'company_id', 'id', 'hashed_id', 'credit_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['credits' => 'credit_id'],
|
||||
['client_contacts' => 'client_contact_id'],
|
||||
],
|
||||
'credit_invitations',
|
||||
'key');
|
||||
|
||||
$this->assertEquals(2, CreditInvitation::count());
|
||||
|
||||
// Credits Invitations
|
||||
|
||||
|
||||
// Expenses
|
||||
|
||||
$this->assertEquals(2, count($this->backup_json_object->expenses));
|
||||
|
||||
$this->genericImport(Expense::class,
|
||||
['assigned_user_id', 'user_id', 'client_id', 'company_id', 'id', 'hashed_id', 'project_id','vendor_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['users' => 'assigned_user_id'],
|
||||
['clients' => 'client_id'],
|
||||
['projects' => 'project_id'],
|
||||
['vendors' => 'vendor_id'],
|
||||
],
|
||||
'expenses',
|
||||
'number');
|
||||
|
||||
$this->assertEquals(2, Expense::count());
|
||||
|
||||
// Expenses
|
||||
|
||||
// Tasks
|
||||
|
||||
$this->assertEquals(3, count($this->backup_json_object->tasks));
|
||||
|
||||
$this->genericImport(Task::class,
|
||||
['assigned_user_id', 'user_id', 'client_id', 'company_id', 'id', 'hashed_id', 'invoice_id','project_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['users' => 'assigned_user_id'],
|
||||
['clients' => 'client_id'],
|
||||
['projects' => 'project_id'],
|
||||
['invoices' => 'invoice_id'],
|
||||
],
|
||||
'tasks',
|
||||
'number');
|
||||
|
||||
$this->assertEquals(3, Task::count());
|
||||
|
||||
// Tasks
|
||||
|
||||
// Payments
|
||||
|
||||
$this->assertEquals(2, count($this->backup_json_object->payments));
|
||||
|
||||
$this->genericImport(Payment::class,
|
||||
['assigned_user_id', 'user_id', 'client_id', 'company_id', 'id', 'hashed_id', 'client_contact_id','invitation_id','vendor_id','paymentables'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['users' => 'assigned_user_id'],
|
||||
['clients' => 'client_id'],
|
||||
['client_contacts' => 'client_contact_id'],
|
||||
['vendors' => 'vendor_id'],
|
||||
['invoice_invitations' => 'invitation_id'],
|
||||
['company_gateways' => 'company_gateway_id'],
|
||||
],
|
||||
'payments',
|
||||
'number');
|
||||
|
||||
$this->assertEquals(2, Payment::count());
|
||||
|
||||
// Payments
|
||||
|
||||
// Paymentables
|
||||
|
||||
$this->paymentablesImport();
|
||||
|
||||
$this->assertEquals(1, Paymentable::count());
|
||||
|
||||
|
||||
// Paymentables
|
||||
|
||||
|
||||
// Activities
|
||||
$activities = [];
|
||||
|
||||
foreach($this->backup_json_object->activities as $activity)
|
||||
{
|
||||
$activity->account_id = $this->company->account_id;
|
||||
$activities[] = $activity;
|
||||
}
|
||||
|
||||
$this->assertEquals(25, count($this->backup_json_object->activities));
|
||||
|
||||
$this->backup_json_object->activities = $activities;
|
||||
|
||||
$this->genericImport(Activity::class,
|
||||
[
|
||||
'user_id',
|
||||
'company_id',
|
||||
'client_id',
|
||||
'client_contact_id',
|
||||
'project_id',
|
||||
'vendor_id',
|
||||
'payment_id',
|
||||
'invoice_id',
|
||||
'credit_id',
|
||||
'invitation_id',
|
||||
'task_id',
|
||||
'expense_id',
|
||||
'token_id',
|
||||
'quote_id',
|
||||
'subscription_id',
|
||||
'recurring_invoice_id',
|
||||
'hashed_id',
|
||||
'company_id',
|
||||
],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['clients' => 'client_id'],
|
||||
['client_contacts' => 'client_contact_id'],
|
||||
['projects' => 'project_id'],
|
||||
['vendors' => 'vendor_id'],
|
||||
['payments' => 'payment_id'],
|
||||
['invoices' => 'invoice_id'],
|
||||
['credits' => 'credit_id'],
|
||||
['tasks' => 'task_id'],
|
||||
['expenses' => 'expense_id'],
|
||||
['quotes' => 'quote_id'],
|
||||
['subscriptions' => 'subscription_id'],
|
||||
['recurring_invoices' => 'recurring_invoice_id'],
|
||||
['invitations' => 'invitation_id'],
|
||||
],
|
||||
'activities',
|
||||
'created_at');
|
||||
|
||||
$this->assertEquals(25, Activity::count());
|
||||
|
||||
// Activities
|
||||
|
||||
// Backup
|
||||
|
||||
$this->assertEquals(25, count($this->backup_json_object->backups));
|
||||
|
||||
$this->genericImportWithoutCompany(Backup::class,
|
||||
['activity_id','hashed_id'],
|
||||
[
|
||||
['activities' => 'activity_id'],
|
||||
],
|
||||
'backups',
|
||||
'created_at');
|
||||
|
||||
$this->assertEquals(25, Backup::count());
|
||||
|
||||
// Backup
|
||||
|
||||
// Company Ledger
|
||||
$this->assertEquals(3, count($this->backup_json_object->company_ledger));
|
||||
|
||||
$this->genericImport(CompanyLedger::class,
|
||||
['company_id', 'user_id', 'client_id', 'activity_id', 'id','account_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
['clients' => 'client_id'],
|
||||
['activities' => 'activity_id'],
|
||||
],
|
||||
'company_ledger',
|
||||
'created_at');
|
||||
|
||||
$this->assertEquals(3, CompanyLedger::count());
|
||||
|
||||
// Company Ledger
|
||||
|
||||
// Designs
|
||||
|
||||
$this->genericImport(Design::class,
|
||||
['company_id', 'user_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
],
|
||||
'designs',
|
||||
'name');
|
||||
|
||||
// Designs
|
||||
|
||||
// Documents
|
||||
$this->assertEquals(2, count($this->backup_json_object->documents));
|
||||
|
||||
$this->documentsImport();
|
||||
|
||||
$this->assertEquals(2, Document::count());
|
||||
|
||||
// Documents
|
||||
}
|
||||
|
||||
private function genericImport($class, $unset, $transform, $object_property, $match_key)
|
||||
private function documentsImport()
|
||||
{
|
||||
|
||||
foreach($this->backup_json_object->documents as $document)
|
||||
{
|
||||
|
||||
$new_document = new Document();
|
||||
$new_document->user_id = $this->transformId('users', $document->user_id);
|
||||
$new_document->assigned_user_id = $this->transformId('users', $document->assigned_user_id);
|
||||
$new_document->company_id = $this->company->id;
|
||||
$new_document->project_id = $this->transformId('projects', $document->project_id);
|
||||
$new_document->vendor_id = $this->transformId('vendors', $document->vendor_id);
|
||||
$new_document->url = $document->url;
|
||||
$new_document->preview = $document->preview;
|
||||
$new_document->name = $document->name;
|
||||
$new_document->type = $document->type;
|
||||
$new_document->disk = $document->disk;
|
||||
$new_document->hash = $document->hash;
|
||||
$new_document->size = $document->size;
|
||||
$new_document->width = $document->width;
|
||||
$new_document->height = $document->height;
|
||||
$new_document->is_default = $document->is_default;
|
||||
$new_document->custom_value1 = $document->custom_value1;
|
||||
$new_document->custom_value2 = $document->custom_value2;
|
||||
$new_document->custom_value3 = $document->custom_value3;
|
||||
$new_document->custom_value4 = $document->custom_value4;
|
||||
$new_document->deleted_at = $document->deleted_at;
|
||||
$new_document->documentable_id = $this->transformDocumentId($document->documentable_id, $document->documentable_type);
|
||||
$new_document->documentable_type = $document->documentable_type;
|
||||
|
||||
$new_document->save(['timestamps' => false]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function transformDocumentId($id, $type)
|
||||
{
|
||||
switch ($type) {
|
||||
case Company::class:
|
||||
return $this->company->id;
|
||||
break;
|
||||
case Client::class:
|
||||
return $this->transformId('clients', $id);
|
||||
break;
|
||||
case ClientContact::class:
|
||||
return $this->transformId('client_contacts', $id);
|
||||
break;
|
||||
case Credit::class:
|
||||
return $this->transformId('credits', $id);
|
||||
break;
|
||||
case Expense::class:
|
||||
return $this->transformId('expenses', $id);
|
||||
break;
|
||||
case 'invoices':
|
||||
return $this->transformId('invoices', $id);
|
||||
break;
|
||||
case Payment::class:
|
||||
return $this->transformId('payments', $id);
|
||||
break;
|
||||
case Product::class:
|
||||
return $this->transformId('products', $id);
|
||||
break;
|
||||
case Quote::class:
|
||||
return $this->transformId('quotes', $id);
|
||||
break;
|
||||
case RecurringInvoice::class:
|
||||
return $this->transformId('recurring_invoices', $id);
|
||||
break;
|
||||
case Company::class:
|
||||
return $this->transformId('clients', $id);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function paymentablesImport()
|
||||
{
|
||||
|
||||
foreach($this->backup_json_object->payments as $payment)
|
||||
{
|
||||
|
||||
foreach($payment->paymentables as $paymentable_obj)
|
||||
{
|
||||
|
||||
$paymentable = new Paymentable();
|
||||
$paymentable->payment_id = $this->transformId('payments', $paymentable_obj->payment_id);
|
||||
$paymentable->paymentable_type = $paymentable_obj->paymentable_type;
|
||||
$paymentable->amount = $paymentable_obj->amount;
|
||||
$paymentable->refunded = $paymentable_obj->refunded;
|
||||
$paymentable->created_at = $paymentable_obj->created_at;
|
||||
$paymentable->deleted_at = $paymentable_obj->deleted_at;
|
||||
$paymentable->updated_at = $paymentable_obj->updated_at;
|
||||
$paymentable->paymentable_id = $this->convertPaymentableId($paymentable_obj->paymentable_type, $paymentable_obj->paymentable_id);
|
||||
$paymentable->paymentable_type = $paymentable_obj->paymentable_type;
|
||||
$paymentable->save(['timestamps' => false]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function convertPaymentableId($type, $id)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'invoices':
|
||||
return $this->transformId('invoices', $id);
|
||||
break;
|
||||
case Credit::class:
|
||||
return $this->transformId('credits', $id);
|
||||
break;
|
||||
case Payment::class:
|
||||
return $this->transformId('payments', $id);
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function genericNewClassImport($class, $unset, $transforms, $object_property)
|
||||
{
|
||||
|
||||
$class::unguard();
|
||||
|
||||
|
||||
foreach($this->backup_json_object->{$object_property} as $obj)
|
||||
{
|
||||
|
||||
/* Remove unwanted keys*/
|
||||
$obj_array = (array)$obj;
|
||||
foreach($unset as $un){
|
||||
unset($obj_array[$un]);
|
||||
}
|
||||
|
||||
foreach($trans as $key => $value)
|
||||
{
|
||||
$obj_array["{$value}"] = $this->transformId($object_property, $obj->{$value});
|
||||
$activity_invitation_key = false;
|
||||
|
||||
if($class instanceof Activity){
|
||||
|
||||
if(isset($obj->invitation_id)){
|
||||
|
||||
if(isset($obj->invoice_id))
|
||||
$activity_invitation_key = 'invoice_invitations';
|
||||
elseif(isset($obj->quote_id))
|
||||
$activity_invitation_key = 'quote_invitations';
|
||||
elseif($isset($obj->credit_id))
|
||||
$activity_invitation_key = 'credit_invitations';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$new_obj = $class::firstOrNew(
|
||||
[$match_key => $obj->{$match_key}, 'company_id' => $this->company->id],
|
||||
$obj_array,
|
||||
);
|
||||
|
||||
/* Transform old keys to new keys */
|
||||
foreach($transforms as $transform)
|
||||
{
|
||||
foreach($transform as $key => $value)
|
||||
{
|
||||
if($class instanceof Activity && $activity_invitation_key)
|
||||
$key = $activity_invitation_key;
|
||||
|
||||
$obj_array["{$value}"] = $this->transformId($key, $obj->{$value});
|
||||
}
|
||||
}
|
||||
|
||||
if($class instanceof CompanyGateway) {
|
||||
$obj_array['config'] = encrypt($obj_array['config']);
|
||||
}
|
||||
|
||||
$new_obj = new $class();
|
||||
$new_obj->company_id = $this->company->id;
|
||||
$new_obj->fill($obj_array);
|
||||
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
|
||||
@ -497,10 +1045,119 @@ class ImportCompanyTest extends TestCase
|
||||
}
|
||||
|
||||
$class::reguard();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function genericImportWithoutCompany($class, $unset, $transforms, $object_property, $match_key)
|
||||
{
|
||||
|
||||
$class::unguard();
|
||||
|
||||
foreach($this->backup_json_object->{$object_property} as $obj)
|
||||
{
|
||||
/* Remove unwanted keys*/
|
||||
$obj_array = (array)$obj;
|
||||
foreach($unset as $un){
|
||||
unset($obj_array[$un]);
|
||||
}
|
||||
|
||||
/* Transform old keys to new keys */
|
||||
foreach($transforms as $transform)
|
||||
{
|
||||
foreach($transform as $key => $value)
|
||||
{
|
||||
$obj_array["{$value}"] = $this->transformId($key, $obj->{$value});
|
||||
}
|
||||
}
|
||||
|
||||
/* New to convert product ids from old hashes to new hashes*/
|
||||
if($class instanceof Subscription){
|
||||
$obj_array['product_ids'] = $this->recordProductIds($obj_array['product_ids']);
|
||||
$obj_array['recurring_product_ids'] = $this->recordProductIds($obj_array['recurring_product_ids']);
|
||||
}
|
||||
|
||||
$new_obj = $class::firstOrNew(
|
||||
[$match_key => $obj->{$match_key}],
|
||||
$obj_array,
|
||||
);
|
||||
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
|
||||
if($new_obj instanceof CompanyLedger){
|
||||
|
||||
}
|
||||
else
|
||||
$this->ids["{$object_property}"]["{$obj->hashed_id}"] = $new_obj->id;
|
||||
|
||||
}
|
||||
|
||||
$class::reguard();
|
||||
|
||||
}
|
||||
|
||||
|
||||
private function genericImport($class, $unset, $transforms, $object_property, $match_key)
|
||||
{
|
||||
|
||||
$class::unguard();
|
||||
|
||||
foreach($this->backup_json_object->{$object_property} as $obj)
|
||||
{
|
||||
/* Remove unwanted keys*/
|
||||
$obj_array = (array)$obj;
|
||||
foreach($unset as $un){
|
||||
unset($obj_array[$un]);
|
||||
}
|
||||
|
||||
/* Transform old keys to new keys */
|
||||
foreach($transforms as $transform)
|
||||
{
|
||||
foreach($transform as $key => $value)
|
||||
{
|
||||
$obj_array["{$value}"] = $this->transformId($key, $obj->{$value});
|
||||
}
|
||||
}
|
||||
|
||||
/* New to convert product ids from old hashes to new hashes*/
|
||||
if($class instanceof Subscription){
|
||||
$obj_array['product_ids'] = $this->recordProductIds($obj_array['product_ids']);
|
||||
$obj_array['recurring_product_ids'] = $this->recordProductIds($obj_array['recurring_product_ids']);
|
||||
}
|
||||
|
||||
$new_obj = $class::firstOrNew(
|
||||
[$match_key => $obj->{$match_key}, 'company_id' => $this->company->id],
|
||||
$obj_array,
|
||||
);
|
||||
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
|
||||
if($new_obj instanceof CompanyLedger){
|
||||
}
|
||||
else
|
||||
$this->ids["{$object_property}"]["{$obj->hashed_id}"] = $new_obj->id;
|
||||
|
||||
}
|
||||
|
||||
$class::reguard();
|
||||
|
||||
}
|
||||
|
||||
private function recordProductIds($ids)
|
||||
{
|
||||
|
||||
$id_array = explode(",", $ids);
|
||||
|
||||
$tmp_arr = [];
|
||||
|
||||
foreach($id_array as $id) {
|
||||
|
||||
$tmp_arr[] = $this->encodePrimaryKey($this->transformId('products', $id));
|
||||
}
|
||||
|
||||
return implode(",", $tmp_arr);
|
||||
}
|
||||
|
||||
private function transformId(string $resource, ?string $old): ?int
|
||||
{
|
||||
if(empty($old))
|
||||
@ -511,7 +1168,7 @@ class ImportCompanyTest extends TestCase
|
||||
}
|
||||
|
||||
if (! array_key_exists("{$old}", $this->ids[$resource])) {
|
||||
throw new \Exception("Missing resource key: {$old}");
|
||||
throw new \Exception("Missing {$resource} key: {$old}");
|
||||
}
|
||||
|
||||
return $this->ids[$resource]["{$old}"];
|
||||
|
Binary file not shown.
@ -54,6 +54,10 @@ class UserTest extends TestCase
|
||||
ThrottleRequests::class,
|
||||
PasswordProtection::class
|
||||
);
|
||||
|
||||
// if (config('ninja.testvars.travis') !== false) {
|
||||
// $this->markTestSkipped('Skip test for Travis');
|
||||
// }
|
||||
}
|
||||
|
||||
public function testUserList()
|
||||
@ -95,54 +99,58 @@ class UserTest extends TestCase
|
||||
$this->assertNotNull($arr['data']['company_user']);
|
||||
}
|
||||
|
||||
// public function testUserAttachAndDetach()
|
||||
// {
|
||||
// $this->withoutMiddleware(PasswordProtection::class);
|
||||
public function testUserAttachAndDetach()
|
||||
{
|
||||
|
||||
// $user = UserFactory::create($this->account->id);
|
||||
// $user->first_name = 'Test';
|
||||
// $user->last_name = 'Palloni';
|
||||
// $user->email = $this->default_email;
|
||||
// $user->save();
|
||||
$this->withoutMiddleware(PasswordProtection::class);
|
||||
|
||||
// $data = $user->toArray();
|
||||
$data = [
|
||||
'first_name' => 'Test',
|
||||
'last_name' => 'Palloni',
|
||||
'email' => $this->default_email,
|
||||
];
|
||||
|
||||
// $response = false;
|
||||
$response = false;
|
||||
|
||||
// try {
|
||||
// $response = $this->withHeaders([
|
||||
// 'X-API-SECRET' => config('ninja.api_secret'),
|
||||
// 'X-API-TOKEN' => $this->token,
|
||||
// 'X-API-PASSWORD' => 'ALongAndBriliantPassword',
|
||||
// ])->post('/api/v1/users?include=company_user', $data);
|
||||
try {
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
'X-API-PASSWORD' => 'ALongAndBriliantPassword',
|
||||
])->post('/api/v1/users?include=company_user', $data);
|
||||
|
||||
// } catch (ValidationException $e) {
|
||||
// $message = json_decode($e->validator->getMessageBag(), 1);
|
||||
// nlog($message);
|
||||
// var_dump($message);
|
||||
// $this->assertNotNull($message);
|
||||
// }
|
||||
} catch (ValidationException $e) {
|
||||
$message = json_decode($e->validator->getMessageBag(), 1);
|
||||
nlog($message);
|
||||
var_dump($message);
|
||||
$this->assertNotNull($message);
|
||||
}
|
||||
|
||||
// $response->assertStatus(200);
|
||||
$response->assertStatus(200);
|
||||
|
||||
// // $this->assertNotNull($user->company_user);
|
||||
// // $this->assertEquals($user->company_user->company_id, $this->company->id);
|
||||
$arr = $response->json();
|
||||
|
||||
// $response = $this->withHeaders([
|
||||
// 'X-API-SECRET' => config('ninja.api_secret'),
|
||||
// 'X-API-TOKEN' => $this->token,
|
||||
// 'X-API-PASSWORD' => 'ALongAndBriliantPassword',
|
||||
// ])->delete('/api/v1/users/'.$this->encodePrimaryKey($user->id).'/detach_from_company?include=company_user');
|
||||
// $this->assertNotNull($user->company_user);
|
||||
// $this->assertEquals($user->company_user->company_id, $this->company->id);
|
||||
|
||||
// $response->assertStatus(200);
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
'X-API-PASSWORD' => 'ALongAndBriliantPassword',
|
||||
])->delete('/api/v1/users/'.$arr['data']['id'].'/detach_from_company?include=company_user');
|
||||
|
||||
// $cu = CompanyUser::whereUserId($user->id)->whereCompanyId($this->company->id)->first();
|
||||
// $ct = CompanyToken::whereUserId($user->id)->whereCompanyId($this->company->id)->first();
|
||||
$response->assertStatus(200);
|
||||
|
||||
// $this->assertNull($cu);
|
||||
// $this->assertNull($ct);
|
||||
// $this->assertNotNull($user);
|
||||
// }
|
||||
$user_id = $this->decodePrimaryKey($arr['data']['id']);
|
||||
|
||||
$cu = CompanyUser::whereUserId($user_id)->whereCompanyId($this->company->id)->first();
|
||||
$ct = CompanyToken::whereUserId($user_id)->whereCompanyId($this->company->id)->first();
|
||||
$user = User::find($user_id);
|
||||
|
||||
$this->assertNull($cu);
|
||||
$this->assertNull($ct);
|
||||
$this->assertNotNull($user);
|
||||
}
|
||||
|
||||
public function testAttachUserToMultipleCompanies()
|
||||
{
|
||||
@ -168,14 +176,12 @@ class UserTest extends TestCase
|
||||
$cu->save();
|
||||
|
||||
/*Create New Blank User and Attach to Company 2*/
|
||||
$new_user = UserFactory::create($this->account->id);
|
||||
$new_user->first_name = 'Test';
|
||||
$new_user->last_name = 'Palloni';
|
||||
$new_user->email = $this->default_email;
|
||||
$new_user->save();
|
||||
|
||||
$data = $new_user->toArray();
|
||||
|
||||
$data = [
|
||||
'first_name' => 'Test',
|
||||
'last_name' => 'Palloni',
|
||||
'email' => $this->default_email,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $company_token->token,
|
||||
|
@ -34,9 +34,9 @@ class SystemHealthTest extends TestCase
|
||||
|
||||
$this->assertTrue((bool) $results['system_health']);
|
||||
|
||||
$this->assertTrue($results['extensions'][0]['mysqli']);
|
||||
$this->assertTrue($results['extensions'][1]['gd']);
|
||||
$this->assertTrue($results['extensions'][2]['curl']);
|
||||
$this->assertTrue($results['extensions'][3]['zip']);
|
||||
// $this->assertTrue($results['extensions'][0]['mysqli']);
|
||||
$this->assertTrue($results['extensions'][0]['gd']);
|
||||
$this->assertTrue($results['extensions'][1]['curl']);
|
||||
$this->assertTrue($results['extensions'][2]['zip']);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user