Quick Hacks: Asterisk account codes, Ruby programming, and the Freshbooks API

I put together a quick hack last night that will help with managing truant accounts on JKL5’s “Cloudphones” SMB VoIP PBX. Ostensibly, it’s a pre-paid service for small to medium businesses, as well as a couple of residential customers. However, we have a couple of problem accounts that require checking on the accounts payable report followed by a few nagging emails to get to pay┬átheir┬ábills at the 45 day mark.

In each trunk and phone configuration, I have the customer’s company name set as the “Account Code” for any call they take or make. In an IAX2 trunk, it looks like this:

[example_incoming_trunk]
 type=user
 host=iax04.unlimitel.ca
 username=1112223333
 context=call_router
 requirecalltoken=yes
 accountcode=jkl5group
[example_outgoing_trunk]
 type=peer
 host=edge01.my_itsp.com
 secret=some9797numbers
 username=1112223333
 accountcode=jkl5group

With something like a SIP phone, it’s just as easy:

SNOM 870 VoIP Phone

SNOM 870 VoIP Phone, using SIP over WiFi … great phone!

[extension.5150.sip]
 type=friend
 secret=some7634secret
 context=dialing_privledged
 subscribecontext=devices_list
 callerid=John Doe <5150>
 mailbox=5150@jkl5group_com
 host=dynamic
 nat=yes
 canreinvite=no
 dtmfmode=RFC2833
 disallow=all
 allow=ulaw
 allow=alaw
 allow=gsm
 language=en
 ;
 qualify=yes
 accountcode=jkl5group

So, when a call either comes or goes, we can recover the account call from the CDR data from the call, even as the call is progressing. An Asterisk dialplan example might look like:

[accounting_system_check]
 exten => s,1, NoOp(... Checking account status 
     for [${CDR(accountcode)}] )
 same => n, Set(__account_status=${SHELL(/opt/
     check_account.rb ${CDR(accountcode)} )})
 same => n, NoOp("Accounting: account_status is
     ${account_status}")
 same => n, Return()
Asterisk PBX

Asterisk PBX – Helping Solve the VoIP Puzzle for small and medium sized businesses

“check_account.rb” uses the “ruby-freshbooks” Gem to open an API connection to the FreshBooks server. It pulls a list of “Cloudphones” customers, finds the customer ID # based on the name it is sent and then checks to see if there are invoices more than 30 days old and unpaid for that customer. It then returns a status indicator that can be used to make a “yes” or “no” decision if the customer’s call service needs to be impacted due to a truant account.

Note that the double-underscore at the start of the variable name means that the variable is kept and passed on from one context to another, so it is always accessible during the call.

Within the dialplan, code similar to the following before the call is delivered to the extension or to the outgoing trunk does the job:

same => n, Gosub(accounting_system_check,s,1)
same => n, GotoIf($["${account_status}" =
     "OVERDUE"]?account_restricted,1)

Naturally, “account_restricted” should do something valuable, such as inform the outgoing caller about their account status, or send an incoming call directly to voicemail. Using a “Gosub()” avoids repeating the check code across multiple contexts

The best part about all of this is that as soon as the over due invoices are marked paid on FreshBooks, such as an online payment via credit card or PayPal, then the next call coming in or going out gets the “Green Light” and call flow behavior is normal.

We don’t have to waste time policing our Cloudphones accounts recievables, and we leverage a great functionality provided by FreshBooks. The customer status is always up to date, as fast as the payment arrives. It’s a “win” all the way around.