mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
commit
9400ac9f67
@ -1 +1 @@
|
||||
5.7.51
|
||||
5.7.52
|
@ -11,52 +11,53 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\ClientRegistrationFields;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\DataMapper\FeesAndLimits;
|
||||
use App\Events\Invoice\InvoiceWasCreated;
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasCreated;
|
||||
use App\Factory\GroupSettingFactory;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Factory\SubscriptionFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\BankTransactionRule;
|
||||
use stdClass;
|
||||
use Carbon\Carbon;
|
||||
use Faker\Factory;
|
||||
use App\Models\Task;
|
||||
use App\Models\User;
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Country;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Models\Country;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\VendorContact;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\DataMapper\FeesAndLimits;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Models\BankTransactionRule;
|
||||
use App\Factory\GroupSettingFactory;
|
||||
use App\Factory\SubscriptionFactory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use stdClass;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Events\Invoice\InvoiceWasCreated;
|
||||
use App\DataMapper\ClientRegistrationFields;
|
||||
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasCreated;
|
||||
|
||||
class CreateSingleAccount extends Command
|
||||
{
|
||||
@ -72,6 +73,7 @@ class CreateSingleAccount extends Command
|
||||
|
||||
protected $gateway;
|
||||
|
||||
public $faker;
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
@ -79,6 +81,8 @@ class CreateSingleAccount extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->faker = Factory::create();
|
||||
|
||||
if (Ninja::isHosted() || config('ninja.is_docker') || !$this->confirm('Are you sure you want to inject dummy data?')) {
|
||||
return;
|
||||
}
|
||||
@ -503,19 +507,61 @@ class CreateSingleAccount extends Command
|
||||
|
||||
private function createTask($client)
|
||||
{
|
||||
$vendor = Task::factory()->create([
|
||||
$time_log = $this->createTimeLog(rand(1,20));
|
||||
$status = TaskStatus::where('company_id', $client->company_id)->get()->random();
|
||||
|
||||
return Task::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
'time_log' => $time_log,
|
||||
'description' => $this->faker->paragraph,
|
||||
'status_id' => $status->id ?? null,
|
||||
'number' => rand(10000,100000000),
|
||||
'rate' => rand(1,150),
|
||||
'client_id' => $client->id
|
||||
]);
|
||||
}
|
||||
|
||||
private function createTimeLog(int $count)
|
||||
{
|
||||
$time_log = [];
|
||||
|
||||
$min = 0;
|
||||
|
||||
for ($x = 0; $x < $count; $x++) {
|
||||
|
||||
$rando = rand(300, 87000);
|
||||
|
||||
$time_log[] = [
|
||||
Carbon::now()->addSeconds($min)->timestamp,
|
||||
Carbon::now()->addSeconds($min += $rando)->timestamp,
|
||||
$this->faker->sentence,
|
||||
rand(0,1) === 0 ? false : true
|
||||
];
|
||||
|
||||
$min += 300;
|
||||
}
|
||||
|
||||
return json_encode($time_log);
|
||||
}
|
||||
|
||||
private function createProject($client)
|
||||
{
|
||||
$vendor = Project::factory()->create([
|
||||
$project = Project::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
'client_id' => $client->id,
|
||||
'due_date' => now()->addSeconds(rand(100000,1000000))->format('Y-m-d'),
|
||||
'budgeted_hours' => rand(100,1000),
|
||||
'task_rate' => rand(1,200),
|
||||
]);
|
||||
|
||||
for($x=0; $x < rand(2, 5); $x++) {
|
||||
$task = $this->createTask($client);
|
||||
$task->project_id = $project->id;
|
||||
$task->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function createInvoice($client)
|
||||
@ -559,6 +605,7 @@ class CreateSingleAccount extends Command
|
||||
$invoice->amount = 100; // Braintree sandbox only allows payments under 2,000 to complete successfully.
|
||||
}
|
||||
|
||||
/** @var \App\Models\Invoice $invoice */
|
||||
$invoice->save();
|
||||
$invoice->service()->createInvitations()->markSent();
|
||||
|
||||
@ -586,6 +633,7 @@ class CreateSingleAccount extends Command
|
||||
|
||||
$credit = $invoice_calc->getCredit();
|
||||
|
||||
/** @var \App\Models\Credit $credit */
|
||||
$credit->save();
|
||||
$credit->service()->markSent()->save();
|
||||
$credit->service()->createInvitations();
|
||||
@ -628,6 +676,7 @@ class CreateSingleAccount extends Command
|
||||
|
||||
$quote->save();
|
||||
|
||||
/** @var \App\Models\Quote $quote */
|
||||
$quote->service()->markSent()->save();
|
||||
$quote->service()->createInvitations();
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ class CompanySettings extends BaseSettings
|
||||
public $require_quote_signature = false; //@TODO ben to confirm
|
||||
|
||||
//email settings
|
||||
public $email_sending_method = 'default'; //enum 'default','gmail','office365' 'client_postmark', 'client_mailgun'//@implemented
|
||||
public $email_sending_method = 'default'; //enum 'default','gmail','office365' 'client_postmark', 'client_mailgun' //@implemented
|
||||
|
||||
public $gmail_sending_user_id = '0'; //@implemented
|
||||
|
||||
@ -491,7 +491,10 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $classification = ''; // individual, business, partnership, trust, charity, government, other
|
||||
|
||||
public $payment_email_all_contacts = false;
|
||||
|
||||
public static $casts = [
|
||||
'payment_email_all_contacts' => 'bool',
|
||||
'statement_design_id' => 'string',
|
||||
'delivery_note_design_id' => 'string',
|
||||
'payment_receipt_design_id' => 'string',
|
||||
|
@ -252,11 +252,9 @@ class InvoiceController extends Controller
|
||||
if ($invoices->count() == 1) {
|
||||
$invoice = $invoices->first();
|
||||
|
||||
$file = $invoice->service()->getInvoicePdf(auth()->guard('contact')->user());
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file), ['Content-Type' => 'application/pdf']);
|
||||
return response()->streamDownload(function () use ($invoice) {
|
||||
echo $invoice->service()->getInvoicePdf(auth()->guard('contact')->user());
|
||||
}, $invoice->getFileName(), ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
|
||||
return $this->buildZip($invoices);
|
||||
|
@ -526,11 +526,12 @@ class InvoiceController extends BaseController
|
||||
}
|
||||
|
||||
if ($action == 'download' && $invoices->count() >=1 && $user->can('view', $invoices->first())) {
|
||||
$file = $invoices->first()->service()->getInvoicePdf();
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file), ['Content-Type' => 'application/pdf']);
|
||||
|
||||
$filename = $invoices->first()->getFileName();
|
||||
|
||||
return response()->streamDownload(function () use($invoices) {
|
||||
echo $invoices->first()->service()->getInvoicePdf();
|
||||
}, $filename, ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
|
||||
if ($action == 'bulk_print' && $user->can('view', $invoices->first())) {
|
||||
@ -538,10 +539,10 @@ class InvoiceController extends BaseController
|
||||
return (new \App\Jobs\Entity\CreateRawPdf($invoice->invitations->first()))->handle();
|
||||
});
|
||||
|
||||
$merge = (new PdfMerge($paths->toArray()))->run();
|
||||
|
||||
|
||||
return response()->streamDownload(function () use ($merge) {
|
||||
echo($merge);
|
||||
return response()->streamDownload(function () use ($paths) {
|
||||
echo $merge = (new PdfMerge($paths->toArray()))->run();
|
||||
}, 'print.pdf', ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
|
||||
@ -700,11 +701,9 @@ class InvoiceController extends BaseController
|
||||
break;
|
||||
case 'download':
|
||||
|
||||
$file = $invoice->service()->getInvoicePdf();
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo Storage::get($file);
|
||||
}, basename($file), ['Content-Type' => 'application/pdf']);
|
||||
return response()->streamDownload(function () use ($invoice) {
|
||||
echo $invoice->service()->getInvoicePdf();
|
||||
}, $invoice->getFileName(), ['Content-Type' => 'application/pdf']);
|
||||
|
||||
case 'restore':
|
||||
$this->invoice_repo->restore($invoice);
|
||||
@ -936,15 +935,11 @@ class InvoiceController extends BaseController
|
||||
*/
|
||||
public function deliveryNote(ShowInvoiceRequest $request, Invoice $invoice)
|
||||
{
|
||||
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||
|
||||
return response()->streamDownload(function () use ($invoice) {
|
||||
echo $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||
}, $invoice->getDeliveryNoteName(), ['Content-Type' => 'application/pdf']);
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo $file;
|
||||
}, basename($file), ['Content-Type' => 'application/pdf']);
|
||||
|
||||
// return response()->streamDownload(function () use ($file) {
|
||||
// echo Storage::get($file);
|
||||
// }, basename($file), ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,9 +137,9 @@ class PreviewController extends BaseController
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
if(request()->has('template')) {
|
||||
// if(request()->has('template')) {
|
||||
return $this->template();
|
||||
}
|
||||
// }
|
||||
|
||||
if (request()->has('entity') &&
|
||||
request()->has('entity_id') &&
|
||||
|
@ -720,11 +720,9 @@ class QuoteController extends BaseController
|
||||
break;
|
||||
case 'download':
|
||||
|
||||
$file = $quote->service()->getQuotePdf();
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo $file;
|
||||
}, $quote->numberFormatter().".pdf", ['Content-Type' => 'application/pdf']);
|
||||
return response()->streamDownload(function () use ($quote) {
|
||||
echo $quote->service()->getQuotePdf();
|
||||
}, $quote->getFileName(), ['Content-Type' => 'application/pdf']);
|
||||
|
||||
case 'restore':
|
||||
$this->quote_repo->restore($quote);
|
||||
@ -833,11 +831,9 @@ class QuoteController extends BaseController
|
||||
$headers = array_merge($headers, ['Content-Disposition' => 'inline']);
|
||||
}
|
||||
|
||||
$file = $quote->service()->getQuotePdf($contact);
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
echo $file;
|
||||
}, $quote->numberFormatter().".pdf", $headers);
|
||||
return response()->streamDownload(function () use ($quote,$contact) {
|
||||
echo $quote->service()->getQuotePdf($contact);
|
||||
}, $quote->getFileName(), $headers);
|
||||
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@ class SearchController extends Controller
|
||||
'integrations,api_tokens' => '/settings/integrations/api_tokens',
|
||||
'integrations,api_webhooks' => '/settings/integrations/api_webhooks',
|
||||
'integrations,analytics' => '/settings/integrations/analytics',
|
||||
'gateways' => '/settings/gateways',
|
||||
'gateways' => '/settings/online_payments',
|
||||
'gateways,create' => '/settings/gateways/create',
|
||||
'bank_accounts,transaction_rules' => '/settings/bank_accounts/transaction_rules',
|
||||
'bank_accounts,transaction_rules/create' => '/settings/bank_accounts/transaction_rules/create',
|
||||
|
@ -1152,7 +1152,7 @@ class CompanyImport implements ShouldQueue
|
||||
$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->name = str_replace("/", "-", $document->name);
|
||||
$new_document->type = $document->type;
|
||||
$new_document->disk = $document->disk;
|
||||
$new_document->hash = $document->hash;
|
||||
@ -1199,7 +1199,7 @@ class CompanyImport implements ShouldQueue
|
||||
{
|
||||
$this->genericImport(
|
||||
Webhook::class,
|
||||
['company_id', 'user_id'],
|
||||
['company_id', 'user_id', 'hashed_id', 'id',],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
],
|
||||
|
@ -117,44 +117,46 @@ class SystemMaintenance implements ShouldQueue
|
||||
});
|
||||
}
|
||||
|
||||
private function cleanPdfs()
|
||||
{
|
||||
$company_keys = Company::query()
|
||||
->pluck('company_key')
|
||||
->toArray();
|
||||
//double check this is correct.
|
||||
|
||||
// private function cleanPdfs()
|
||||
// {
|
||||
// $company_keys = Company::query()
|
||||
// ->pluck('company_key')
|
||||
// ->toArray();
|
||||
|
||||
$directories = Storage::disk(config('filesystems.default'))->directories();
|
||||
// $directories = Storage::disk(config('filesystems.default'))->directories();
|
||||
|
||||
$del_dirs = ['quotes','invoices','credits','recurring_invoices', 'e_invoice'];
|
||||
// $del_dirs = ['quotes','invoices','credits','recurring_invoices', 'e_invoice'];
|
||||
|
||||
collect($directories)->each(function ($parent_directory) use ($del_dirs, $company_keys) {
|
||||
// collect($directories)->each(function ($parent_directory) use ($del_dirs, $company_keys) {
|
||||
|
||||
if (! in_array($parent_directory, $company_keys)) {
|
||||
nlog("Deleting {$parent_directory}");
|
||||
// if (! in_array($parent_directory, $company_keys)) {
|
||||
// nlog("Deleting {$parent_directory}");
|
||||
|
||||
/* Ensure we are not deleting the root folder */
|
||||
if (strlen($parent_directory) > 1) {
|
||||
nlog("Company No Longer Exists => deleting {$parent_directory}");
|
||||
Storage::disk(config('filesystems.default'))->deleteDirectory($parent_directory);
|
||||
return;
|
||||
}
|
||||
// /* Ensure we are not deleting the root folder */
|
||||
// if (strlen($parent_directory) > 1) {
|
||||
// nlog("Company No Longer Exists => deleting {$parent_directory}");
|
||||
// Storage::disk(config('filesystems.default'))->deleteDirectory($parent_directory);
|
||||
// return;
|
||||
// }
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
$sub_directories = Storage::allDirectories($parent_directory);
|
||||
// $sub_directories = Storage::allDirectories($parent_directory);
|
||||
|
||||
collect($sub_directories)->each(function ($sub_dir) use ($del_dirs) {
|
||||
foreach($del_dirs as $del_dir) {
|
||||
if(stripos($sub_dir, $del_dir) !== false) {
|
||||
nlog("Deleting {$sub_dir} as it matches {$del_dir}");
|
||||
Storage::deleteDirectory($sub_dir);
|
||||
}
|
||||
}
|
||||
// collect($sub_directories)->each(function ($sub_dir) use ($del_dirs) {
|
||||
// foreach($del_dirs as $del_dir) {
|
||||
// if(stripos($sub_dir, $del_dir) !== false) {
|
||||
// nlog("Deleting {$sub_dir} as it matches {$del_dir}");
|
||||
// Storage::deleteDirectory($sub_dir);
|
||||
// }
|
||||
// }
|
||||
|
||||
});
|
||||
// });
|
||||
|
||||
});
|
||||
// });
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
}
|
@ -72,6 +72,10 @@ class EmailPayment implements ShouldQueue
|
||||
|
||||
$email_builder = (new PaymentEmailEngine($this->payment, $this->contact))->build();
|
||||
|
||||
if($this->settings->payment_email_all_contacts && $this->payment->invoices && $this->payment->invoices->count() >= 1) {
|
||||
$this->emailAllContacts($email_builder);
|
||||
}
|
||||
|
||||
$invitation = null;
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
@ -100,4 +104,25 @@ class EmailPayment implements ShouldQueue
|
||||
event(new PaymentWasEmailed($this->payment, $this->payment->company, $this->contact, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
}
|
||||
}
|
||||
|
||||
private function emailAllContacts($email_builder): void
|
||||
{
|
||||
|
||||
$invoice = $this->payment->invoices->first();
|
||||
|
||||
$invoice->invitations->each(function ($invite) use ($email_builder){
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new TemplateEmail($email_builder, $invite->contact, $invite);
|
||||
$nmo->to_user = $invite->contact;
|
||||
$nmo->settings = $this->settings;
|
||||
$nmo->company = $this->company;
|
||||
$nmo->entity = $this->payment;
|
||||
(new NinjaMailerJob($nmo))->handle();
|
||||
|
||||
event(new PaymentWasEmailed($this->payment, $this->payment->company, $invite->contact, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ class UploadFile implements ShouldQueue
|
||||
$document->user_id = $this->user->id;
|
||||
$document->company_id = $this->company->id;
|
||||
$document->url = $instance;
|
||||
$document->name = $this->file->getClientOriginalName();
|
||||
$document->name = str_replace("/", "-", $this->file->getClientOriginalName());
|
||||
$document->type = $this->file->extension();
|
||||
$document->disk = $this->disk;
|
||||
$document->hash = $this->file->hashName();
|
||||
|
@ -236,6 +236,21 @@ class BaseModel extends Model
|
||||
return $this->numberFormatter().'.'.$extension;
|
||||
}
|
||||
|
||||
public function getDeliveryNoteName($extension = 'pdf')
|
||||
{
|
||||
|
||||
$number = ctrans("texts.delivery_note"). "_" . $this->numberFormatter().'.'.$extension;
|
||||
|
||||
$formatted_number = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $number);
|
||||
|
||||
$formatted_number = mb_ereg_replace("([\.]{2,})", '', $formatted_number);
|
||||
|
||||
$formatted_number = preg_replace('/\s+/', '_', $formatted_number);
|
||||
|
||||
return \Illuminate\Support\Str::ascii($formatted_number);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extension
|
||||
* @return string
|
||||
|
@ -937,7 +937,6 @@ class Company extends BaseModel
|
||||
$date = new \DateTime("now", new \DateTimeZone($timezone->name));
|
||||
$offset -= $date->getOffset();
|
||||
|
||||
// $offset -= $timezone->utc_offset;
|
||||
$offset += ($entity_send_time * 3600);
|
||||
|
||||
return $offset;
|
||||
|
@ -162,6 +162,18 @@ class Task extends BaseModel
|
||||
return $this->belongsTo(TaskStatus::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function stringStatus()
|
||||
{
|
||||
if($this->invoice_id)
|
||||
return '<h5><span class="badge badge-success">'.ctrans('texts.invoiced').'</span></h5>';
|
||||
|
||||
if($this->status)
|
||||
return '<h5><span class="badge badge-primary">' . $this->status?->name ?? '';
|
||||
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
public function invoice()
|
||||
{
|
||||
return $this->belongsTo(Invoice::class)->withTrashed();
|
||||
@ -242,20 +254,58 @@ class Task extends BaseModel
|
||||
|
||||
public function processLogs()
|
||||
{
|
||||
|
||||
return
|
||||
collect($this->time_log)->map(function ($log){
|
||||
collect(json_decode($this->time_log,true))->map(function ($log){
|
||||
|
||||
$parent_entity = $this->client ?? $this->company;
|
||||
|
||||
if($log[0])
|
||||
$log[0] = Carbon::createFromTimestamp($log[0])->format($parent_entity->date_format());
|
||||
$log[0] = Carbon::createFromTimestamp($log[0])->format($parent_entity->date_format().' H:m:s');
|
||||
|
||||
if($log[1] && $log[1] != 0)
|
||||
$log[1] = Carbon::createFromTimestamp($log[1])->format($parent_entity->date_format());
|
||||
$log[1] = Carbon::createFromTimestamp($log[1])->format($parent_entity->date_format().' H:m:s');
|
||||
else
|
||||
$log[1] = ctrans('texts.running');
|
||||
|
||||
return $log;
|
||||
})->toArray();
|
||||
}
|
||||
|
||||
|
||||
public function processLogsExpandedNotation()
|
||||
{
|
||||
|
||||
return
|
||||
collect(json_decode($this->time_log,true))->map(function ($log){
|
||||
|
||||
$parent_entity = $this->client ?? $this->company;
|
||||
$logged = [];
|
||||
|
||||
if($log[0] && $log[1] !=0 ) {
|
||||
$duration = $log[1] - $log[0];
|
||||
}
|
||||
else {
|
||||
$duration = 0;
|
||||
}
|
||||
|
||||
if($log[0])
|
||||
$logged['start_date_raw'] = $log[0];
|
||||
$logged['start_date'] = Carbon::createFromTimestamp($log[0])->format($parent_entity->date_format().' H:m:s');
|
||||
|
||||
if($log[1] && $log[1] != 0) {
|
||||
$logged['end_date_raw'] = $log[1];
|
||||
$logged['end_date'] = Carbon::createFromTimestamp($log[1])->format($parent_entity->date_format().' H:m:s');
|
||||
}
|
||||
else{
|
||||
$logged['end_date_raw'] = 0;
|
||||
$logged['end_date'] = ctrans('texts.running');
|
||||
}
|
||||
$logged['duration'] = $duration;
|
||||
|
||||
return $logged;
|
||||
|
||||
})->toArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -761,6 +761,7 @@ $company_name = substr($company_name, 0, $length);
|
||||
$descriptor = "{$company_name} {$invoices_string}";
|
||||
|
||||
$invoices_string = str_pad($descriptor, 5, ctrans('texts.invoice'), STR_PAD_RIGHT);
|
||||
$invoices_string = substr($invoices_string, 0, 22);
|
||||
|
||||
// $invoices_string = str_pad($invoices_string, 5, ctrans('texts.invoice'), STR_PAD_LEFT);
|
||||
|
||||
|
@ -71,6 +71,7 @@ class CreditCard
|
||||
'gateway_type_id' => GatewayType::CREDIT_CARD,
|
||||
],
|
||||
'setup_future_usage' => 'off_session',
|
||||
'payment_method_types' => ['card'],
|
||||
];
|
||||
|
||||
$data['intent'] = $this->stripe->createPaymentIntent($payment_intent_data);
|
||||
|
@ -125,6 +125,7 @@ class StripePaymentDriver extends BaseDriver
|
||||
|
||||
Stripe::setApiKey($this->company_gateway->getConfigField('apiKey'));
|
||||
Stripe::setApiVersion('2022-11-15');
|
||||
// Stripe::setAPiVersion('2023-08-16');
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -128,7 +128,6 @@ class PaymentRepository extends BaseRepository
|
||||
|
||||
/*Ensure payment number generated*/
|
||||
if (! $payment->number || strlen($payment->number) == 0) {
|
||||
// $payment->number = $payment->client->getNextPaymentNumber($payment->client, $payment);
|
||||
$payment->service()->applyNumber();
|
||||
}
|
||||
|
||||
|
@ -192,9 +192,9 @@ class InstantPayment
|
||||
$starting_invoice_amount = $first_invoice->balance;
|
||||
|
||||
/* Schedule a job to check the gateway fees for this invoice*/
|
||||
if (Ninja::isHosted()) {
|
||||
CheckGatewayFee::dispatch($first_invoice->id, $client->company->db)->delay(800);
|
||||
}
|
||||
// if (Ninja::isHosted()) {
|
||||
// CheckGatewayFee::dispatch($first_invoice->id, $client->company->db)->delay(800);
|
||||
// }
|
||||
|
||||
if ($gateway) {
|
||||
$first_invoice->service()->addGatewayFee($gateway, $payment_method_id, $invoice_totals)->save();
|
||||
|
@ -713,7 +713,7 @@ class PdfBuilder
|
||||
|
||||
$data[$key][$table_type.'.cost'] = $this->service->config->formatMoney($item->cost);
|
||||
|
||||
$data[$key][$table_type.'.line_total'] = $this->service->config->formatMoney($item->line_total);
|
||||
$data[$key][$table_type.'.line_total'] = $this->service->config->formatMoneyNoRounding($item->line_total);
|
||||
} else {
|
||||
$data[$key][$table_type.'.quantity'] = '';
|
||||
|
||||
|
@ -62,7 +62,7 @@ class TemplateAction implements ShouldQueue
|
||||
private string $template,
|
||||
private string $entity,
|
||||
private int $user_id,
|
||||
private Company $company,
|
||||
public Company $company,
|
||||
private string $db,
|
||||
private string $hash,
|
||||
private bool $send_email = false
|
||||
@ -109,7 +109,10 @@ class TemplateAction implements ShouldQueue
|
||||
$data[$key] = $result;
|
||||
}
|
||||
|
||||
$ts = $template_service->build($data);
|
||||
|
||||
$ts = $template_service
|
||||
->setCompany($this->company)
|
||||
->build($data);
|
||||
|
||||
// nlog($ts->getHtml());
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -214,7 +214,7 @@ class TemplateService
|
||||
{
|
||||
|
||||
$this->data = $this->preProcessDataBlocks($data);
|
||||
|
||||
// nlog($this->data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -596,7 +596,7 @@ class TemplateService
|
||||
'balance_raw' => ($payment->amount - $payment->refunded - $payment->applied),
|
||||
'date' => $this->translateDate($payment->date, $payment->client->date_format(), $payment->client->locale()),
|
||||
'method' => $payment->translatedType(),
|
||||
'currency' => $payment->currency->code,
|
||||
'currency' => $payment->currency->code ?? $this->company->currency()->code,
|
||||
'exchange_rate' => $payment->exchange_rate,
|
||||
'transaction_reference' => $payment->transaction_reference,
|
||||
'is_manual' => $payment->is_manual,
|
||||
@ -838,25 +838,26 @@ class TemplateService
|
||||
/**
|
||||
* @todo refactor
|
||||
*
|
||||
* @param mixed $tasks
|
||||
* @param \App\Models\Task[] $tasks
|
||||
* @return array
|
||||
*/
|
||||
public function processTasks($tasks, bool $nested = false): array
|
||||
{
|
||||
|
||||
return collect($tasks)->map(function ($task) use ($nested) {
|
||||
|
||||
/** @var \App\Models\Task $task */
|
||||
return [
|
||||
'number' => (string) $task->number ?: '',
|
||||
'description' => (string) $task->description ?: '',
|
||||
'duration' => $task->duration ?: 0,
|
||||
'rate' => Number::formatMoney($task->rate ?? 0, $task->client ?? $task->company),
|
||||
'rate_raw' => $task->rate ?? 0,
|
||||
'created_at' => $this->translateDate($task->created_at, $task->client ? $task->client->date_format() : $task->company->date_format(), $task->client ? $task->client->locale() : $task->company->locale()),
|
||||
'updated_at' => $this->translateDate($task->updated_at, $task->client ? $task->client->date_format() : $task->company->date_format(), $task->client ? $task->client->locale() : $task->company->locale()),
|
||||
'date' => $task->calculated_start_date ? $this->translateDate($task->calculated_start_date, $task->client ? $task->client->date_format() : $task->company->date_format(), $task->client ? $task->client->locale() : $task->company->locale()) : '',
|
||||
// 'invoice_id' => $this->encodePrimaryKey($task->invoice_id) ?: '',
|
||||
'project' => ($task->project && !$nested) ? $this->transformProject($task->project, true) : [],
|
||||
'time_log' => $task->processLogs(),
|
||||
'time_log' => $task->processLogsExpandedNotation(),
|
||||
'custom_value1' => $task->custom_value1 ?: '',
|
||||
'custom_value2' => $task->custom_value2 ?: '',
|
||||
'custom_value3' => $task->custom_value3 ?: '',
|
||||
@ -902,6 +903,7 @@ class TemplateService
|
||||
'created_at' => $this->translateDate($project->created_at, $project->client->date_format(), $project->client->locale()),
|
||||
'updated_at' => $this->translateDate($project->updated_at, $project->client->date_format(), $project->client->locale()),
|
||||
'task_rate' => Number::formatMoney($project->task_rate ?? 0, $project->client),
|
||||
'task_rate_raw' => $project->task_rate ?? 0,
|
||||
'due_date' => $project->due_date ? $this->translateDate($project->due_date, $project->client->date_format(), $project->client->locale()) : '',
|
||||
'private_notes' => (string) $project->private_notes ?: '',
|
||||
'public_notes' => (string) $project->public_notes ?: '',
|
||||
|
@ -15,8 +15,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => env('APP_VERSION','5.7.51'),
|
||||
'app_tag' => env('APP_TAG','5.7.51'),
|
||||
'app_version' => env('APP_VERSION','5.7.52'),
|
||||
'app_tag' => env('APP_TAG','5.7.52'),
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
|
@ -47,7 +47,7 @@
|
||||
{{ $task->project?->name }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500">
|
||||
{{ $task->status?->name }}
|
||||
{!! $task->stringStatus() !!}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm leading-5 text-gray-500">
|
||||
{{ \Carbon\CarbonInterval::seconds($task->calcDuration())->cascade()->forHumans() }}
|
||||
|
@ -21,7 +21,7 @@ use Tests\TestCase;
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Http\Controllers\ActivityController
|
||||
*/
|
||||
*/
|
||||
class DownloadHistoricalInvoiceTest extends TestCase
|
||||
{
|
||||
use MockAccountData;
|
||||
@ -39,6 +39,78 @@ class DownloadHistoricalInvoiceTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testDownloadInvoiceRoute()
|
||||
{
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get("/api/v1/invoices/{$this->invoice->hashed_id}/download");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertDownload();
|
||||
|
||||
}
|
||||
|
||||
public function testDownloadDeliveryRoute()
|
||||
{
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get("/api/v1/invoices/{$this->invoice->hashed_id}/delivery_note");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertDownload();
|
||||
|
||||
}
|
||||
|
||||
public function testDownloadInvoiceBulkActionRoute()
|
||||
{
|
||||
$data = [
|
||||
'action' => 'download',
|
||||
'ids' => [$this->invoice->hashed_id],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post("/api/v1/invoices/bulk", $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertDownload();
|
||||
|
||||
}
|
||||
|
||||
public function testDownloadQuoteRoute()
|
||||
{
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get("/api/v1/quotes/{$this->quote->hashed_id}/download");
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertDownload();
|
||||
|
||||
}
|
||||
|
||||
public function testDownloadQuoteBulkActionRoute()
|
||||
{
|
||||
$data = [
|
||||
'action' => 'download',
|
||||
'ids' => [$this->quote->hashed_id],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post("/api/v1/quotes/bulk", $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
}
|
||||
|
||||
private function mockActivity()
|
||||
{
|
||||
$activity_repo = new ActivityRepository();
|
||||
|
Loading…
x
Reference in New Issue
Block a user