Merge pull request #8973 from turbo124/v5-develop

v5.7.49
This commit is contained in:
David Bomba 2023-11-15 08:57:48 +11:00 committed by GitHub
commit fec38eac8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 636 additions and 12 deletions

View File

@ -1 +1 @@
5.7.48
5.7.49

View File

@ -0,0 +1,66 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Import\Definitions;
class TaskMap
{
public static function importable()
{
return [
0 => 'task.number',
1 => 'task.user_id',
2 => 'task.rate',
3 => 'project.name',
4 => 'client.name',
5 => 'client.email',
6 => 'task.description',
7 => 'task.is_billable',
8 => 'task.start_date',
9 => 'task.end_date',
10 => 'task.start_time',
11 => 'task.end_time',
12 => 'task.duration',
13 => 'task.status',
14 => 'task.custom_value1',
15 => 'task.custom_value1',
16 => 'task.custom_value1',
17 => 'task.custom_value1',
18 => 'task.notes',
];
}
public static function import_keys()
{
return [
0 => 'texts.task_number',
1 => 'texts.user',
2 => 'texts.task_rate',
3 => 'texts.project',
4 => 'texts.client',
5 => 'texts.client_email',
6 => 'texts.description',
7 => 'texts.billable',
8 => 'texts.start_date',
9 => 'texts.end_date',
10 => 'texts.start_time',
11 => 'texts.end_time',
12 => 'texts.duration',
13 => 'texts.status',
14 => 'texts.task1',
15 => 'texts.task2',
16 => 'texts.task3',
17 => 'texts.task4',
18 => 'texts.notes',
];
}
}

View File

@ -16,6 +16,7 @@ use App\Factory\InvoiceFactory;
use App\Factory\PaymentFactory;
use App\Factory\QuoteFactory;
use App\Factory\RecurringInvoiceFactory;
use App\Factory\TaskFactory;
use App\Http\Requests\Quote\StoreQuoteRequest;
use App\Import\ImportException;
use App\Jobs\Mail\NinjaMailerJob;
@ -30,6 +31,7 @@ use App\Repositories\InvoiceRepository;
use App\Repositories\PaymentRepository;
use App\Repositories\QuoteRepository;
use App\Repositories\RecurringInvoiceRepository;
use App\Repositories\TaskRepository;
use App\Utils\Traits\CleanLineItems;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
@ -158,6 +160,32 @@ class BaseImport
}, $csvData);
}
private function groupTasks($csvData, $key)
{
if (! $key || count(array_column($csvData, $key)) == 0) {
return $csvData;
}
// Group by tasks.
$grouped = [];
foreach ($csvData as $item) {
if (empty($item[$key])) {
$this->error_array['task'][] = [
'task' => $item,
'error' => 'No task number',
];
} else {
$grouped[$item[$key]][] = $item;
}
}
return $grouped;
}
private function groupInvoices($csvData, $key)
{
if (! $key) {
@ -413,6 +441,65 @@ class BaseImport
return $count;
}
public function ingestTasks($tasks, $task_number_key)
{
$count = 0;
$task_transformer = $this->transformer;
$task_repository = new TaskRepository();
$tasks = $this->groupTasks($tasks, $task_number_key);
foreach ($tasks as $raw_task) {
$task_data = [];
try {
$task_data = $task_transformer->transform($raw_task);
$task_data['user_id'] = $this->company->owner()->id;
$validator = $this->request_name::runFormRequest($task_data);
if ($validator->fails()) {
$this->error_array['task'][] = [
'invoice' => $task_data,
'error' => $validator->errors()->all(),
];
} else {
$task = TaskFactory::create(
$this->company->id,
$this->company->owner()->id
);
$task_repository->save($task_data, $task);
$count++;
}
} catch (\Exception $ex) {
if (\DB::connection(config('database.default'))->transactionLevel() > 0) {
\DB::connection(config('database.default'))->rollBack();
}
if ($ex instanceof ImportException) {
$message = $ex->getMessage();
} else {
report($ex);
$message = 'Unknown error ';
nlog($ex->getMessage());
nlog($task_data);
}
$this->error_array['task'][] = [
'task' => $task_data,
'error' => $message,
];
}
}
nlog($count);
return $count;
}
public function ingestInvoices($invoices, $invoice_number_key)
{

View File

@ -19,6 +19,7 @@ use App\Factory\PaymentFactory;
use App\Factory\ProductFactory;
use App\Factory\QuoteFactory;
use App\Factory\RecurringInvoiceFactory;
use App\Factory\TaskFactory;
use App\Factory\VendorFactory;
use App\Http\Requests\BankTransaction\StoreBankTransactionRequest;
use App\Http\Requests\Client\StoreClientRequest;
@ -28,6 +29,7 @@ use App\Http\Requests\Payment\StorePaymentRequest;
use App\Http\Requests\Product\StoreProductRequest;
use App\Http\Requests\Quote\StoreQuoteRequest;
use App\Http\Requests\RecurringInvoice\StoreRecurringInvoiceRequest;
use App\Http\Requests\Task\StoreTaskRequest;
use App\Http\Requests\Vendor\StoreVendorRequest;
use App\Import\Transformer\Bank\BankTransformer;
use App\Import\Transformer\Csv\ClientTransformer;
@ -37,6 +39,7 @@ use App\Import\Transformer\Csv\PaymentTransformer;
use App\Import\Transformer\Csv\ProductTransformer;
use App\Import\Transformer\Csv\QuoteTransformer;
use App\Import\Transformer\Csv\RecurringInvoiceTransformer;
use App\Import\Transformer\Csv\TaskTransformer;
use App\Import\Transformer\Csv\VendorTransformer;
use App\Repositories\BankTransactionRepository;
use App\Repositories\ClientRepository;
@ -46,6 +49,7 @@ use App\Repositories\PaymentRepository;
use App\Repositories\ProductRepository;
use App\Repositories\QuoteRepository;
use App\Repositories\RecurringInvoiceRepository;
use App\Repositories\TaskRepository;
use App\Repositories\VendorRepository;
use App\Services\Bank\BankMatchingService;
use App\Utils\Traits\MakesHash;
@ -69,6 +73,7 @@ class Csv extends BaseImport implements ImportInterface
'quote',
'bank_transaction',
'recurring_invoice',
'task',
])
) {
$this->{$entity}();
@ -348,6 +353,33 @@ class Csv extends BaseImport implements ImportInterface
public function task()
{
$entity_type = 'task';
$data = $this->getCsvData($entity_type);
if (is_array($data)) {
$data = $this->preTransformCsv($data, $entity_type);
}
if (empty($data)) {
$this->entity_count['tasks'] = 0;
return;
}
$this->request_name = StoreTaskRequest::class;
$this->repository_name = TaskRepository::class;
$this->factory_name = TaskFactory::class;
$this->repository = app()->make($this->repository_name);
// $this->repository->import_mode = true;
$this->transformer = new TaskTransformer($this->company);
$task_count = $this->ingestTasks($data, 'task.number');
$this->entity_count['tasks'] = $task_count;
}
public function transform(array $data)

View File

@ -676,6 +676,9 @@ class BaseTransformer
*/
public function getProjectId($name, $clientId = null)
{
if(strlen($name) == 0)
return null;
$project = Project::query()->where('company_id', $this->company->id)
->where('is_deleted', false)
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [

View File

@ -0,0 +1,170 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Import\Transformer\Csv;
use App\Models\TaskStatus;
use App\Import\Transformer\BaseTransformer;
/**
* Class TaskTransformer.
*/
class TaskTransformer extends BaseTransformer
{
private int $stubbed_timestamp = 0;
/**
* @param $data
*
* @return bool|array
*/
public function transform($task_items_data)
{
$this->stubbed_timestamp = time();
$task_data = reset($task_items_data);
$clientId = $this->getClient(
$this->getString($task_data, 'client.name'),
$this->getString($task_data, 'client.email')
);
$projectId = $task_data['project.name'] ?? '';
$transformed = [
'company_id' => $this->company->id,
'number' => $this->getString($task_data, 'task.number'),
'user_id' => $this->getString($task_data, 'task.user_id'),
'client_id' => $clientId,
'project_id' => $this->getProjectId($projectId, $clientId),
'description' => $this->getString($task_data, 'task.description'),
'status' => $this->getTaskStatusId($task_data),
'custom_value1' => $this->getString($task_data, 'task.custom_value1'),
'custom_value2' => $this->getString($task_data, 'task.custom_value2'),
'custom_value3' => $this->getString($task_data, 'task.custom_value3'),
'custom_value4' => $this->getString($task_data, 'task.custom_value4'),
];
if(count($task_items_data) == count($task_items_data, COUNT_RECURSIVE)) {
$transformed['time_log'] = json_encode([$this->parseLog($task_items_data)]);
return $transformed;
}
$time_log = collect($task_items_data)
->map(function ($item) {
return $this->parseLog($item);
})->toJson();
$transformed['time_log'] = $time_log;
return $transformed;
}
private function parseLog($item)
{
$start_date = false;
$end_date = false;
$notes = $item['task.notes'] ?? '';
if(isset($item['task.is_billable']) && is_string($item['task.is_billable']) && in_array($item['task.is_billable'], ['yes', 'true', '1']))
$is_billable = true;
elseif(isset($item['task.is_billable']) && is_bool($item['task.is_billable']))
$is_billable = $item['task.is_billable'];
else
$is_billable = false;
if(isset($item['task.start_date']) &&
isset($item['task.end_date'])) {
$start_date = $this->resolveStartDate($item);
$end_date = $this->resolveEndDate($item);
} elseif(isset($item['task.duration'])) {
$duration = strtotime($item['task.duration']) - strtotime('TODAY');
$start_date = $this->stubbed_timestamp;
$end_date = $this->stubbed_timestamp + $duration;
$this->stubbed_timestamp;
} else {
return '';
}
return [$start_date, $end_date, $notes, $is_billable];
}
private function resolveStartDate($item)
{
$stub_start_date = $item['task.start_date'];
$stub_start_date .= isset($item['task.start_time']) ? " ".$item['task.start_time'] : '';
try {
$stub_start_date = \Carbon\Carbon::parse($stub_start_date);
$this->stubbed_timestamp = $stub_start_date->timestamp;
return $stub_start_date->timestamp;
} catch (\Exception $e) {
nlog($e->getMessage());
return $this->stubbed_timestamp;
}
}
private function resolveEndDate($item)
{
$stub_end_date = $item['task.end_date'];
$stub_end_date .= isset($item['task.end_time']) ? " ".$item['task.end_time'] : '';
try {
$stub_end_date = \Carbon\Carbon::parse($stub_end_date);
if($stub_end_date->timestamp == $this->stubbed_timestamp) {
$this->stubbed_timestamp;
return $this->stubbed_timestamp;
}
$this->stubbed_timestamp = $stub_end_date->timestamp;
return $stub_end_date->timestamp;
} catch (\Exception $e) {
nlog($e->getMessage());
return $this->stubbed_timestamp;
}
}
private function getTaskStatusId($item): ?int
{
if(isset($item['task.status']))
{
$name = strtolower(trim($item['task.status']));
$ts = TaskStatus::query()->where('company_id', $this->company->id)
->where('is_deleted', false)
->whereRaw("LOWER(REPLACE(`name`, ' ' ,'')) = ?", [
strtolower(str_replace(' ', '', $name)),
])
->first();
if($ts)
return $ts->id;
}
return TaskStatus::where('company_id', $this->company->id)
->where('is_deleted', false)
->orderBy('status_order', 'asc')
->first()->id ?? null;
}
}

View File

@ -100,13 +100,13 @@ class Statement
], \App\Services\PdfMaker\Design::STATEMENT),
'variables' => $variables,
'options' => [
'client' => $this->client,
'entity' => $this->entity,
'variables' => $variables,
'invoices' => $this->getInvoices()->cursor(),
'payments' => $this->getPayments()->cursor(),
'credits' => $this->getCredits()->cursor(),
'aging' => $this->getAging(),
// 'client' => $this->client,
// 'entity' => $this->entity,
// 'variables' => $variables,
// 'invoices' => $this->getInvoices()->cursor(),
// 'payments' => $this->getPayments()->cursor(),
// 'credits' => $this->getCredits()->cursor(),
// 'aging' => $this->getAging(),
],
'process_markdown' => $this->entity->client->company->markdown_enabled,
];

View File

@ -844,10 +844,12 @@ class PdfBuilder
public function processTaxColumns(string $type): void
{
if ($type == 'product') {
$column_type = 'product';
$type_id = 1;
}
if ($type == 'task') {
$column_type = 'task';
$type_id = 2;
}
@ -886,10 +888,10 @@ class PdfBuilder
array_push($taxes, sprintf('%s%s.tax_rate3', '$', $type));
}
$key = array_search(sprintf('%s%s.tax', '$', $type), $this->service->config->pdf_variables["{$type}_columns"], true);
$key = array_search(sprintf('%s%s.tax', '$', $type), $this->service->config->pdf_variables["{$column_type}_columns"], true);
if ($key !== false) {
array_splice($this->service->config->pdf_variables["{$type}_columns"], $key, 1, $taxes);
array_splice($this->service->config->pdf_variables["{$column_type}_columns"], $key, 1, $taxes);
}
}
}

View File

@ -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.48'),
'app_tag' => env('APP_TAG','5.7.48'),
'app_version' => env('APP_VERSION','5.7.49'),
'app_tag' => env('APP_TAG','5.7.49'),
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false),

View File

@ -0,0 +1,168 @@
<?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\Import\CSV;
use Tests\TestCase;
use App\Models\Task;
use App\Models\Client;
use App\Models\Vendor;
use App\Models\Invoice;
use App\Utils\TruthSource;
use Tests\MockAccountData;
use Illuminate\Support\Str;
use App\Import\Providers\Csv;
use App\Utils\Traits\MakesHash;
use Illuminate\Support\Facades\Cache;
use App\Import\Transformer\BaseTransformer;
use Illuminate\Routing\Middleware\ThrottleRequests;
/**
* @test
* @covers App\Import\Providers\Csv
*/
class TaskImportTest extends TestCase
{
use MakesHash;
use MockAccountData;
protected function setUp(): void
{
parent::setUp();
$this->withoutMiddleware(ThrottleRequests::class);
config(['database.default' => config('ninja.db.default')]);
$this->makeTestData();
$this->withoutExceptionHandling();
auth()->login($this->user);
}
public function testTaskImportWithGroupedTaskNumbers()
{
Task::query()
->where('company_id', $this->company->id)
->forceDelete();
$this->assertEquals(0, Task::withTrashed()->where('company_id', $this->company->id)->count());
/*Need to import clients first*/
$csv = file_get_contents(
base_path().'/tests/Feature/Import/tasks2.csv'
);
$hash = Str::random(32);
$column_map = [
0 => 'task.user_id',
3 => 'project.name',
2 => 'client.name',
4 => 'task.number',
5 => 'task.description',
6 => 'task.is_billable',
7 => 'task.start_date',
9 => 'task.end_date',
8 => 'task.start_time',
10 => 'task.end_time',
11 => 'task.duration',
];
$data = [
'hash' => $hash,
'column_map' => ['task' => ['mapping' => $column_map]],
'skip_header' => true,
'import_type' => 'csv',
];
Cache::put($hash.'-task', base64_encode($csv), 360);
$csv_importer = new Csv($data, $this->company);
$this->assertInstanceOf(Csv::class, $csv_importer);
$csv_importer->import('task');
$base_transformer = new BaseTransformer($this->company);
$task = Task::where('company_id', $this->company->id)->where('number', 'x1234')->first();
$this->assertNotNull($task);
$this->assertEquals(1998, $task->calcDuration());
$time_log = json_decode($task->time_log);
foreach($time_log as $log) {
$this->assertFalse($log[3]);
}
$task = Task::where('company_id', $this->company->id)->where('number', 'x1233')->first();
$this->assertNotNull($task);
$this->assertEquals(9833, $task->calcDuration());
$time_log = json_decode($task->time_log);
foreach($time_log as $log)
{
$this->assertFalse($log[3]);
}
}
public function testTaskImport()
{
Task::query()
->where('company_id', $this->company->id)
->forceDelete();
$this->assertEquals(0, Task::withTrashed()->where('company_id', $this->company->id)->count());
/*Need to import clients first*/
$csv = file_get_contents(
base_path().'/tests/Feature/Import/tasks.csv'
);
$hash = Str::random(32);
$column_map = [
0 => 'task.user_id',
3 => 'project.name',
2 => 'client.name',
5 => 'task.description',
6 => 'task.is_billable',
7 => 'task.start_date',
9 => 'task.end_date',
8 => 'task.start_time',
10 => 'task.end_time',
11 => 'task.duration',
];
$data = [
'hash' => $hash,
'column_map' => ['task' => ['mapping' => $column_map]],
'skip_header' => true,
'import_type' => 'csv',
];
Cache::put($hash.'-task', base64_encode($csv), 360);
$csv_importer = new Csv($data, $this->company);
$this->assertInstanceOf(Csv::class, $csv_importer);
$csv_importer->import('task');
$base_transformer = new BaseTransformer($this->company);
}
}

View File

@ -0,0 +1,48 @@
User,Email,Client,Project,Task,Description,Billable,Start date,Start time,End date,End time,Duration,Tags,Amount ()
Jimmy,user@example.com,,Fixup My Code,,Short and sweet,No,2023-10-23,10:01:07,2023-10-23,12:15:02,02:13:55,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-23,13:07:17,2023-10-23,14:11:17,01:04:00,,
Jimmy,user@example.com,,,,Side Hustle,No,2023-10-23,14:11:19,2023-10-23,17:08:38,02:57:19,,
Bob,bob@example.com,,Fixup My Code,,,No,2023-10-23,14:49:00,2023-10-23,18:03:43,03:14:43,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-23,17:31:40,2023-10-23,18:18:56,00:47:16,,
Bob,bob@example.com,,,,,No,2023-10-24,10:00:00,2023-10-24,17:05:00,07:05:00,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-24,10:01:00,2023-10-24,10:35:55,00:34:55,,
Jimmy,user@example.com,,Gov Proj,,euro handball training,No,2023-10-24,10:35:55,2023-10-24,10:39:49,00:03:54,,
Jimmy,user@example.com,,Gov Proj,,Fix all the servers,No,2023-10-24,10:39:50,2023-10-24,11:05:25,00:25:35,,
Jimmy,user@example.com,,Gov Proj,,euro handball training,No,2023-10-24,11:05:27,2023-10-24,11:10:56,00:05:29,,
Jimmy,user@example.com,,Gov Proj,,clean the house,No,2023-10-24,11:11:08,2023-10-24,11:17:11,00:06:03,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-24,11:17:13,2023-10-24,11:21:36,00:04:23,,
Jimmy,user@example.com,,Gov Proj,,euro handball training,No,2023-10-24,11:21:42,2023-10-24,11:22:58,00:01:16,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-24,11:22:59,2023-10-24,11:32:55,00:09:56,,
Jimmy,user@example.com,,Gov Proj,,wax the floor,No,2023-10-24,11:32:55,2023-10-24,12:19:17,00:46:22,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-24,12:19:19,2023-10-24,12:40:57,00:21:38,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-24,13:18:54,2023-10-24,17:05:55,03:47:01,,
Jimmy,user@example.com,,Gov Proj,,Sand the car,No,2023-10-24,17:05:59,2023-10-24,17:08:27,00:02:28,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-24,17:08:29,2023-10-24,18:00:33,00:52:04,,
Bob,bob@example.com,,Fixup My Code,,,No,2023-10-25,09:58:45,2023-10-25,10:32:09,00:33:24,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-25,10:09:06,2023-10-25,13:01:49,02:52:43,,
Bob,bob@example.com,,Gov Proj,,Attack the windows,No,2023-10-25,10:32:10,2023-10-25,15:54:19,05:22:09,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-25,13:52:30,2023-10-25,15:30:48,01:38:18,,
Jimmy,user@example.com,,Gov Proj,,assist old lady to cross the street,No,2023-10-25,15:30:48,2023-10-25,15:49:10,00:18:22,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-25,15:49:11,2023-10-25,18:09:43,02:20:32,,
Bob,bob@example.com,,Fixup My Code,,,No,2023-10-25,16:08:29,2023-10-25,18:06:12,01:57:43,,
Jlecrenay,jlecrenay@solyme.com,,Gestion interne,,admin work,No,2023-10-26,09:24:00,2023-10-26,12:22:00,02:58:00,,
Bob,bob@example.com,,,,admin work,No,2023-10-26,09:25:49,2023-10-26,11:58:34,02:32:45,,
Jimmy,user@example.com,,,,,No,2023-10-26,09:26:22,2023-10-26,19:04:06,09:37:44,,
Bob,bob@example.com,,Fixup My Code,,administration,No,2023-10-26,13:25:23,2023-10-26,17:08:00,03:42:37,,
Jlecrenay,jlecrenay@solyme.com,,Holiday Project,,training,No,2023-10-26,13:37:31,2023-10-26,18:14:00,04:36:29,,
Jlecrenay,jlecrenay@solyme.com,,,,reading,No,2023-10-27,08:37:17,2023-10-27,09:24:45,00:47:28,,
Jlecrenay,jlecrenay@solyme.com,,LOCATION,,travel,No,2023-10-27,09:25:29,2023-10-27,09:41:46,00:16:17,,
Jlecrenay,jlecrenay@solyme.com,,,,travel,No,2023-10-27,09:42:04,2023-10-27,10:46:35,01:04:31,,
Jimmy,user@example.com,,Fixup My Code,,travel,No,2023-10-27,09:55:02,2023-10-27,10:25:52,00:30:50,,
Bob,bob@example.com,,Fixup My Code,,travel,No,2023-10-27,10:10:00,2023-10-27,10:44:00,00:34:00,,
Jimmy,user@example.com,,Gov Proj,,travel,No,2023-10-27,10:25:53,2023-10-27,10:41:22,00:15:29,,
Jimmy,user@example.com,,Gov Proj,,travel,No,2023-10-27,10:41:23,2023-10-27,10:59:56,00:18:33,,
Bob,bob@example.com,,Fixup My Code,,,No,2023-10-27,10:44:00,2023-10-27,10:59:00,00:15:00,,
Jlecrenay,jlecrenay@solyme.com,,Holiday Project,,travel,No,2023-10-27,10:46:38,2023-10-27,12:20:13,01:33:35,,
Jimmy,user@example.com,,Gov Proj,,travel,No,2023-10-27,11:01:42,2023-10-27,11:06:01,00:04:19,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-27,11:09:38,2023-10-27,12:20:07,01:10:29,,
Jimmy,user@example.com,,Fixup My Code,,,No,2023-10-27,13:02:18,2023-10-27,13:57:17,00:54:59,,
Jlecrenay,jlecrenay@solyme.com,,Covert Ops,,Holiday,No,2023-10-27,13:20:27,2023-10-27,18:10:58,04:50:31,,
Jimmy,user@example.com,,Gov Proj,,fix the wifi,No,2023-10-27,13:57:17,2023-10-27,14:39:11,00:41:54,,
Bob,bob@example.com,,Gov Proj,,fix the spark plugs,No,2023-10-27,14:29:25,2023-10-27,16:31:24,02:01:59,,
Jimmy,user@example.com,,BLOOM,,travel,No,2023-10-27,14:39:12,2023-10-27,15:12:30,00:33:18,,
1 User Email Client Project Task Description Billable Start date Start time End date End time Duration Tags Amount ()
2 Jimmy user@example.com Fixup My Code Short and sweet No 2023-10-23 10:01:07 2023-10-23 12:15:02 02:13:55
3 Jimmy user@example.com Fixup My Code No 2023-10-23 13:07:17 2023-10-23 14:11:17 01:04:00
4 Jimmy user@example.com Side Hustle No 2023-10-23 14:11:19 2023-10-23 17:08:38 02:57:19
5 Bob bob@example.com Fixup My Code No 2023-10-23 14:49:00 2023-10-23 18:03:43 03:14:43
6 Jimmy user@example.com Fixup My Code No 2023-10-23 17:31:40 2023-10-23 18:18:56 00:47:16
7 Bob bob@example.com No 2023-10-24 10:00:00 2023-10-24 17:05:00 07:05:00
8 Jimmy user@example.com Fixup My Code No 2023-10-24 10:01:00 2023-10-24 10:35:55 00:34:55
9 Jimmy user@example.com Gov Proj euro handball training No 2023-10-24 10:35:55 2023-10-24 10:39:49 00:03:54
10 Jimmy user@example.com Gov Proj Fix all the servers No 2023-10-24 10:39:50 2023-10-24 11:05:25 00:25:35
11 Jimmy user@example.com Gov Proj euro handball training No 2023-10-24 11:05:27 2023-10-24 11:10:56 00:05:29
12 Jimmy user@example.com Gov Proj clean the house No 2023-10-24 11:11:08 2023-10-24 11:17:11 00:06:03
13 Jimmy user@example.com Fixup My Code No 2023-10-24 11:17:13 2023-10-24 11:21:36 00:04:23
14 Jimmy user@example.com Gov Proj euro handball training No 2023-10-24 11:21:42 2023-10-24 11:22:58 00:01:16
15 Jimmy user@example.com Fixup My Code No 2023-10-24 11:22:59 2023-10-24 11:32:55 00:09:56
16 Jimmy user@example.com Gov Proj wax the floor No 2023-10-24 11:32:55 2023-10-24 12:19:17 00:46:22
17 Jimmy user@example.com Fixup My Code No 2023-10-24 12:19:19 2023-10-24 12:40:57 00:21:38
18 Jimmy user@example.com Fixup My Code No 2023-10-24 13:18:54 2023-10-24 17:05:55 03:47:01
19 Jimmy user@example.com Gov Proj Sand the car No 2023-10-24 17:05:59 2023-10-24 17:08:27 00:02:28
20 Jimmy user@example.com Fixup My Code No 2023-10-24 17:08:29 2023-10-24 18:00:33 00:52:04
21 Bob bob@example.com Fixup My Code No 2023-10-25 09:58:45 2023-10-25 10:32:09 00:33:24
22 Jimmy user@example.com Fixup My Code No 2023-10-25 10:09:06 2023-10-25 13:01:49 02:52:43
23 Bob bob@example.com Gov Proj Attack the windows No 2023-10-25 10:32:10 2023-10-25 15:54:19 05:22:09
24 Jimmy user@example.com Fixup My Code No 2023-10-25 13:52:30 2023-10-25 15:30:48 01:38:18
25 Jimmy user@example.com Gov Proj assist old lady to cross the street No 2023-10-25 15:30:48 2023-10-25 15:49:10 00:18:22
26 Jimmy user@example.com Fixup My Code No 2023-10-25 15:49:11 2023-10-25 18:09:43 02:20:32
27 Bob bob@example.com Fixup My Code No 2023-10-25 16:08:29 2023-10-25 18:06:12 01:57:43
28 Jlecrenay jlecrenay@solyme.com Gestion interne admin work No 2023-10-26 09:24:00 2023-10-26 12:22:00 02:58:00
29 Bob bob@example.com admin work No 2023-10-26 09:25:49 2023-10-26 11:58:34 02:32:45
30 Jimmy user@example.com No 2023-10-26 09:26:22 2023-10-26 19:04:06 09:37:44
31 Bob bob@example.com Fixup My Code administration No 2023-10-26 13:25:23 2023-10-26 17:08:00 03:42:37
32 Jlecrenay jlecrenay@solyme.com Holiday Project training No 2023-10-26 13:37:31 2023-10-26 18:14:00 04:36:29
33 Jlecrenay jlecrenay@solyme.com reading No 2023-10-27 08:37:17 2023-10-27 09:24:45 00:47:28
34 Jlecrenay jlecrenay@solyme.com LOCATION travel No 2023-10-27 09:25:29 2023-10-27 09:41:46 00:16:17
35 Jlecrenay jlecrenay@solyme.com travel No 2023-10-27 09:42:04 2023-10-27 10:46:35 01:04:31
36 Jimmy user@example.com Fixup My Code travel No 2023-10-27 09:55:02 2023-10-27 10:25:52 00:30:50
37 Bob bob@example.com Fixup My Code travel No 2023-10-27 10:10:00 2023-10-27 10:44:00 00:34:00
38 Jimmy user@example.com Gov Proj travel No 2023-10-27 10:25:53 2023-10-27 10:41:22 00:15:29
39 Jimmy user@example.com Gov Proj travel No 2023-10-27 10:41:23 2023-10-27 10:59:56 00:18:33
40 Bob bob@example.com Fixup My Code No 2023-10-27 10:44:00 2023-10-27 10:59:00 00:15:00
41 Jlecrenay jlecrenay@solyme.com Holiday Project travel No 2023-10-27 10:46:38 2023-10-27 12:20:13 01:33:35
42 Jimmy user@example.com Gov Proj travel No 2023-10-27 11:01:42 2023-10-27 11:06:01 00:04:19
43 Jimmy user@example.com Fixup My Code No 2023-10-27 11:09:38 2023-10-27 12:20:07 01:10:29
44 Jimmy user@example.com Fixup My Code No 2023-10-27 13:02:18 2023-10-27 13:57:17 00:54:59
45 Jlecrenay jlecrenay@solyme.com Covert Ops Holiday No 2023-10-27 13:20:27 2023-10-27 18:10:58 04:50:31
46 Jimmy user@example.com Gov Proj fix the wifi No 2023-10-27 13:57:17 2023-10-27 14:39:11 00:41:54
47 Bob bob@example.com Gov Proj fix the spark plugs No 2023-10-27 14:29:25 2023-10-27 16:31:24 02:01:59
48 Jimmy user@example.com BLOOM travel No 2023-10-27 14:39:12 2023-10-27 15:12:30 00:33:18

View File

@ -0,0 +1,48 @@
User,Email,Client,Project,Task,Description,Billable,Start date,Start time,End date,End time,Duration,Tags,Amount ()
Bob,user@example.com,,Fixup My Code,x111,Short and sweet,No,2023-10-23,10:01:07,2023-10-23,12:15:02,02:13:55,,
Bob,user@example.com,,Fixup My Code,x111,,No,2023-10-23,13:07:17,2023-10-23,14:11:17,01:04:00,,
Bob,user@example.com,,,,Side Hustle,No,2023-10-23,14:11:19,2023-10-23,17:08:38,02:57:19,,
James,james@example.com,,Fixup My Code,x111,,No,2023-10-23,14:49:00,2023-10-23,18:03:43,03:14:43,,
Bob,user@example.com,,Fixup My Code,x111,,No,2023-10-23,17:31:40,2023-10-23,18:18:56,00:47:16,,
James,james@example.com,,,,,No,2023-10-24,10:00:00,2023-10-24,17:05:00,07:05:00,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-24,10:01:00,2023-10-24,10:35:55,00:34:55,,
Bob,user@example.com,,Gov Proj,x112,euro handball training,No,2023-10-24,10:35:55,2023-10-24,10:39:49,00:03:54,,
Bob,user@example.com,,Gov Proj,x112,Fix all the servers,No,2023-10-24,10:39:50,2023-10-24,11:05:25,00:25:35,,
Bob,user@example.com,,Gov Proj,x112,euro handball training,No,2023-10-24,11:05:27,2023-10-24,11:10:56,00:05:29,,
Bob,user@example.com,,Gov Proj,x112,clean the house,No,2023-10-24,11:11:08,2023-10-24,11:17:11,00:06:03,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-24,11:17:13,2023-10-24,11:21:36,00:04:23,,
Bob,user@example.com,,Gov Proj,,euro handball training,No,2023-10-24,11:21:42,2023-10-24,11:22:58,00:01:16,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-24,11:22:59,2023-10-24,11:32:55,00:09:56,,
Bob,user@example.com,,Gov Proj,,wax the floor,No,2023-10-24,11:32:55,2023-10-24,12:19:17,00:46:22,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-24,12:19:19,2023-10-24,12:40:57,00:21:38,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-24,13:18:54,2023-10-24,17:05:55,03:47:01,,
Bob,user@example.com,,Gov Proj,,Sand the car,No,2023-10-24,17:05:59,2023-10-24,17:08:27,00:02:28,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-24,17:08:29,2023-10-24,18:00:33,00:52:04,,
James,james@example.com,,Fixup My Code,,,No,2023-10-25,09:58:45,2023-10-25,10:32:09,00:33:24,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-25,10:09:06,2023-10-25,13:01:49,02:52:43,,
James,james@example.com,,Gov Proj,,Attack the windows,No,2023-10-25,10:32:10,2023-10-25,15:54:19,05:22:09,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-25,13:52:30,2023-10-25,15:30:48,01:38:18,,
Bob,user@example.com,,Gov Proj,,assist old lady to cross the street,No,2023-10-25,15:30:48,2023-10-25,15:49:10,00:18:22,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-25,15:49:11,2023-10-25,18:09:43,02:20:32,,
James,james@example.com,,Fixup My Code,,,No,2023-10-25,16:08:29,2023-10-25,18:06:12,01:57:43,,
Jlecrenay,jlecrenay@solyme.com,,Gestion interne,,admin work,No,2023-10-26,09:24:00,2023-10-26,12:22:00,02:58:00,,
James,james@example.com,,,,admin work,No,2023-10-26,09:25:49,2023-10-26,11:58:34,02:32:45,,
Bob,user@example.com,,,,,No,2023-10-26,09:26:22,2023-10-26,19:04:06,09:37:44,,
James,james@example.com,,Fixup My Code,,administration,No,2023-10-26,13:25:23,2023-10-26,17:08:00,03:42:37,,
Jlecrenay,jlecrenay@solyme.com,,Holiday Project,,training,No,2023-10-26,13:37:31,2023-10-26,18:14:00,04:36:29,,
Jlecrenay,jlecrenay@solyme.com,,,,reading,No,2023-10-27,08:37:17,2023-10-27,09:24:45,00:47:28,,
Jlecrenay,jlecrenay@solyme.com,,LOCATION,,travel,No,2023-10-27,09:25:29,2023-10-27,09:41:46,00:16:17,,
Jlecrenay,jlecrenay@solyme.com,,,,travel,No,2023-10-27,09:42:04,2023-10-27,10:46:35,01:04:31,,
Bob,user@example.com,,Fixup My Code,,travel,No,2023-10-27,09:55:02,2023-10-27,10:25:52,00:30:50,,
James,james@example.com,,Fixup My Code,,travel,No,2023-10-27,10:10:00,2023-10-27,10:44:00,00:34:00,,
Bob,user@example.com,,Gov Proj,,travel,No,2023-10-27,10:25:53,2023-10-27,10:41:22,00:15:29,,
Bob,user@example.com,,Gov Proj,,travel,No,2023-10-27,10:41:23,2023-10-27,10:59:56,00:18:33,,
James,james@example.com,,Fixup My Code,,,No,2023-10-27,10:44:00,2023-10-27,10:59:00,00:15:00,,
Jlecrenay,jlecrenay@solyme.com,,Holiday Project,,travel,No,2023-10-27,10:46:38,2023-10-27,12:20:13,01:33:35,,
Bob,user@example.com,,Gov Proj,,travel,No,2023-10-27,11:01:42,2023-10-27,11:06:01,00:04:19,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-27,11:09:38,2023-10-27,12:20:07,01:10:29,,
Bob,user@example.com,,Fixup My Code,,,No,2023-10-27,13:02:18,2023-10-27,13:57:17,00:54:59,,
Jlecrenay,jlecrenay@solyme.com,,Covert Ops,,Holiday,No,2023-10-27,13:20:27,2023-10-27,18:10:58,04:50:31,,
Bob,user@example.com,,Gov Proj,x1233,fix the wifi,No,2023-10-27,13:57:17,2023-10-27,14:39:11,00:41:54,,
James,james@example.com,,Gov Proj,x1233,fix the spark plugs,No,2023-10-27,14:29:25,2023-10-27,16:31:24,02:01:59,,
Bob,user@example.com,,BLOOM,x1234,travel,No,2023-10-27,14:39:12,2023-10-27,15:12:30,00:33:18,,
1 User Email Client Project Task Description Billable Start date Start time End date End time Duration Tags Amount ()
2 Bob user@example.com Fixup My Code x111 Short and sweet No 2023-10-23 10:01:07 2023-10-23 12:15:02 02:13:55
3 Bob user@example.com Fixup My Code x111 No 2023-10-23 13:07:17 2023-10-23 14:11:17 01:04:00
4 Bob user@example.com Side Hustle No 2023-10-23 14:11:19 2023-10-23 17:08:38 02:57:19
5 James james@example.com Fixup My Code x111 No 2023-10-23 14:49:00 2023-10-23 18:03:43 03:14:43
6 Bob user@example.com Fixup My Code x111 No 2023-10-23 17:31:40 2023-10-23 18:18:56 00:47:16
7 James james@example.com No 2023-10-24 10:00:00 2023-10-24 17:05:00 07:05:00
8 Bob user@example.com Fixup My Code No 2023-10-24 10:01:00 2023-10-24 10:35:55 00:34:55
9 Bob user@example.com Gov Proj x112 euro handball training No 2023-10-24 10:35:55 2023-10-24 10:39:49 00:03:54
10 Bob user@example.com Gov Proj x112 Fix all the servers No 2023-10-24 10:39:50 2023-10-24 11:05:25 00:25:35
11 Bob user@example.com Gov Proj x112 euro handball training No 2023-10-24 11:05:27 2023-10-24 11:10:56 00:05:29
12 Bob user@example.com Gov Proj x112 clean the house No 2023-10-24 11:11:08 2023-10-24 11:17:11 00:06:03
13 Bob user@example.com Fixup My Code No 2023-10-24 11:17:13 2023-10-24 11:21:36 00:04:23
14 Bob user@example.com Gov Proj euro handball training No 2023-10-24 11:21:42 2023-10-24 11:22:58 00:01:16
15 Bob user@example.com Fixup My Code No 2023-10-24 11:22:59 2023-10-24 11:32:55 00:09:56
16 Bob user@example.com Gov Proj wax the floor No 2023-10-24 11:32:55 2023-10-24 12:19:17 00:46:22
17 Bob user@example.com Fixup My Code No 2023-10-24 12:19:19 2023-10-24 12:40:57 00:21:38
18 Bob user@example.com Fixup My Code No 2023-10-24 13:18:54 2023-10-24 17:05:55 03:47:01
19 Bob user@example.com Gov Proj Sand the car No 2023-10-24 17:05:59 2023-10-24 17:08:27 00:02:28
20 Bob user@example.com Fixup My Code No 2023-10-24 17:08:29 2023-10-24 18:00:33 00:52:04
21 James james@example.com Fixup My Code No 2023-10-25 09:58:45 2023-10-25 10:32:09 00:33:24
22 Bob user@example.com Fixup My Code No 2023-10-25 10:09:06 2023-10-25 13:01:49 02:52:43
23 James james@example.com Gov Proj Attack the windows No 2023-10-25 10:32:10 2023-10-25 15:54:19 05:22:09
24 Bob user@example.com Fixup My Code No 2023-10-25 13:52:30 2023-10-25 15:30:48 01:38:18
25 Bob user@example.com Gov Proj assist old lady to cross the street No 2023-10-25 15:30:48 2023-10-25 15:49:10 00:18:22
26 Bob user@example.com Fixup My Code No 2023-10-25 15:49:11 2023-10-25 18:09:43 02:20:32
27 James james@example.com Fixup My Code No 2023-10-25 16:08:29 2023-10-25 18:06:12 01:57:43
28 Jlecrenay jlecrenay@solyme.com Gestion interne admin work No 2023-10-26 09:24:00 2023-10-26 12:22:00 02:58:00
29 James james@example.com admin work No 2023-10-26 09:25:49 2023-10-26 11:58:34 02:32:45
30 Bob user@example.com No 2023-10-26 09:26:22 2023-10-26 19:04:06 09:37:44
31 James james@example.com Fixup My Code administration No 2023-10-26 13:25:23 2023-10-26 17:08:00 03:42:37
32 Jlecrenay jlecrenay@solyme.com Holiday Project training No 2023-10-26 13:37:31 2023-10-26 18:14:00 04:36:29
33 Jlecrenay jlecrenay@solyme.com reading No 2023-10-27 08:37:17 2023-10-27 09:24:45 00:47:28
34 Jlecrenay jlecrenay@solyme.com LOCATION travel No 2023-10-27 09:25:29 2023-10-27 09:41:46 00:16:17
35 Jlecrenay jlecrenay@solyme.com travel No 2023-10-27 09:42:04 2023-10-27 10:46:35 01:04:31
36 Bob user@example.com Fixup My Code travel No 2023-10-27 09:55:02 2023-10-27 10:25:52 00:30:50
37 James james@example.com Fixup My Code travel No 2023-10-27 10:10:00 2023-10-27 10:44:00 00:34:00
38 Bob user@example.com Gov Proj travel No 2023-10-27 10:25:53 2023-10-27 10:41:22 00:15:29
39 Bob user@example.com Gov Proj travel No 2023-10-27 10:41:23 2023-10-27 10:59:56 00:18:33
40 James james@example.com Fixup My Code No 2023-10-27 10:44:00 2023-10-27 10:59:00 00:15:00
41 Jlecrenay jlecrenay@solyme.com Holiday Project travel No 2023-10-27 10:46:38 2023-10-27 12:20:13 01:33:35
42 Bob user@example.com Gov Proj travel No 2023-10-27 11:01:42 2023-10-27 11:06:01 00:04:19
43 Bob user@example.com Fixup My Code No 2023-10-27 11:09:38 2023-10-27 12:20:07 01:10:29
44 Bob user@example.com Fixup My Code No 2023-10-27 13:02:18 2023-10-27 13:57:17 00:54:59
45 Jlecrenay jlecrenay@solyme.com Covert Ops Holiday No 2023-10-27 13:20:27 2023-10-27 18:10:58 04:50:31
46 Bob user@example.com Gov Proj x1233 fix the wifi No 2023-10-27 13:57:17 2023-10-27 14:39:11 00:41:54
47 James james@example.com Gov Proj x1233 fix the spark plugs No 2023-10-27 14:29:25 2023-10-27 16:31:24 02:01:59
48 Bob user@example.com BLOOM x1234 travel No 2023-10-27 14:39:12 2023-10-27 15:12:30 00:33:18