The Lazy Coder’s Guide to Deleting Old Harvest Invoices in Batch

I’ve used Harvest for time tracking over the years, and a bit of invoicing for consulting projects. I recently realized that all those old invoices were piling up and wanted to clear them out. Turns out, there’s no good way to do that other than one by one manually…unless you are lazy and a coder like me. Then you dust off your Groovy and write some code:

import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.HttpMethodBase
import org.apache.commons.httpclient.UsernamePasswordCredentials
import org.apache.commons.httpclient.auth.AuthScope
import org.apache.commons.httpclient.methods.DeleteMethod
import org.apache.commons.httpclient.methods.GetMethod

/**
 *
 * Delete old invoices from Harvest by date range.
 * Note that the API only seems to return 50 at a time, so may have to repeat 
 * but script is idempotent.
 *
 * Author: Daniel Seltzer
 * Date: 11/16/14
 */

final def username = 'user@domain.com'
final def password = 'password'
final def authString = 'asdfasdflaksjdfalkdsjfasldfj=='
final def accountName = 'company'

final def APIbase = 'https://' + accountName + '.harvestapp.com'
final def toDate = '20120101'
final def fromDate = '20080101'


HttpClient client = new HttpClient()
client.getState().setCredentials(
    AuthScope.ANY, new UsernamePasswordCredentials(username, password)
)

def getUrl = APIbase + '/invoices?from=' + fromDate + '&to=' + toDate
GetMethod call = new GetMethod(getUrl)
setupCall(call, authString)	


try {
    int status = client.executeMethod( call )

    if (status != 200) {
        throw new RuntimeException('Call to ' + getUrl + ' failed: ' + status)
    }

    def invoices = new XmlSlurper().parseText(call.getResponseBodyAsString()).invoice
    println 'Found ' + invoices.size() + ' invoices.'

    for (i in invoices) {
        print 'Deleting invoice: ' + i.id
        def delUrl = APIbase + '/invoices/' + i.id

        DeleteMethod del = new DeleteMethod(delUrl)
        setupCall(del, authString)

        status = client.executeMethod(del)
        if (status == 200) {
            println '...done.'
        }
        else {
            throw new RuntimeException('Call to ' + delUrl + ' failed: ' + status)
        }
    }

} finally {
    call.releaseConnection()
}


private void setupCall(HttpMethodBase call, authString) {
    call.setDoAuthentication(true)
    call.setRequestHeader('Accept', 'application/xml')
    call.setRequestHeader('Content-Type', 'application/xml')
    call.setRequestHeader('User-Agent', 'Groovy')
    call.setRequestHeader('Authorization', 'Basic ' + authString)
}

There are some utilities online to let you generate the auth string, which is Base64 encoded. Make sure you put in all the right variable values and this should work for you.

This is why APIs are so important. Because lazy coders can get around repetitive manual tasks and remember the pleasures of code.

 

This entry was posted in Tech Note. Bookmark the permalink.

Comments are closed.