mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-15 15:42:51 +01:00
Merge branch 'v5-develop' into v5-stable
This commit is contained in:
commit
15d04dc253
@ -1 +1 @@
|
|||||||
5.2.16
|
5.2.17
|
@ -29,7 +29,7 @@ class RecurringInvoiceToInvoiceFactory
|
|||||||
$invoice->public_notes = $recurring_invoice->public_notes;
|
$invoice->public_notes = $recurring_invoice->public_notes;
|
||||||
$invoice->private_notes = $recurring_invoice->private_notes;
|
$invoice->private_notes = $recurring_invoice->private_notes;
|
||||||
//$invoice->date = now()->format($client->date_format());
|
//$invoice->date = now()->format($client->date_format());
|
||||||
$invoice->due_date = $recurring_invoice->calculateDueDate($recurring_invoice->next_send_date);
|
//$invoice->due_date = $recurring_invoice->calculateDueDate(now());
|
||||||
$invoice->is_deleted = $recurring_invoice->is_deleted;
|
$invoice->is_deleted = $recurring_invoice->is_deleted;
|
||||||
$invoice->line_items = $recurring_invoice->line_items;
|
$invoice->line_items = $recurring_invoice->line_items;
|
||||||
$invoice->tax_name1 = $recurring_invoice->tax_name1;
|
$invoice->tax_name1 = $recurring_invoice->tax_name1;
|
||||||
|
@ -76,6 +76,11 @@ class ClientFilters extends QueryFilters
|
|||||||
return $this->builder->where('id_number', $id_number);
|
return $this->builder->where('id_number', $id_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function number(string $number):Builder
|
||||||
|
{
|
||||||
|
return $this->builder->where('number', $number);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter based on search text.
|
* Filter based on search text.
|
||||||
*
|
*
|
||||||
|
@ -19,6 +19,7 @@ use App\Factory\RecurringInvoiceFactory;
|
|||||||
use App\Http\Requests\Invoice\StoreInvoiceRequest;
|
use App\Http\Requests\Invoice\StoreInvoiceRequest;
|
||||||
use App\Http\Requests\Preview\PreviewInvoiceRequest;
|
use App\Http\Requests\Preview\PreviewInvoiceRequest;
|
||||||
use App\Jobs\Util\PreviewPdf;
|
use App\Jobs\Util\PreviewPdf;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\ClientContact;
|
use App\Models\ClientContact;
|
||||||
use App\Models\Credit;
|
use App\Models\Credit;
|
||||||
@ -30,8 +31,8 @@ use App\Repositories\CreditRepository;
|
|||||||
use App\Repositories\InvoiceRepository;
|
use App\Repositories\InvoiceRepository;
|
||||||
use App\Repositories\QuoteRepository;
|
use App\Repositories\QuoteRepository;
|
||||||
use App\Repositories\RecurringInvoiceRepository;
|
use App\Repositories\RecurringInvoiceRepository;
|
||||||
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
|
||||||
use App\Services\PdfMaker\Design as PdfDesignModel;
|
use App\Services\PdfMaker\Design as PdfDesignModel;
|
||||||
|
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
||||||
use App\Services\PdfMaker\Design;
|
use App\Services\PdfMaker\Design;
|
||||||
use App\Services\PdfMaker\PdfMaker;
|
use App\Services\PdfMaker\PdfMaker;
|
||||||
use App\Utils\HostedPDF\NinjaPdf;
|
use App\Utils\HostedPDF\NinjaPdf;
|
||||||
@ -168,25 +169,29 @@ class PreviewController extends BaseController
|
|||||||
|
|
||||||
public function live(PreviewInvoiceRequest $request)
|
public function live(PreviewInvoiceRequest $request)
|
||||||
{
|
{
|
||||||
|
$company = auth()->user()->company();
|
||||||
|
|
||||||
|
MultiDB::setDb($company->db);
|
||||||
|
|
||||||
if($request->input('entity') == 'invoice'){
|
if($request->input('entity') == 'invoice'){
|
||||||
$repo = new InvoiceRepository();
|
$repo = new InvoiceRepository();
|
||||||
$factory = InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
$entity_obj = InvoiceFactory::create($company->id, auth()->user()->id);
|
||||||
$class = Invoice::class;
|
$class = Invoice::class;
|
||||||
}
|
}
|
||||||
elseif($request->input('entity') == 'quote'){
|
elseif($request->input('entity') == 'quote'){
|
||||||
$repo = new QuoteRepository();
|
$repo = new QuoteRepository();
|
||||||
$factory = QuoteFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
$entity_obj = QuoteFactory::create($company->id, auth()->user()->id);
|
||||||
$class = Quote::class;
|
$class = Quote::class;
|
||||||
|
|
||||||
}
|
}
|
||||||
elseif($request->input('entity') == 'credit'){
|
elseif($request->input('entity') == 'credit'){
|
||||||
$repo = new CreditRepository();
|
$repo = new CreditRepository();
|
||||||
$factory = CreditFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
$entity_obj = CreditFactory::create($company->id, auth()->user()->id);
|
||||||
$class = Credit::class;
|
$class = Credit::class;
|
||||||
}
|
}
|
||||||
elseif($request->input('entity') == 'recurring_invoice'){
|
elseif($request->input('entity') == 'recurring_invoice'){
|
||||||
$repo = new RecurringInvoiceRepository();
|
$repo = new RecurringInvoiceRepository();
|
||||||
$factory = RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
$entity_obj = RecurringInvoiceFactory::create($company->id, auth()->user()->id);
|
||||||
$class = RecurringInvoice::class;
|
$class = RecurringInvoice::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,13 +202,15 @@ class PreviewController extends BaseController
|
|||||||
|
|
||||||
if($request->has('entity_id')){
|
if($request->has('entity_id')){
|
||||||
|
|
||||||
$entity_obj = $class::withTrashed()->whereId($this->decodePrimaryKey($request->input('entity_id')))->company()->first();
|
$entity_obj = $class::on(config('database.default'))
|
||||||
$entity_obj = $repo->save($request->all(), $entity_obj);
|
->where('id', $this->decodePrimaryKey($request->input('entity_id')))
|
||||||
|
->where('company_id', $company->id)
|
||||||
|
->withTrashed()
|
||||||
|
->first();
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
$entity_obj = $repo->save($request->all(), $factory);
|
$entity_obj = $repo->save($request->all(), $entity_obj);
|
||||||
}
|
|
||||||
|
|
||||||
$entity_obj->load('client');
|
$entity_obj->load('client');
|
||||||
|
|
||||||
@ -218,7 +225,7 @@ class PreviewController extends BaseController
|
|||||||
|
|
||||||
/* Catch all in case migration doesn't pass back a valid design */
|
/* Catch all in case migration doesn't pass back a valid design */
|
||||||
if(!$design)
|
if(!$design)
|
||||||
$design = Design::find(2);
|
$design = \App\Models\Design::find(2);
|
||||||
|
|
||||||
if ($design->is_custom) {
|
if ($design->is_custom) {
|
||||||
$options = [
|
$options = [
|
||||||
@ -277,7 +284,7 @@ class PreviewController extends BaseController
|
|||||||
return (new NinjaPdf())->build($maker->getCompiledHTML(true));
|
return (new NinjaPdf())->build($maker->getCompiledHTML(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), $company);
|
||||||
|
|
||||||
|
|
||||||
if(Ninja::isHosted())
|
if(Ninja::isHosted())
|
||||||
@ -302,7 +309,7 @@ class PreviewController extends BaseController
|
|||||||
$t = app('translator');
|
$t = app('translator');
|
||||||
$t->replace(Ninja::transformTranslations(auth()->user()->company()->settings));
|
$t->replace(Ninja::transformTranslations(auth()->user()->company()->settings));
|
||||||
|
|
||||||
DB::connection(config('database.default'))->beginTransaction();
|
DB::connection(auth()->user()->company()->db)->beginTransaction();
|
||||||
|
|
||||||
$client = Client::factory()->create([
|
$client = Client::factory()->create([
|
||||||
'user_id' => auth()->user()->id,
|
'user_id' => auth()->user()->id,
|
||||||
@ -377,7 +384,7 @@ class PreviewController extends BaseController
|
|||||||
|
|
||||||
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
|
||||||
|
|
||||||
DB::connection(config('database.default'))->rollBack();
|
DB::connection(auth()->user()->company()->db)->rollBack();
|
||||||
|
|
||||||
$response = Response::make($file_path, 200);
|
$response = Response::make($file_path, 200);
|
||||||
$response->header('Content-Type', 'application/pdf');
|
$response->header('Content-Type', 'application/pdf');
|
||||||
|
@ -158,6 +158,37 @@ class RequiredClientInfo extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function showCopyBillingCheckbox(): bool
|
||||||
|
{
|
||||||
|
$fields = [];
|
||||||
|
|
||||||
|
collect($this->fields)->map(function ($field) use (&$fields) {
|
||||||
|
if (! array_key_exists('filled', $field)) {
|
||||||
|
$fields[] = $field['name'];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
if (Str::startsWith($field, 'client_shipping')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleCopyBilling(): void
|
||||||
|
{
|
||||||
|
$this->emit('update-shipping-data', [
|
||||||
|
'client_shipping_address_line_1' => $this->contact->client->address1,
|
||||||
|
'client_shipping_address_line_2' => $this->contact->client->address2,
|
||||||
|
'client_shipping_city' => $this->contact->client->city,
|
||||||
|
'client_shipping_state' => $this->contact->client->state,
|
||||||
|
'client_shipping_postal_code' => $this->contact->client->postal_code,
|
||||||
|
'client_shipping_country_id' => $this->contact->client->country_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
count($this->fields) > 0
|
count($this->fields) > 0
|
||||||
|
@ -74,9 +74,9 @@ class PaymentWebhookRequest extends Request
|
|||||||
{
|
{
|
||||||
// For testing purposes we'll slow down the webhook processing by 2 seconds
|
// For testing purposes we'll slow down the webhook processing by 2 seconds
|
||||||
// to make sure webhook request doesn't came before our processing.
|
// to make sure webhook request doesn't came before our processing.
|
||||||
if (app()->environment() !== 'production') {
|
//if (app()->environment() !== 'production') {
|
||||||
sleep(2);
|
sleep(2);
|
||||||
}
|
//}
|
||||||
|
|
||||||
// Some gateways, like Checkout, we can dynamically pass payment hash,
|
// Some gateways, like Checkout, we can dynamically pass payment hash,
|
||||||
// which we will resolve here and get payment information from it.
|
// which we will resolve here and get payment information from it.
|
||||||
|
@ -37,7 +37,7 @@ class PreviewInvoiceRequest extends Request
|
|||||||
{
|
{
|
||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
$rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
// $rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||||
|
|
||||||
$rules['number'] = ['nullable'];
|
$rules['number'] = ['nullable'];
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class AutoBillCron
|
|||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
|
|
||||||
/* Get all invoices where the send date is less than NOW + 30 minutes() */
|
/* Get all invoices where the send date is less than NOW + 30 minutes() */
|
||||||
nlog("Performing Autobilling ".Carbon::now()->format('Y-m-d h:i:s'));
|
info("Performing Autobilling ".Carbon::now()->format('Y-m-d h:i:s'));
|
||||||
|
|
||||||
if (! config('ninja.db.multi_db_enabled')) {
|
if (! config('ninja.db.multi_db_enabled')) {
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ class AutoBillCron
|
|||||||
|
|
||||||
private function runAutoBiller(Invoice $invoice)
|
private function runAutoBiller(Invoice $invoice)
|
||||||
{
|
{
|
||||||
nlog("Firing autobill for {$invoice->company_id} - {$invoice->number}");
|
info("Firing autobill for {$invoice->company_id} - {$invoice->number}");
|
||||||
$invoice->service()->autoBill()->save();
|
$invoice->service()->autoBill()->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ class EmailEntity implements ShouldQueue
|
|||||||
$nmo->reminder_template = $this->reminder_template;
|
$nmo->reminder_template = $this->reminder_template;
|
||||||
$nmo->entity = $this->entity;
|
$nmo->entity = $this->entity;
|
||||||
|
|
||||||
NinjaMailerJob::dispatch($nmo);
|
NinjaMailerJob::dispatchNow($nmo);
|
||||||
|
|
||||||
/* Mark entity sent */
|
/* Mark entity sent */
|
||||||
$this->entity->service()->markSent()->save();
|
$this->entity->service()->markSent()->save();
|
||||||
|
@ -60,7 +60,10 @@ class SendRecurring implements ShouldQueue
|
|||||||
$invoice = RecurringInvoiceToInvoiceFactory::create($this->recurring_invoice, $this->recurring_invoice->client);
|
$invoice = RecurringInvoiceToInvoiceFactory::create($this->recurring_invoice, $this->recurring_invoice->client);
|
||||||
|
|
||||||
$invoice->date = now()->format('Y-m-d');
|
$invoice->date = now()->format('Y-m-d');
|
||||||
|
$invoice->due_date = $this->recurring_invoice->calculateDueDate(now()->format('Y-m-d'));
|
||||||
|
|
||||||
|
if($invoice->client->getSetting('auto_email_invoice'))
|
||||||
|
{
|
||||||
$invoice = $invoice->service()
|
$invoice = $invoice->service()
|
||||||
->markSent()
|
->markSent()
|
||||||
->applyNumber()
|
->applyNumber()
|
||||||
@ -68,6 +71,14 @@ class SendRecurring implements ShouldQueue
|
|||||||
->fillDefaults()
|
->fillDefaults()
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
|
||||||
|
$invoice = $invoice->service()
|
||||||
|
->fillDefaults()
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
|
||||||
nlog("updating recurring invoice dates");
|
nlog("updating recurring invoice dates");
|
||||||
/* Set next date here to prevent a recurring loop forming */
|
/* Set next date here to prevent a recurring loop forming */
|
||||||
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
|
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
|
||||||
@ -86,14 +97,14 @@ class SendRecurring implements ShouldQueue
|
|||||||
$this->recurring_invoice->save();
|
$this->recurring_invoice->save();
|
||||||
|
|
||||||
//Admin notification for recurring invoice sent.
|
//Admin notification for recurring invoice sent.
|
||||||
if ($invoice->invitations->count() >= 1) {
|
if ($invoice->invitations->count() >= 1 ) {
|
||||||
$invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', 'email_template_invoice');
|
$invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', 'email_template_invoice');
|
||||||
}
|
}
|
||||||
|
|
||||||
nlog("Invoice {$invoice->number} created");
|
nlog("Invoice {$invoice->number} created");
|
||||||
|
|
||||||
$invoice->invitations->each(function ($invitation) use ($invoice) {
|
$invoice->invitations->each(function ($invitation) use ($invoice) {
|
||||||
if ($invitation->contact && strlen($invitation->contact->email) >=1) {
|
if ($invitation->contact && strlen($invitation->contact->email) >=1 && $invoice->client->getSetting('auto_email_invoice')) {
|
||||||
|
|
||||||
try{
|
try{
|
||||||
EmailEntity::dispatch($invitation, $invoice->company);
|
EmailEntity::dispatch($invitation, $invoice->company);
|
||||||
|
@ -35,11 +35,14 @@ use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
|
|||||||
use App\Http\ValidationRules\ValidUserForCompany;
|
use App\Http\ValidationRules\ValidUserForCompany;
|
||||||
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||||
use App\Jobs\Company\CreateCompanyToken;
|
use App\Jobs\Company\CreateCompanyToken;
|
||||||
|
use App\Jobs\Mail\NinjaMailerJob;
|
||||||
|
use App\Jobs\Mail\NinjaMailerObject;
|
||||||
use App\Jobs\Ninja\CheckCompanyData;
|
use App\Jobs\Ninja\CheckCompanyData;
|
||||||
use App\Jobs\Ninja\CompanySizeCheck;
|
use App\Jobs\Ninja\CompanySizeCheck;
|
||||||
use App\Jobs\Util\VersionCheck;
|
use App\Jobs\Util\VersionCheck;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Mail\MigrationCompleted;
|
use App\Mail\MigrationCompleted;
|
||||||
|
use App\Mail\Migration\StripeConnectMigration;
|
||||||
use App\Models\Activity;
|
use App\Models\Activity;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\ClientContact;
|
use App\Models\ClientContact;
|
||||||
@ -87,6 +90,7 @@ use Illuminate\Http\UploadedFile;
|
|||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@ -214,27 +218,15 @@ class Import implements ShouldQueue
|
|||||||
$this->{$method}($data[$import]);
|
$this->{$method}($data[$import]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if(Ninja::isHosted() && array_key_exists('ninja_tokens', $data))
|
$task_statuses = [
|
||||||
$this->processNinjaTokens($data['ninja_tokens']);
|
['name' => ctrans('texts.backlog'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 1],
|
||||||
|
['name' => ctrans('texts.ready_to_do'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 2],
|
||||||
|
['name' => ctrans('texts.in_progress'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 3],
|
||||||
|
['name' => ctrans('texts.done'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 4],
|
||||||
|
|
||||||
// $this->fixData();
|
];
|
||||||
|
|
||||||
$this->setInitialCompanyLedgerBalances();
|
TaskStatus::insert($task_statuses);
|
||||||
|
|
||||||
// $this->fixClientBalances();
|
|
||||||
$check_data = CheckCompanyData::dispatchNow($this->company, md5(time()));
|
|
||||||
|
|
||||||
try{
|
|
||||||
Mail::to($this->user->email, $this->user->name())
|
|
||||||
->send(new MigrationCompleted($this->company, implode("<br>",$check_data)));
|
|
||||||
}
|
|
||||||
catch(\Exception $e) {
|
|
||||||
|
|
||||||
nlog($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*After a migration first some basic jobs to ensure the system is up to date*/
|
|
||||||
VersionCheck::dispatch();
|
|
||||||
|
|
||||||
$account = $this->company->account;
|
$account = $this->company->account;
|
||||||
$account->default_company_id = $this->company->id;
|
$account->default_company_id = $this->company->id;
|
||||||
@ -246,19 +238,35 @@ class Import implements ShouldQueue
|
|||||||
$this->company->save();
|
$this->company->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->setInitialCompanyLedgerBalances();
|
||||||
|
|
||||||
|
// $this->fixClientBalances();
|
||||||
|
$check_data = CheckCompanyData::dispatchNow($this->company, md5(time()));
|
||||||
|
|
||||||
|
// if(Ninja::isHosted() && array_key_exists('ninja_tokens', $data))
|
||||||
|
$this->processNinjaTokens($data['ninja_tokens']);
|
||||||
|
|
||||||
|
// $this->fixData();
|
||||||
|
try{
|
||||||
|
App::forgetInstance('translator');
|
||||||
|
$t = app('translator');
|
||||||
|
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||||
|
|
||||||
|
Mail::to($this->user->email, $this->user->name())
|
||||||
|
->send(new MigrationCompleted($this->company, implode("<br>",$check_data)));
|
||||||
|
}
|
||||||
|
catch(\Exception $e) {
|
||||||
|
nlog($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*After a migration first some basic jobs to ensure the system is up to date*/
|
||||||
|
VersionCheck::dispatch();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// CreateCompanyPaymentTerms::dispatchNow($sp035a66, $spaa9f78);
|
// CreateCompanyPaymentTerms::dispatchNow($sp035a66, $spaa9f78);
|
||||||
// CreateCompanyTaskStatuses::dispatchNow($this->company, $this->user);
|
// CreateCompanyTaskStatuses::dispatchNow($this->company, $this->user);
|
||||||
|
|
||||||
$task_statuses = [
|
|
||||||
['name' => ctrans('texts.backlog'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 1],
|
|
||||||
['name' => ctrans('texts.ready_to_do'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 2],
|
|
||||||
['name' => ctrans('texts.in_progress'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 3],
|
|
||||||
['name' => ctrans('texts.done'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now(), 'status_order' => 4],
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
TaskStatus::insert($task_statuses);
|
|
||||||
|
|
||||||
info('Completed🚀🚀🚀🚀🚀 at '.now());
|
info('Completed🚀🚀🚀🚀🚀 at '.now());
|
||||||
|
|
||||||
unlink($this->file_path);
|
unlink($this->file_path);
|
||||||
@ -1289,7 +1297,8 @@ class Import implements ShouldQueue
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!$entity)
|
if(!$entity)
|
||||||
throw new Exception("Resource invoice/quote document not available.");
|
continue;
|
||||||
|
// throw new Exception("Resource invoice/quote document not available.");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1381,9 +1390,21 @@ class Import implements ShouldQueue
|
|||||||
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
|
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* On Hosted platform we need to advise Stripe users to connect with Stripe Connect */
|
||||||
if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
|
if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
|
||||||
|
|
||||||
|
$nmo = new NinjaMailerObject;
|
||||||
|
$nmo->mailable = new StripeConnectMigration($this->company);
|
||||||
|
$nmo->company = $this->company;
|
||||||
|
$nmo->settings = $this->company->settings;
|
||||||
|
$nmo->to_user = $this->user;
|
||||||
|
NinjaMailerJob::dispatch($nmo);
|
||||||
|
|
||||||
$modified['gateway_key'] = 'd14dd26a47cecc30fdd65700bfb67b34';
|
$modified['gateway_key'] = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||||
$modified['fees_and_limits'] = [];
|
|
||||||
|
//why do we set this to a blank array?
|
||||||
|
//$modified['fees_and_limits'] = [];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$company_gateway = CompanyGateway::create($modified);
|
$company_gateway = CompanyGateway::create($modified);
|
||||||
|
@ -133,6 +133,7 @@ class PaymentEmailEngine extends BaseEmailEngine
|
|||||||
$data['$email'] = ['value' => isset($this->contact) ? $this->contact->email : 'no contact email on record', 'label' => ctrans('texts.email')];
|
$data['$email'] = ['value' => isset($this->contact) ? $this->contact->email : 'no contact email on record', 'label' => ctrans('texts.email')];
|
||||||
$data['$client_name'] = ['value' => $this->client->present()->name() ?: ' ', 'label' => ctrans('texts.client_name')];
|
$data['$client_name'] = ['value' => $this->client->present()->name() ?: ' ', 'label' => ctrans('texts.client_name')];
|
||||||
$data['$client.name'] = &$data['$client_name'];
|
$data['$client.name'] = &$data['$client_name'];
|
||||||
|
$data['$client'] = &$data['$client_name'];
|
||||||
$data['$client.address1'] = &$data['$address1'];
|
$data['$client.address1'] = &$data['$address1'];
|
||||||
$data['$client.address2'] = &$data['$address2'];
|
$data['$client.address2'] = &$data['$address2'];
|
||||||
$data['$client_address'] = ['value' => $this->client->present()->address() ?: ' ', 'label' => ctrans('texts.address')];
|
$data['$client_address'] = ['value' => $this->client->present()->address() ?: ' ', 'label' => ctrans('texts.address')];
|
||||||
@ -164,6 +165,7 @@ class PaymentEmailEngine extends BaseEmailEngine
|
|||||||
$data['$contact.custom2'] = ['value' => isset($this->contact) ? $this->contact->custom_value2 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
|
$data['$contact.custom2'] = ['value' => isset($this->contact) ? $this->contact->custom_value2 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
|
||||||
$data['$contact.custom3'] = ['value' => isset($this->contact) ? $this->contact->custom_value3 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
|
$data['$contact.custom3'] = ['value' => isset($this->contact) ? $this->contact->custom_value3 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
|
||||||
$data['$contact.custom4'] = ['value' => isset($this->contact) ? $this->contact->custom_value4 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
|
$data['$contact.custom4'] = ['value' => isset($this->contact) ? $this->contact->custom_value4 : ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'contact1')];
|
||||||
|
$data['$firstName'] = &$data['$contact.first_name'];
|
||||||
|
|
||||||
$data['$company.city_state_postal'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
|
$data['$company.city_state_postal'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, false) ?: ' ', 'label' => ctrans('texts.city_state_postal')];
|
||||||
$data['$company.postal_city_state'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
|
$data['$company.postal_city_state'] = ['value' => $this->company->present()->cityStateZip($this->settings->city, $this->settings->state, $this->settings->postal_code, true) ?: ' ', 'label' => ctrans('texts.postal_city_state')];
|
||||||
@ -191,7 +193,11 @@ class PaymentEmailEngine extends BaseEmailEngine
|
|||||||
$data['$company4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company4', $this->settings->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company4')];
|
$data['$company4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company4', $this->settings->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company4')];
|
||||||
|
|
||||||
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->payment->getLink().'">'.ctrans('texts.view_payment').'</a>', 'label' => ctrans('texts.view_payment')];
|
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->payment->getLink().'">'.ctrans('texts.view_payment').'</a>', 'label' => ctrans('texts.view_payment')];
|
||||||
|
$data['$paymentLink'] = &$data['$view_link'];
|
||||||
|
$data['$portalButton'] = &$data['$view_link'];
|
||||||
|
|
||||||
$data['$view_url'] = ['value' => $this->payment->getLink(), 'label' => ctrans('texts.view_payment')];
|
$data['$view_url'] = ['value' => $this->payment->getLink(), 'label' => ctrans('texts.view_payment')];
|
||||||
|
$data['$signature'] = ['value' => $this->settings->email_signature ?: ' ', 'label' => ''];
|
||||||
|
|
||||||
$data['$invoices'] = ['value' => $this->formatInvoices(), 'label' => ctrans('texts.invoices')];
|
$data['$invoices'] = ['value' => $this->formatInvoices(), 'label' => ctrans('texts.invoices')];
|
||||||
|
|
||||||
|
55
app/Mail/Migration/StripeConnectMigration.php
Normal file
55
app/Mail/Migration/StripeConnectMigration.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://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Mail\Migration;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class StripeConnectMigration extends Mailable
|
||||||
|
{
|
||||||
|
// use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public $settings;
|
||||||
|
|
||||||
|
public $logo;
|
||||||
|
|
||||||
|
public $whitelabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($company)
|
||||||
|
{
|
||||||
|
$this->company = $company;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the message.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function build()
|
||||||
|
{
|
||||||
|
$this->settings = $this->company->settings;
|
||||||
|
$this->logo = $this->company->present()->logo();
|
||||||
|
$this->whitelabel = $this->company->account->isPaid();
|
||||||
|
|
||||||
|
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||||
|
->subject(ctrans('texts.stripe_connect_migration_title'))
|
||||||
|
->view('email.migration.stripe_connect');
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,11 @@ use App\DataMapper\ClientSettings;
|
|||||||
use App\DataMapper\CompanySettings;
|
use App\DataMapper\CompanySettings;
|
||||||
use App\DataMapper\FeesAndLimits;
|
use App\DataMapper\FeesAndLimits;
|
||||||
use App\Models\CompanyGateway;
|
use App\Models\CompanyGateway;
|
||||||
|
use App\Models\Expense;
|
||||||
use App\Models\Presenters\ClientPresenter;
|
use App\Models\Presenters\ClientPresenter;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\Quote;
|
||||||
|
use App\Models\Task;
|
||||||
use App\Services\Client\ClientService;
|
use App\Services\Client\ClientService;
|
||||||
use App\Utils\Traits\AppSetup;
|
use App\Utils\Traits\AppSetup;
|
||||||
use App\Utils\Traits\GeneratesCounter;
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
@ -153,6 +157,16 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
return $this->hasMany(ClientGatewayToken::class);
|
return $this->hasMany(ClientGatewayToken::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function expenses()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Expense::class)->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function projects()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Project::class)->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the specific payment token per
|
* Retrieves the specific payment token per
|
||||||
* gateway - per payment method.
|
* gateway - per payment method.
|
||||||
@ -217,6 +231,16 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
return $this->hasMany(Invoice::class)->withTrashed();
|
return $this->hasMany(Invoice::class)->withTrashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function quotes()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Quote::class)->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tasks()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Task::class)->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
public function recurring_invoices()
|
public function recurring_invoices()
|
||||||
{
|
{
|
||||||
return $this->hasMany(RecurringInvoice::class)->withTrashed();
|
return $this->hasMany(RecurringInvoice::class)->withTrashed();
|
||||||
@ -774,7 +798,7 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
|
|
||||||
public function payments()
|
public function payments()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Payment::class);
|
return $this->hasMany(Payment::class)->withTrashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function timezone_offset()
|
public function timezone_offset()
|
||||||
|
@ -95,8 +95,12 @@ class ACH
|
|||||||
return render('gateways.stripe.ach.verify', $data);
|
return render('gateways.stripe.ach.verify', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processVerification($request, ClientGatewayToken $token)
|
public function processVerification(Request $request, ClientGatewayToken $token)
|
||||||
{
|
{
|
||||||
|
$request->validate([
|
||||||
|
'transactions.*' => ['integer', 'min:1'],
|
||||||
|
]);
|
||||||
|
|
||||||
if (isset($token->meta->state) && $token->meta->state === 'authorized') {
|
if (isset($token->meta->state) && $token->meta->state === 'authorized') {
|
||||||
return redirect()
|
return redirect()
|
||||||
->route('client.payment_methods.show', $token->hashed_id)
|
->route('client.payment_methods.show', $token->hashed_id)
|
||||||
@ -105,7 +109,7 @@ class ACH
|
|||||||
|
|
||||||
$this->stripe->init();
|
$this->stripe->init();
|
||||||
|
|
||||||
$bank_account = Customer::retrieveSource($request->customer, ['source' => $request->source], $this->stripe->stripe_connect_auth);
|
$bank_account = Customer::retrieveSource($request->customer, $request->source, [], $this->stripe->stripe_connect_auth);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$bank_account->verify(['amounts' => request()->transactions]);
|
$bank_account->verify(['amounts' => request()->transactions]);
|
||||||
@ -183,7 +187,7 @@ class ACH
|
|||||||
return $this->processUnsuccessfulPayment($state);
|
return $this->processUnsuccessfulPayment($state);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
if ($e instanceof CardException) {
|
if ($e instanceof CardException) {
|
||||||
return redirect()->route('client.payment_methods.verification', ['payment_method' => ClientGatewayToken::first()->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
|
return redirect()->route('client.payment_methods.verification', ['payment_method' => $source->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||||
|
@ -125,7 +125,7 @@ class ActivityRepository extends BaseRepository
|
|||||||
|
|
||||||
$design = Design::find($entity_design_id);
|
$design = Design::find($entity_design_id);
|
||||||
|
|
||||||
if(!$entity->invitations()->exists()){
|
if(!$entity->invitations()->exists() || !$design){
|
||||||
nlog("No invitations for entity {$entity->id} - {$entity->number}");
|
nlog("No invitations for entity {$entity->id} - {$entity->number}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ class PaymentRepository extends BaseRepository {
|
|||||||
|
|
||||||
if ( ! $is_existing_payment && ! $this->import_mode ) {
|
if ( ! $is_existing_payment && ! $this->import_mode ) {
|
||||||
|
|
||||||
if (array_key_exists('email_receipt', $data) && $data['email_receipt'] == true)
|
if (array_key_exists('email_receipt', $data) && $data['email_receipt'] == 'true')
|
||||||
$payment->service()->sendEmail();
|
$payment->service()->sendEmail();
|
||||||
elseif(!array_key_exists('email_receipt', $data) && $payment->client->getSetting('client_manual_payment_notification'))
|
elseif(!array_key_exists('email_receipt', $data) && $payment->client->getSetting('client_manual_payment_notification'))
|
||||||
$payment->service()->sendEmail();
|
$payment->service()->sendEmail();
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace App\Services\Client;
|
namespace App\Services\Client;
|
||||||
|
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
|
use App\Services\Client\Merge;
|
||||||
use App\Services\Client\PaymentMethod;
|
use App\Services\Client\PaymentMethod;
|
||||||
use App\Utils\Number;
|
use App\Utils\Number;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
@ -77,6 +78,13 @@ class ClientService
|
|||||||
return (new PaymentMethod($this->client, $amount))->run();
|
return (new PaymentMethod($this->client, $amount))->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function merge(Client $mergable_client)
|
||||||
|
{
|
||||||
|
$this->client = (new Merge($this->client, $mergable_client))->run();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function save() :Client
|
public function save() :Client
|
||||||
{
|
{
|
||||||
$this->client->save();
|
$this->client->save();
|
||||||
|
102
app/Services/Client/Merge.php
Normal file
102
app/Services/Client/Merge.php
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Services\Client;
|
||||||
|
|
||||||
|
use App\Factory\CompanyLedgerFactory;
|
||||||
|
use App\Models\Activity;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\CompanyGateway;
|
||||||
|
use App\Models\CompanyLedger;
|
||||||
|
use App\Models\GatewayType;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Services\AbstractService;
|
||||||
|
use App\Utils\Ninja;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
|
class Merge extends AbstractService
|
||||||
|
{
|
||||||
|
public $client;
|
||||||
|
|
||||||
|
public $mergable_client;
|
||||||
|
|
||||||
|
public function __construct(Client $client, Client $mergable_client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
$this->mergable_client = $mergable_client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->client->balance += $this->mergable_client->balance;
|
||||||
|
$this->client->paid_to_date += $this->mergable_client->paid_to_date;
|
||||||
|
$this->client->save();
|
||||||
|
|
||||||
|
$this->updateLedger($this->mergable_client->balance);
|
||||||
|
|
||||||
|
$this->mergable_client->activities()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->contacts()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->gateway_tokens()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->credits()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->expenses()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->invoices()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->payments()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->projects()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->quotes()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->recurring_invoices()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->tasks()->update(['client_id' => $this->client->id]);
|
||||||
|
$this->mergable_client->documents()->update(['documentable_id' => $this->client->id]);
|
||||||
|
|
||||||
|
/* Loop through contacts an only merge distinct contacts by email */
|
||||||
|
$this->mergable_client->contacts->each(function ($contact){
|
||||||
|
|
||||||
|
$exist = $this->client->contacts->contains(function ($client_contact) use($contact){
|
||||||
|
return $client_contact->email == $contact->email;
|
||||||
|
});
|
||||||
|
|
||||||
|
if($exist)
|
||||||
|
{
|
||||||
|
$contact->delete();
|
||||||
|
$contact->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->mergable_client->forceDelete();
|
||||||
|
|
||||||
|
return $this->client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateLedger($adjustment)
|
||||||
|
{
|
||||||
|
$balance = 0;
|
||||||
|
|
||||||
|
$company_ledger = CompanyLedger::whereClientId($this->client->id)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($company_ledger) {
|
||||||
|
$balance = $company_ledger->balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
$company_ledger = CompanyLedgerFactory::create($this->client->company_id, $this->client->user_id);
|
||||||
|
$company_ledger->client_id = $this->client->id;
|
||||||
|
$company_ledger->adjustment = $adjustment;
|
||||||
|
$company_ledger->notes = "Balance update after merging " . $this->mergable_client->present()->name();
|
||||||
|
$company_ledger->balance = $balance + $adjustment;
|
||||||
|
$company_ledger->activity_id = Activity::UPDATE_CLIENT;
|
||||||
|
$company_ledger->save();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -74,6 +74,8 @@ class PaymentMethod
|
|||||||
->company
|
->company
|
||||||
->company_gateways
|
->company_gateways
|
||||||
->whereIn('id', $transformed_ids)
|
->whereIn('id', $transformed_ids)
|
||||||
|
->where('is_deleted', false)
|
||||||
|
->whereNull('deleted_at')
|
||||||
->where('gateway_key', '!=', '54faab2ab6e3223dbe848b1686490baa')
|
->where('gateway_key', '!=', '54faab2ab6e3223dbe848b1686490baa')
|
||||||
->sortby(function ($model) use ($transformed_ids) { //company gateways are sorted in order of priority
|
->sortby(function ($model) use ($transformed_ids) { //company gateways are sorted in order of priority
|
||||||
return array_search($model->id, $transformed_ids);// this closure sorts for us
|
return array_search($model->id, $transformed_ids);// this closure sorts for us
|
||||||
@ -85,6 +87,7 @@ class PaymentMethod
|
|||||||
->company
|
->company
|
||||||
->company_gateways
|
->company_gateways
|
||||||
->where('gateway_key', '!=', '54faab2ab6e3223dbe848b1686490baa')
|
->where('gateway_key', '!=', '54faab2ab6e3223dbe848b1686490baa')
|
||||||
|
->whereNull('deleted_at')
|
||||||
->where('is_deleted', false);
|
->where('is_deleted', false);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -107,6 +110,8 @@ class PaymentMethod
|
|||||||
->company
|
->company
|
||||||
->company_gateways
|
->company_gateways
|
||||||
->whereIn('id', $transformed_ids)
|
->whereIn('id', $transformed_ids)
|
||||||
|
->where('is_deleted', false)
|
||||||
|
->whereNull('deleted_at')
|
||||||
->where('gateway_key', '54faab2ab6e3223dbe848b1686490baa')
|
->where('gateway_key', '54faab2ab6e3223dbe848b1686490baa')
|
||||||
->sortby(function ($model) use ($transformed_ids) { //company gateways are sorted in order of priority
|
->sortby(function ($model) use ($transformed_ids) { //company gateways are sorted in order of priority
|
||||||
return array_search($model->id, $transformed_ids);// this closure sorts for us
|
return array_search($model->id, $transformed_ids);// this closure sorts for us
|
||||||
@ -118,6 +123,7 @@ class PaymentMethod
|
|||||||
->company
|
->company
|
||||||
->company_gateways
|
->company_gateways
|
||||||
->where('gateway_key', '54faab2ab6e3223dbe848b1686490baa')
|
->where('gateway_key', '54faab2ab6e3223dbe848b1686490baa')
|
||||||
|
->whereNull('deleted_at')
|
||||||
->where('is_deleted', false);
|
->where('is_deleted', false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,10 @@ class AutoBillInvoice extends AbstractService
|
|||||||
->setPaymentHash($payment_hash)
|
->setPaymentHash($payment_hash)
|
||||||
->tokenBilling($gateway_token, $payment_hash);
|
->tokenBilling($gateway_token, $payment_hash);
|
||||||
|
|
||||||
|
if($payment){
|
||||||
|
info("Auto Bill payment captured for ".$this->invoice->number);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->invoice;
|
return $this->invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +160,7 @@ class Design extends BaseDesign
|
|||||||
|
|
||||||
if ($this->type == 'delivery_note') {
|
if ($this->type == 'delivery_note') {
|
||||||
$elements = [
|
$elements = [
|
||||||
|
['element' => 'p', 'content' => ctrans('texts.delivery_note'), 'properties' => ['data-ref' => 'delivery_note-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
|
||||||
['element' => 'p', 'content' => $this->entity->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
|
['element' => 'p', 'content' => $this->entity->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
|
||||||
['element' => 'p', 'content' => $this->entity->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
|
['element' => 'p', 'content' => $this->entity->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
|
||||||
['element' => 'p', 'content' => $this->entity->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
|
['element' => 'p', 'content' => $this->entity->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
namespace App\Services\PdfMaker;
|
namespace App\Services\PdfMaker;
|
||||||
|
|
||||||
|
use League\CommonMark\CommonMarkConverter;
|
||||||
|
|
||||||
class PdfMaker
|
class PdfMaker
|
||||||
{
|
{
|
||||||
use PdfMakerUtilities;
|
use PdfMakerUtilities;
|
||||||
@ -36,6 +38,9 @@ class PdfMaker
|
|||||||
|
|
||||||
private $options;
|
private $options;
|
||||||
|
|
||||||
|
/** @var CommonMarkConverter */
|
||||||
|
protected $commonmark;
|
||||||
|
|
||||||
public function __construct(array $data)
|
public function __construct(array $data)
|
||||||
{
|
{
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
@ -43,6 +48,10 @@ class PdfMaker
|
|||||||
if (array_key_exists('options', $data)) {
|
if (array_key_exists('options', $data)) {
|
||||||
$this->options = $data['options'];
|
$this->options = $data['options'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->commonmark = new CommonMarkConverter([
|
||||||
|
'allow_unsafe_links' => false,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function design(Design $design)
|
public function design(Design $design)
|
||||||
|
@ -91,6 +91,12 @@ trait PdfMakerUtilities
|
|||||||
foreach ($children as $child) {
|
foreach ($children as $child) {
|
||||||
$contains_html = false;
|
$contains_html = false;
|
||||||
|
|
||||||
|
if ($child['element'] !== 'script') {
|
||||||
|
$child['content'] = $this->commonmark->convertToHtml($child['content'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// $child['content'] = array_key_exists('content', $child) ? nl2br($child['content']) : '';
|
||||||
|
|
||||||
if (isset($child['content'])) {
|
if (isset($child['content'])) {
|
||||||
if (isset($child['is_empty']) && $child['is_empty'] === true) {
|
if (isset($child['is_empty']) && $child['is_empty'] === true) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -39,7 +39,7 @@ class UserService
|
|||||||
$nmo->to_user = $this->user;
|
$nmo->to_user = $this->user;
|
||||||
$nmo->settings = $company->settings;
|
$nmo->settings = $company->settings;
|
||||||
|
|
||||||
NinjaMailerJob::dispatch($nmo);
|
NinjaMailerJob::dispatch($nmo, true);
|
||||||
|
|
||||||
Ninja::registerNinjaUser($this->user);
|
Ninja::registerNinjaUser($this->user);
|
||||||
|
|
||||||
|
@ -130,6 +130,7 @@ class HtmlEngine
|
|||||||
$data['$terms'] = &$data['$entity.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_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_invoice').'</a>', 'label' => ctrans('texts.view_invoice')];
|
||||||
$data['$viewLink'] = &$data['$view_link'];
|
$data['$viewLink'] = &$data['$view_link'];
|
||||||
|
$data['$viewButton'] = &$data['$view_link'];
|
||||||
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_invoice')];
|
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_invoice')];
|
||||||
$data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->entity->client->date_format(), $this->entity->client->locale()) ?: ' ', 'label' => ctrans('texts.invoice_date')];
|
$data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->entity->client->date_format(), $this->entity->client->locale()) ?: ' ', 'label' => ctrans('texts.invoice_date')];
|
||||||
|
|
||||||
@ -422,6 +423,10 @@ class HtmlEngine
|
|||||||
$data['$autoBill'] = ['value' => ctrans('texts.auto_bill_notification_placeholder'), 'label' => ''];
|
$data['$autoBill'] = ['value' => ctrans('texts.auto_bill_notification_placeholder'), 'label' => ''];
|
||||||
$data['$auto_bill'] = &$data['$autoBill'];
|
$data['$auto_bill'] = &$data['$autoBill'];
|
||||||
|
|
||||||
|
/*Payment Aliases*/
|
||||||
|
$data['$paymentLink'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_payment').'</a>', 'label' => ctrans('texts.view_payment')];
|
||||||
|
$data['$portalButton'] = &$data['$paymentLink'];
|
||||||
|
|
||||||
$arrKeysLength = array_map('strlen', array_keys($data));
|
$arrKeysLength = array_map('strlen', array_keys($data));
|
||||||
array_multisort($arrKeysLength, SORT_DESC, $data);
|
array_multisort($arrKeysLength, SORT_DESC, $data);
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => '5.2.16',
|
'app_version' => '5.2.17',
|
||||||
'app_tag' => '5.2.16',
|
'app_tag' => '5.2.17',
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', ''),
|
'api_secret' => env('API_SECRET', ''),
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\PaymentType;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class AddZellePaymentType extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Model::unguard();
|
||||||
|
|
||||||
|
$pt = PaymentType::where('name', 'Zelle')->first();
|
||||||
|
|
||||||
|
if(!$pt){
|
||||||
|
|
||||||
|
$payment_type = new PaymentType();
|
||||||
|
$payment_type->id = 33;
|
||||||
|
$payment_type->name = 'Zelle';
|
||||||
|
$payment_type->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
@ -70,6 +70,7 @@ class PaymentTypesSeeder extends Seeder
|
|||||||
['name' => 'GoCardless', 'gateway_type_id' => self::GATEWAY_TYPE_GOCARDLESS],
|
['name' => 'GoCardless', 'gateway_type_id' => self::GATEWAY_TYPE_GOCARDLESS],
|
||||||
['name' => 'Crypto', 'gateway_type_id' => self::GATEWAY_TYPE_CRYPTO],
|
['name' => 'Crypto', 'gateway_type_id' => self::GATEWAY_TYPE_CRYPTO],
|
||||||
['name' => 'Credit', 'gateway_type_id' => self::GATEWAY_TYPE_CREDIT],
|
['name' => 'Credit', 'gateway_type_id' => self::GATEWAY_TYPE_CREDIT],
|
||||||
|
['name' => 'Zelle'],
|
||||||
];
|
];
|
||||||
|
|
||||||
$x = 1;
|
$x = 1;
|
||||||
|
2
public/flutter_service_worker.js
vendored
2
public/flutter_service_worker.js
vendored
@ -32,7 +32,7 @@ const RESOURCES = {
|
|||||||
"manifest.json": "ce1b79950eb917ea619a0a30da27c6a3",
|
"manifest.json": "ce1b79950eb917ea619a0a30da27c6a3",
|
||||||
"version.json": "3f9e03374a3e78d2cab3afd8723d0993",
|
"version.json": "3f9e03374a3e78d2cab3afd8723d0993",
|
||||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||||
"main.dart.js": "2791c77ee5c4768ffdc2ef8aea29967c",
|
"main.dart.js": "02238eed5a325865e74b8a1e7afd03a6",
|
||||||
"/": "d389ab59423a76b2aaaa683ed382c78e"
|
"/": "d389ab59423a76b2aaaa683ed382c78e"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
197389
public/main.dart.js
vendored
197389
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
212086
public/main.foss.dart.js
vendored
212086
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
@ -3948,8 +3948,8 @@ https://invoiceninja.github.io/docs/migration/#troubleshooting',
|
|||||||
'download_timeframe' => 'Use this link to download your files, the link will expire in 1 hour.',
|
'download_timeframe' => 'Use this link to download your files, the link will expire in 1 hour.',
|
||||||
'new_signup' => 'Neue Registrierung',
|
'new_signup' => 'Neue Registrierung',
|
||||||
'new_signup_text' => 'Ein neuer Benutzer wurde von :user - :email von der IP: :ip erstellt',
|
'new_signup_text' => 'Ein neuer Benutzer wurde von :user - :email von der IP: :ip erstellt',
|
||||||
'notification_payment_paid_subject' => 'Neue Zahlung von :Kunde',
|
'notification_payment_paid_subject' => 'Neue Zahlung von :client',
|
||||||
'notification_partial_payment_paid_subject' => 'Neue Anzahlung von :Kunde',
|
'notification_partial_payment_paid_subject' => 'Neue Anzahlung von :client',
|
||||||
'notification_payment_paid' => 'A payment of :amount was made by client :client towards :invoice',
|
'notification_payment_paid' => 'A payment of :amount was made by client :client towards :invoice',
|
||||||
'notification_partial_payment_paid' => 'A partial payment of :amount was made by client :client towards :invoice',
|
'notification_partial_payment_paid' => 'A partial payment of :amount was made by client :client towards :invoice',
|
||||||
'notification_bot' => 'Benachrichtigungs-Bot',
|
'notification_bot' => 'Benachrichtigungs-Bot',
|
||||||
|
@ -4287,6 +4287,8 @@ $LANG = array(
|
|||||||
'company_deleted' => 'Company deleted',
|
'company_deleted' => 'Company deleted',
|
||||||
'company_deleted_body' => 'Company [ :company ] was deleted by :user',
|
'company_deleted_body' => 'Company [ :company ] was deleted by :user',
|
||||||
'back_to' => 'Back to :url',
|
'back_to' => 'Back to :url',
|
||||||
|
'stripe_connect_migration_title' => 'Connect your Stripe Account',
|
||||||
|
'stripe_connect_migration_desc' => 'Invoice Ninja v5 uses Stripe Connect to link your Stripe account to Invoice Ninja. This provides an additional layer of security for your account. Now that you data has migrated, you will need to Authorize Stripe to accept payments in v5.<br><br>To do this, navigate to Settings > Online Payments > Configure Gateways. Click on Stripe Connect and then under Settings click Setup Gateway. This will take you to Stripe to authorize Invoice Ninja and on your return your account will be successfully linked!',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $LANG;
|
return $LANG;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
@component('email.template.admin', ['logo' => $logo, 'settings' => $settings])
|
@component('email.template.admin', ['logo' => $logo, 'settings' => $settings])
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<h1>{{ ctrans('texts.max_companies') }}</h1>
|
<h1>{{ ctrans('texts.max_companies') }}</h1>
|
||||||
|
|
||||||
<p>{{ ctrans('texts.max_companies_desc') }}</p>
|
<p>{{ ctrans('texts.max_companies_desc') }}</p>
|
||||||
</div>
|
</div>
|
||||||
@endcomponent
|
@endcomponent
|
||||||
|
7
resources/views/email/migration/stripe_connect.blade.php
Normal file
7
resources/views/email/migration/stripe_connect.blade.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
@component('email.template.admin', ['logo' => $logo, 'settings' => $settings])
|
||||||
|
<div class="center">
|
||||||
|
<h1>{{ ctrans('texts.stripe_connect_migration_title') }}</h1>
|
||||||
|
|
||||||
|
<p>{{ ctrans('texts.stripe_connect_migration_desc') }}</p>
|
||||||
|
</div>
|
||||||
|
@endcomponent
|
@ -35,6 +35,16 @@
|
|||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
||||||
|
@if($this->showCopyBillingCheckbox())
|
||||||
|
@component('portal.ninja2020.components.general.card-element-single')
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button type="button" class="bg-gray-100 px-2 py-1 text-sm rounded" wire:click="handleCopyBilling">
|
||||||
|
{{ ctrans('texts.copy_billing') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
@endcomponent
|
||||||
|
@endif
|
||||||
|
|
||||||
@component('portal.ninja2020.components.general.card-element-single')
|
@component('portal.ninja2020.components.general.card-element-single')
|
||||||
<div class="flex flex-col items-end">
|
<div class="flex flex-col items-end">
|
||||||
<button class="button button-primary bg-primary">
|
<button class="button button-primary bg-primary">
|
||||||
|
@ -11,11 +11,23 @@
|
|||||||
<input type="hidden" name="source" value="{{ $token->token }}">
|
<input type="hidden" name="source" value="{{ $token->token }}">
|
||||||
|
|
||||||
@component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount_cents')])
|
@component('portal.ninja2020.components.general.card-element', ['title' => '#1 ' . ctrans('texts.amount_cents')])
|
||||||
<input type="text" name="transactions[]" class="w-full input" required dusk="verification-1st">
|
<input type="text" name="transactions[]" class="w-full input" required dusk="verification-1st" value="{{ old('transactions.0') }}">
|
||||||
|
|
||||||
|
@error('transactions.0')
|
||||||
|
<div class="validation validation-fail">
|
||||||
|
{{ $message }}
|
||||||
|
</div>
|
||||||
|
@enderror
|
||||||
@endcomponent
|
@endcomponent
|
||||||
|
|
||||||
@component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount_cents')])
|
@component('portal.ninja2020.components.general.card-element', ['title' => '#2 ' . ctrans('texts.amount_cents')])
|
||||||
<input type="text" name="transactions[]" class="w-full input" required dusk="verification-2nd">
|
<input type="text" name="transactions[]" class="w-full input" required dusk="verification-2nd" value="{{ old('transactions.1') }}">
|
||||||
|
|
||||||
|
@error('transactions.1')
|
||||||
|
<div class="validation validation-fail">
|
||||||
|
{{ $message }}
|
||||||
|
</div>
|
||||||
|
@enderror
|
||||||
@endcomponent
|
@endcomponent
|
||||||
|
|
||||||
@component('portal.ninja2020.gateways.includes.pay_now', ['type' => 'submit'])
|
@component('portal.ninja2020.gateways.includes.pay_now', ['type' => 'submit'])
|
||||||
|
@ -58,6 +58,16 @@
|
|||||||
.scrollIntoView({behavior: "smooth"});
|
.scrollIntoView({behavior: "smooth"});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Livewire.on('update-shipping-data', (event) => {
|
||||||
|
for (field in event) {
|
||||||
|
let element = document.querySelector(`input[name=${field}]`);
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
element.value = event[field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
let toggleWithToken = document.querySelector('.toggle-payment-with-token');
|
let toggleWithToken = document.querySelector('.toggle-payment-with-token');
|
||||||
|
|
||||||
|
@ -103,4 +103,31 @@ class ACHTest extends DuskTestCase
|
|||||||
->assertSee('Payment method has been successfully removed.');
|
->assertSee('Payment method has been successfully removed.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testIntegerAndMinimumValueOnVerification()
|
||||||
|
{
|
||||||
|
$this->browse(function (Browser $browser) {
|
||||||
|
$browser
|
||||||
|
->visitRoute('client.payment_methods.index')
|
||||||
|
->press('Add Payment Method')
|
||||||
|
->clickLink('Bank Account')
|
||||||
|
->type('#account-holder-name', 'John Doe')
|
||||||
|
->select('#country', 'US')
|
||||||
|
->select('#currency', 'USD')
|
||||||
|
->type('#routing-number', '110000000')
|
||||||
|
->type('#account-number', '000123456789')
|
||||||
|
->check('#accept-terms')
|
||||||
|
->press('Add Payment Method')
|
||||||
|
->waitForText('ACH (Verification)', 60)
|
||||||
|
->type('@verification-1st', '0.1')
|
||||||
|
->type('@verification-2nd', '0')
|
||||||
|
->press('Complete Verification')
|
||||||
|
->assertSee('The transactions.0 must be an integer')
|
||||||
|
->assertSee('The transactions.1 must be at least 1')
|
||||||
|
->type('@verification-1st', '32')
|
||||||
|
->type('@verification-2nd', '45')
|
||||||
|
->press('Complete Verification')
|
||||||
|
->assertSee('Bank Transfer');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
180
tests/Feature/Client/ClientMergeTest.php
Normal file
180
tests/Feature/Client/ClientMergeTest.php
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tests\Feature\Client;
|
||||||
|
|
||||||
|
use App\DataMapper\ClientSettings;
|
||||||
|
use App\DataMapper\CompanySettings;
|
||||||
|
use App\Http\Livewire\CreditsTable;
|
||||||
|
use App\Models\Account;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\ClientContact;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Credit;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Utils\Traits\AppSetup;
|
||||||
|
use Faker\Factory;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ClientMergeTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseTransactions;
|
||||||
|
use AppSetup;
|
||||||
|
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
private $company;
|
||||||
|
|
||||||
|
private $account;
|
||||||
|
|
||||||
|
public $client;
|
||||||
|
|
||||||
|
private $primary_contact;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->faker = Factory::create();
|
||||||
|
$this->buildCache(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSearchingForContacts()
|
||||||
|
{
|
||||||
|
|
||||||
|
$account = Account::factory()->create();
|
||||||
|
|
||||||
|
$this->user = User::factory()->create([
|
||||||
|
'account_id' => $account->id,
|
||||||
|
'email' => $this->faker->safeEmail
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->company = Company::factory()->create([
|
||||||
|
'account_id' => $account->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->client = Client::factory()->create([
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'company_id' => $this->company->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->primary_contact = ClientContact::factory()->create([
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'is_primary' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
ClientContact::factory()->count(2)->create([
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
ClientContact::factory()->create([
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'email' => 'search@gmail.com'
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$this->assertEquals(4, $this->client->contacts->count());
|
||||||
|
$this->assertTrue($this->client->contacts->contains(function ($contact) {
|
||||||
|
return $contact->email == 'search@gmail.com';
|
||||||
|
}));
|
||||||
|
|
||||||
|
$this->assertFalse($this->client->contacts->contains(function ($contact) {
|
||||||
|
return $contact->email == 'false@gmail.com';
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMergeClients()
|
||||||
|
{
|
||||||
|
|
||||||
|
$account = Account::factory()->create();
|
||||||
|
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'account_id' => $account->id,
|
||||||
|
'email' => $this->faker->safeEmail
|
||||||
|
]);
|
||||||
|
|
||||||
|
$company = Company::factory()->create([
|
||||||
|
'account_id' => $account->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$client = Client::factory()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'company_id' => $company->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$primary_contact = ClientContact::factory()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'client_id' => $client->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
'is_primary' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
ClientContact::factory()->count(2)->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'client_id' => $client->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
ClientContact::factory()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'client_id' => $client->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
'email' => 'search@gmail.com'
|
||||||
|
]);
|
||||||
|
//4contacts
|
||||||
|
|
||||||
|
$mergable_client = Client::factory()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'company_id' => $company->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$primary_contact = ClientContact::factory()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'client_id' => $mergable_client->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
'is_primary' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
ClientContact::factory()->count(2)->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'client_id' => $mergable_client->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
ClientContact::factory()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'client_id' => $mergable_client->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
'email' => 'search@gmail.com'
|
||||||
|
]);
|
||||||
|
//4 contacts
|
||||||
|
|
||||||
|
$this->assertEquals(4, $client->contacts->count());
|
||||||
|
$this->assertEquals(4, $mergable_client->contacts->count());
|
||||||
|
|
||||||
|
$client = $client->service()->merge($mergable_client)->save();
|
||||||
|
|
||||||
|
// nlog($client->contacts->fresh()->toArray());
|
||||||
|
// $this->assertEquals(7, $client->fresh()->contacts->count());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user