Are you using Toggl as your primary tracking tool instead of Costlocker? Do you still want to know the profitability of your business and employees? No problem - Costlocker is now able to simply import all your Toggl tracked hours and process them.

Let’s get started and import your Toggl weekly report to the Costlocker timesheet. It’s a classic ETL process:

  1. Extract your weekly report from Toggl
  2. Transform Toggl entries to Costlocker entries
  3. Load the time entries in Costlocker

tldr; check the PHP code if you prefer reading code

0. Configuration

Toggl and Costlocker use authentication with personal access tokens. It is neccessary to retrieve valid tokens and find a Toggl workspace id.

$config = [
    'costlocker' => [
        'token' => 'COSTLOCKER_TOKEN',
    ],
    'toggl' => [
        'token' => 'TOGGL_TOKEN',
        'workspace' => 'TOGGL_WORKSPACE',
        'selectedWeek' => date('Y-m-d', strtotime('last monday')),
    ],
    'mapping' => [
        'projects' => [
        ],
        'users' => [
        ],
    ],
];

After the configuration is known, just follow the ETL process:

$togglWeeklyReport = getTogglWeeklyReport($config);
$costlockerEntries = convertTogglWeeklyReport($togglWeeklyReport, $config);
$createdEntries = createCostlockerEntries($costlockerEntries, $config);

1. Extract your weekly report from Toggl

Toggl offers weekly report in its API. Note that the API returns empty entries and aggregation for the week. We have to ignore these entries in the next step.

function getTogglWeeklyReport(array $config)
{
    return httpRequest([
        'url' => 'https://toggl.com/reports/api/v2/weekly',
        'query' => [
            'workspace_id' => $config['toggl']['workspace'],
            'since' => $config['toggl']['selectedWeek'],
            'project_ids' => implode(',', array_keys($config['mapping']['projects'])),
            'user_ids' => implode(',', array_keys($config['mapping']['users'])),
            'user_agent' => 'api_test',
        ],
        'auth' => [
            $config['toggl']['token'],
            'api_token',
        ],
    ]);
}

2. Transform Toggl entries to Costlocker entries

Mapping entities between Toggl and Costlocker is the hardest part of the integration, because it’s not possible to just copy-paste the examples. You’ll have to define the rules for creating Costlocker assignments:

{
    "person_id": 1,
    "project_id": 1,
    "activity_id": 1,
    "task_id": null
}

You can map a toggl’s pid to a project_id/activity_id and a uid to a person_id. If you are a premium Toggl user, you can map tasks as well.

$config = [
    'mapping' => [
        'projects' => [
            'TOGGL_PID' => [
                'project_id' => COSTLOCKER_PROJECT_ID,
                'activity_id' => COSTLOCKER_ACTIVITY_ID,
            ],
        ],
        'users' => [
            'TOGGL_UID' => [
                'person_id' => COSTLOCKER_PERSON_ID,
            ],
        ],
    ],
];

The final conversion is simple. Just transform one object to another…

function convertTogglWeeklyReport(array $togglWeeklyReport, array $config)
{
    $costlockerEntries = [];
 
    foreach ($togglWeeklyReport['data'] as $project) {
        foreach ($project['details'] as $user) {
            foreach ($user['totals'] as $dayIndex => $miliseconds) {
                if ($miliseconds && $dayIndex < 7) {
                    $costlockerEntries[] = [
                        "date" => date('Y-m-d H:i:s', strtotime("{$config['toggl']['selectedWeek']} + {$dayIndex} day 8:00")), 
                        "duration" => $miliseconds / 1000,
                        "description" => "Toggl import - user '{$user['title']['user']}', project '{$project['title']['project']}', client '{$project['title']['client']}'",
                        'assignment' => $config['mapping']['users'][$user['uid']] + $config['mapping']['projects'][$project['pid']],
                    ];
                }
            }
        }
    }
 
    return $costlockerEntries;
}

Tip: Costlocker supports upsert. If you fill uuid field of existing time-entry, we won’t create a new one, but update an existing one.

3. Load the time entries in Costlocker

Push entries to /api-public/v2/timeentries/. Costlocker returns uuids of created/updated entries.

function createCostlockerEntries(array $costlockerEntries, array $config)
{
    return httpRequest([
        'url' => 'https://new.costlocker.com/api-public/v2/timeentries/',
        'json' => $costlockerEntries,
        'auth' => [
            'costlocker/import-toggl-entries',
            $config['costlocker']['token'],
        ],
    ]);
}

4. Compare reports in Toggl and Costlocker

If nothing went wrong, you should see identical weekly reports in both Toggl and Costlocker.

Toggl weekly report

Toggl weekly report

Costlocker weekly report

Costlocker weekly report

Did you find a mistake? Is something unclear or isn’t the code working? Help us to improve the article!