Last active
October 30, 2024 13:27
-
-
Save andrewroberts/263863db04b641e9ce92f9a85be87484 to your computer and use it in GitHub Desktop.
Google Apps Script snippet for accessing the Quick Books Online API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// From https://github.com/googlesamples/apps-script-oauth1/blob/master/samples/QuickBooks.gs | |
// Consumer key and secret in Config.gs | |
var OAuth1_ = { | |
/** | |
* Connect to the API | |
*/ | |
connect: function() { | |
Log.functionEntryPoint() | |
var service = OAuth1_.getService() | |
if (!service.hasAccess()) { | |
throw new Error( | |
'Need to authorise script' + | |
'Open the following URL and re-run the script: ' + service.authorize()) | |
} else { | |
Log.info('Already connected to QuickBooks API') | |
} | |
}, // connect() | |
/** | |
* Reset the authorization state, so that it can be re-tested. | |
*/ | |
reset: function() { | |
var service = this.getService(); | |
service.reset(); | |
}, | |
/** | |
* Configures the service. | |
*/ | |
getService: function () { | |
return OAuth1.createService('QuickBooks') | |
// Set the endpoint URLs. | |
.setAccessTokenUrl('https://oauth.intuit.com/oauth/v1/get_access_token') | |
.setRequestTokenUrl('https://oauth.intuit.com/oauth/v1/get_request_token') | |
.setAuthorizationUrl('https://appcenter.intuit.com/Connect/Begin') | |
// Set the consumer key and secret. | |
.setConsumerKey(CONSUMER_KEY) | |
.setConsumerSecret(CONSUMER_SECRET) | |
// Set the name of the callback function in the script referenced | |
// above that should be invoked to complete the OAuth flow. | |
.setCallbackFunction('authCallback') | |
// Set the property store where authorized tokens should be persisted. | |
.setPropertyStore(PropertiesService.getUserProperties()); | |
}, | |
} // OAuth_ | |
/** | |
* Handles the OAuth callback. | |
*/ | |
function authCallback(request) { | |
var service = OAuth1_.getService(); | |
var authorized = service.handleCallback(request); | |
if (authorized) { | |
PropertiesService | |
.getUserProperties() | |
.setProperty(PROPERTY_COMPANY_ID, request.parameter.realmId); | |
return HtmlService.createHtmlOutput('Success!'); | |
} else { | |
return HtmlService.createHtmlOutput('Denied'); | |
} | |
} | |
/* | |
{ | |
"CompanyInfo": { | |
"CompanyName": "Sandbox Company_US_1", | |
"LegalName": "Sandbox Company_US_1", | |
"CompanyAddr": { | |
"Id": "1", | |
"Line1": "123 Sierra Way", | |
"City": "San Pablo", | |
"CountrySubDivisionCode": "CA", | |
"PostalCode": "87999", | |
"Lat": "36.6788345", | |
"Long": "-5.4464622" | |
}, | |
"CustomerCommunicationAddr": { | |
"Id": "1", | |
"Line1": "123 Sierra Way", | |
"City": "San Pablo", | |
"CountrySubDivisionCode": "CA", | |
"PostalCode": "87999", | |
"Lat": "36.6788345", | |
"Long": "-5.4464622" | |
}, | |
"LegalAddr": { | |
"Id": "1", | |
"Line1": "123 Sierra Way", | |
"City": "San Pablo", | |
"CountrySubDivisionCode": "CA", | |
"PostalCode": "87999", | |
"Lat": "36.6788345", | |
"Long": "-5.4464622" | |
}, | |
"PrimaryPhone": {}, | |
"CompanyStartDate": "2016-08-02", | |
"FiscalYearStartMonth": "January", | |
"Country": "US", | |
"Email": { | |
"Address": "noreply@quickbooks.com" | |
}, | |
"WebAddr": {}, | |
"SupportedLanguages": "en", | |
"NameValue": [ | |
{ | |
"Name": "NeoEnabled", | |
"Value": "true" | |
}, | |
{ | |
"Name": "IsQbdtMigrated", | |
"Value": "false" | |
}, | |
{ | |
"Name": "CompanyType", | |
"Value": "Other" | |
}, | |
{ | |
"Name": "SubscriptionStatus", | |
"Value": "TRIAL" | |
}, | |
{ | |
"Name": "OfferingSku", | |
"Value": "QuickBooks Online Plus" | |
}, | |
{ | |
"Name": "PayrollFeature", | |
"Value": "false" | |
}, | |
{ | |
"Name": "AccountantFeature", | |
"Value": "false" | |
}, | |
{ | |
"Name": "QBOIndustryType", | |
"Value": "Landscaping Services" | |
}, | |
{ | |
"Name": "ItemCategoriesFeature", | |
"Value": "false" | |
} | |
], | |
"domain": "QBO", | |
"sparse": false, | |
"Id": "1", | |
"SyncToken": "8", | |
"MetaData": { | |
"CreateTime": "2016-08-02T01:04:58-07:00", | |
"LastUpdatedTime": "2016-08-02T02:34:46-07:00" | |
} | |
}, | |
"time": "2016-08-02T03:07:03.743-07:00" | |
} | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 34567890123456789012345678901234567890123456789012345678901234567890123456789 | |
// JSHint - TODO | |
/* jshint asi: true */ | |
(function() {"use strict"})() | |
// API_.gs | |
// ======= | |
// | |
// Wrapper for QuickBooks API | |
var API_ = { | |
/** | |
* Create a new estimate | |
* | |
* @param {String} customerReferenceNumber | |
* | |
* @return {String} the new estimate number | |
*/ | |
createEstimate: function(customerReferenceNumber) { | |
Log.functionEntryPoint() | |
var service = OAuth1_.getService() | |
if (!service.hasAccess()) { | |
throw new Error( | |
'Need to authorise script' + | |
'Open the following URL and re-run the script: ' + service.authorize()) | |
} | |
var companyId = PropertiesService | |
.getUserProperties() | |
.getProperty(PROPERTY_COMPANY_ID) | |
Log.fine('companyId: ' + companyId) | |
var url = QUICKBOOKS_API_URL + 'company/' + companyId + '/estimate' | |
var payload = JSON.stringify({ | |
"Line": [ | |
{ | |
"Id": "1", | |
"LineNum": 1, | |
"Amount": 0, | |
"DetailType": "SalesItemLineDetail", | |
"SalesItemLineDetail": { | |
"ItemRef": { | |
"value": "221", | |
"name": "Dummy Product - DO NOT DELETE" | |
}, | |
"UnitPrice": 0, | |
"Qty": 1, | |
"TaxCodeRef": { | |
"value": "8" | |
} | |
} | |
}, | |
{ | |
"Amount": 0, | |
"DetailType": "SubTotalLineDetail", | |
"SubTotalLineDetail": {} | |
} | |
], | |
"TxnTaxDetail": { | |
"TotalTax": 0, | |
"TaxLine": [ | |
{ | |
"Amount": 0, | |
"DetailType": "TaxLineDetail", | |
"TaxLineDetail": { | |
"TaxRateRef": { | |
"value": "9" | |
}, | |
"PercentBased": true, | |
"TaxPercent": 0, | |
"NetAmountTaxable": 0 | |
} | |
} | |
] | |
}, | |
/* | |
"Line": [ | |
{ | |
"Amount": 0, | |
"DetailType": "SalesItemLineDetail", | |
"SalesItemLineDetail": { | |
"ItemRef": { | |
"value": "221", | |
"name": "Dummy Product" | |
}, | |
"UnitPrice": 0, | |
"Qty": 1, | |
"TaxCodeRef": { | |
"value": "10" | |
} | |
} | |
}, | |
{ | |
"Amount": 0, | |
"DetailType": "SubTotalLineDetail", | |
"SubTotalLineDetail": {} | |
} | |
], | |
"TxnTaxDetail": { | |
"TotalTax": 0 | |
}, | |
*/ | |
/* | |
"Line": [ | |
{ | |
"Amount": 0, | |
"DetailType": "SalesItemLineDetail", | |
"SalesItemLineDetail": { | |
"ItemRef": { | |
"value": "221", | |
"name": "Dummy Product" | |
}, | |
"UnitPrice": 0, | |
"Qty": 0, | |
"TaxCodeRef": { | |
"value": "10" | |
} | |
} | |
}, | |
], | |
*/ | |
"CustomerRef": { | |
"value": customerReferenceNumber, | |
}, | |
}) | |
var options = { | |
headers: { | |
'Accept': 'application/json' | |
}, | |
contentType: 'application/json', | |
method: 'post', | |
payload: payload, | |
muteHttpExceptions: true, | |
} | |
var response = service.fetch(url, options) | |
var code = response.getResponseCode() | |
if (code !== 200) { | |
throw new Error('Fetch failed, code: ' + code + ', message: ' + response.getContentText()) | |
} | |
var result = JSON.parse(response.getContentText()); | |
// Logger.log(JSON.stringify(result, null, 2)) | |
var id = result.Estimate.Id | |
Log.info('Created new estimate, id: ' + id) | |
return id | |
}, // API_.createEstimate() | |
/** | |
* Create a customer | |
* | |
* @param {Number} rowNumber | |
* @param {Sheet} responseSheet | |
* | |
* @return {Object} | |
*/ | |
createCustomer: function(rowNumber, responseSheet) { | |
Log.functionEntryPoint() | |
var service = OAuth1_.getService(); | |
if (!service.hasAccess()) { | |
throw new Error( | |
'Need to authorise script' + | |
'Open the following URL and re-run the script: ' + service.authorize()) | |
} | |
var companyId = PropertiesService | |
.getUserProperties() | |
.getProperty(PROPERTY_COMPANY_ID) | |
Log.fine('companyId: ' + companyId) | |
var url = QUICKBOOKS_API_URL + 'company/' + companyId + '/customer' | |
var customer = getCustomerFromSheet(rowNumber, responseSheet) | |
var payload = JSON.stringify({ | |
DisplayName: customer.client, | |
BillAddr:{ | |
Line1: customer.address, | |
PostalCode: customer.postcode, | |
}, | |
PrimaryPhone: { | |
FreeFormNumber: customer.phoneNumber1, | |
}, | |
AlternatePhone: { | |
FreeFormNumber: customer.phoneNumber2, | |
}, | |
PrimaryEmailAddr: { | |
Address: customer.emailAddress, | |
}, | |
FamilyName: customer.contactName, | |
}) | |
var options = { | |
headers: { | |
'Accept': 'application/json' | |
}, | |
contentType: 'application/json', | |
method: 'post', | |
payload: payload, | |
muteHttpExceptions: true, | |
} | |
var response = service.fetch(url, options) | |
var code = response.getResponseCode() | |
if (code !== 200) { | |
Log.warning('Fetch failed, code: ' + code + ', message: ' + response.getContentText()) | |
// throw new Error('Fetch failed, code: ' + code + ', message: ' + response.getContentText()) | |
return null | |
} | |
var result = JSON.parse(response.getContentText()) | |
// Logger.log('Customer ID: ' + result.Customer.Id) | |
var id = result.Customer.Id | |
Log.info('Created new customer, id: ' + id) | |
return id | |
// Private Functions | |
// ----------------- | |
/** | |
* Get the customer details from the response sheet | |
* | |
* @param {Number} rowNumber | |
* @param {Sheet} sheet | |
* | |
* @return {Object} row object | |
*/ | |
function getCustomerFromSheet(rowNumber, sheet) { | |
Log.functionEntryPoint() | |
var rowRange = sheet.getRange(rowNumber, 1, 1, sheet.getLastColumn()) | |
var rowObject = SsObjects.getRowsData(sheet, rowRange, 1)[0] | |
return rowObject | |
} // getCustomerFromSheet() | |
} // API_.createCustomer() | |
} // API_ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment