mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-10-14 16:53:17 +01:00
Skip running time entries in solidtime import
Imported running time entries could give a user a second running time entry, for example when an export is imported again while a timer runs.
This commit is contained in:
@@ -211,6 +211,10 @@ class SolidtimeImporter extends DefaultImporter
|
||||
|
||||
// Time entries
|
||||
foreach ($timeEntriesReader as $timeEntryRow) {
|
||||
// Running time entries are skipped, since the user could already have a running time entry
|
||||
if ($timeEntryRow['end'] === '') {
|
||||
continue;
|
||||
}
|
||||
$userId = $this->userImportHelper->getKeyByExternalIdentifier($timeEntryRow['user_id']);
|
||||
if ($userId === null) {
|
||||
throw new ImportException('Time entry references a user that does not exist in the import');
|
||||
@@ -286,22 +290,18 @@ class SolidtimeImporter extends DefaultImporter
|
||||
}
|
||||
$timeEntry->start = $start->utc();
|
||||
|
||||
if ($timeEntryRow['end'] !== '') {
|
||||
try {
|
||||
$end = Carbon::createFromFormat('Y-m-d\TH:i:s\Z', $timeEntryRow['end'], 'UTC');
|
||||
} catch (InvalidFormatException) {
|
||||
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is invalid');
|
||||
}
|
||||
if ($end === null) {
|
||||
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is invalid');
|
||||
}
|
||||
if ($end->lt($start)) {
|
||||
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is before start date ("'.$timeEntryRow['start'].'")');
|
||||
}
|
||||
$timeEntry->end = $end->utc();
|
||||
} else {
|
||||
$timeEntry->end = null;
|
||||
try {
|
||||
$end = Carbon::createFromFormat('Y-m-d\TH:i:s\Z', $timeEntryRow['end'], 'UTC');
|
||||
} catch (InvalidFormatException) {
|
||||
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is invalid');
|
||||
}
|
||||
if ($end === null) {
|
||||
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is invalid');
|
||||
}
|
||||
if ($end->lt($start)) {
|
||||
throw new ImportException('End date ("'.$timeEntryRow['end'].'") is before start date ("'.$timeEntryRow['start'].'")');
|
||||
}
|
||||
$timeEntry->end = $end->utc();
|
||||
|
||||
if ($timeEntryRow['still_active_email_sent_at'] !== '') {
|
||||
try {
|
||||
|
||||
3
resources/testfiles/solidtime_import_test_3/clients.csv
Normal file
3
resources/testfiles/solidtime_import_test_3/clients.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
id,name,organization_id,archived_at,created_at,updated_at
|
||||
b4187a44-41f4-46d7-8460-f15a25b3aad6,"Big Company",ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
e5a4d8f5-81ae-4606-8e84-6ab1ffa58b72,"Other Company (Archived)",ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
|
2
resources/testfiles/solidtime_import_test_3/members.csv
Normal file
2
resources/testfiles/solidtime_import_test_3/members.csv
Normal file
@@ -0,0 +1,2 @@
|
||||
id,user_id,name,email,organization_id,billable_rate,role,created_at,updated_at
|
||||
06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,"Peter Tester",peter.test@email.test,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,admin,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
|
1
resources/testfiles/solidtime_import_test_3/meta.json
Normal file
1
resources/testfiles/solidtime_import_test_3/meta.json
Normal file
@@ -0,0 +1 @@
|
||||
{"id":"d6a324ee-58d5-4096-8069-c63bd55608f7","version":"1.0","organizations":["ee5a8cd6-312f-4ae6-b044-e2014f09ecc2"],"exported_at":"2024-08-26T18:21:59Z"}
|
||||
@@ -0,0 +1 @@
|
||||
id,email,organization_id,role,created_at,updated_at
|
||||
|
@@ -0,0 +1,2 @@
|
||||
id,name,billable_rate,currency,created_at,updated_at
|
||||
ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,"ACME Corp",,EUR,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
|
@@ -0,0 +1,2 @@
|
||||
id,billable_rate,project_id,user_id,member_id,created_at,updated_at
|
||||
180a1a98-2f1c-4596-86e4-63a6be0d7b1d,10002,06e79ec4-33f8-4730-804c-d03c014991d1,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,06e6e605-86bd-417b-b75d-02f671e5d520,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
|
4
resources/testfiles/solidtime_import_test_3/projects.csv
Normal file
4
resources/testfiles/solidtime_import_test_3/projects.csv
Normal file
@@ -0,0 +1,4 @@
|
||||
id,name,color,billable_rate,is_public,client_id,organization_id,is_billable,archived_at,created_at,updated_at
|
||||
06e79ec4-33f8-4730-804c-d03c014991d1,"Project for Big Company",#ec407a,10001,false,b4187a44-41f4-46d7-8460-f15a25b3aad6,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,true,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
622c74a9-7e64-44c2-9426-2a37ac738206,"Project without Client",#ef5350,,false,,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
aa831162-dbb2-4cfe-bfe0-5e3a252c66f0,"Project (Archived)",#6a407f,,true,e5a4d8f5-81ae-4606-8e84-6ab1ffa58b72,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,true,2024-08-25T10:00:00Z,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
|
3
resources/testfiles/solidtime_import_test_3/tags.csv
Normal file
3
resources/testfiles/solidtime_import_test_3/tags.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
id,name,organization_id,created_at,updated_at
|
||||
2c5c2da7-9ef8-4410-bb8f-6e0a90f9d2c7,Development,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
bf6c0ac5-2587-474b-8983-40bb3ea8002f,Backend,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
|
3
resources/testfiles/solidtime_import_test_3/tasks.csv
Normal file
3
resources/testfiles/solidtime_import_test_3/tasks.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
id,name,project_id,organization_id,done_at,created_at,updated_at
|
||||
b49688a0-94f3-4cb3-9ca1-5003de955fb0,"Task 1",06e79ec4-33f8-4730-804c-d03c014991d1,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
b49688a0-94f3-4cb3-9ca1-5003de955fb0,"Task 2",06e79ec4-33f8-4730-804c-d03c014991d1,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,2024-08-24T10:00:00Z,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
|
@@ -0,0 +1,5 @@
|
||||
id,description,start,end,billable_rate,billable,type,member_id,user_id,organization_id,client_id,project_id,task_id,tags,is_imported,still_active_email_sent_at,created_at,updated_at
|
||||
00aae3be-18fc-462d-bee4-350fb605b2f3,,2024-03-04T09:23:52Z,2024-03-04T09:23:52Z,,false,,06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,,,"[""2c5c2da7-9ef8-4410-bb8f-6e0a90f9d2c7"",""bf6c0ac5-2587-474b-8983-40bb3ea8002f""]",false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
1c7a905d-aa12-4d08-bc41-7e92577e7cdf,"Working hard",2024-03-04T09:23:00Z,2024-03-04T10:23:01Z,,true,work,06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,b4187a44-41f4-46d7-8460-f15a25b3aad6,06e79ec4-33f8-4730-804c-d03c014991d1,b49688a0-94f3-4cb3-9ca1-5003de955fb0,[],false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
9f2f5b83-3a6b-4a9c-8d21-4f1e5cf1b7aa,"Lunch break",2024-03-04T12:00:00Z,2024-03-04T12:30:00Z,,false,break,06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,,,[],false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
5d0e8c4a-7b1f-4e2a-9c3d-8a6b2f1e4c7d,"Still running",2024-03-04T13:00:00Z,,,true,work,06e6e605-86bd-417b-b75d-02f671e5d520,0446cdd8-3ad1-43d6-9231-9e0dc4eeb71c,ee5a8cd6-312f-4ae6-b044-e2014f09ecc2,,,,[],false,,2024-08-22T10:36:48Z,2024-08-22T10:36:48Z
|
||||
|
@@ -164,6 +164,31 @@ class SolidtimeImporterTest extends ImporterTestAbstract
|
||||
$this->fail();
|
||||
}
|
||||
|
||||
public function test_import_skips_running_time_entries(): void
|
||||
{
|
||||
// Arrange
|
||||
$zipPath = $this->createTestZip('solidtime_import_test_3');
|
||||
$timezone = 'Europe/Vienna';
|
||||
$organization = Organization::factory()->create();
|
||||
$importer = new SolidtimeImporter;
|
||||
$importer->init($organization);
|
||||
$data = file_get_contents($zipPath);
|
||||
Queue::fake([
|
||||
RecalculateSpentTimeForProject::class,
|
||||
RecalculateSpentTimeForTask::class,
|
||||
]);
|
||||
|
||||
// Act
|
||||
$importer->importData($data, $timezone);
|
||||
$report = $importer->getReport();
|
||||
|
||||
// Assert
|
||||
$this->assertSame(3, $report->timeEntriesCreated);
|
||||
$this->assertSame(3, TimeEntry::query()->count());
|
||||
$this->assertSame(0, TimeEntry::query()->whereNull('end')->count());
|
||||
$this->assertNull(TimeEntry::query()->where('description', 'Still running')->first());
|
||||
}
|
||||
|
||||
public function test_import_of_test_file_twice_succeeds(): void
|
||||
{
|
||||
// Arrange
|
||||
|
||||
Reference in New Issue
Block a user