quickbooks-0.5.0.1: src/QuickBooks.hs
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
------------------------------------------------------------------------------
-- |
-- Module: QuickBooks
--
-- For more information, see:
--
-- * QuickBooks API Reference:
-- <https://developer.intuit.com/docs/api/accounting>.
--
------------------------------------------------------------------------------
module QuickBooks
( -- * Authentication and authorization
getAccessTokens
, getAccessTokens'
, getTempTokens
, getTempTokens'
, authorizationURLForToken
, cancelOAuthAuthorization
, cancelOAuthAuthorization'
-- * Transaction entities
-- ** Invoices
-- *** Types
, module QuickBooks.Types
-- *** CRUD an invoice
, createInvoice
, createInvoice'
, readInvoice
, readInvoice'
, updateInvoice
, updateInvoice'
, deleteInvoice
, deleteInvoice'
-- *** Send an invoice via email
, EmailAddress
, emailAddress
, sendInvoice
, sendInvoice'
-- *** Read in configuration files
, readAPIConfigFromFile
, readAppConfigFromFile
-- * Name list entities
-- ** Customer
, queryCustomer
, queryCustomer'
-- ** Line
, queryItem
, queryItem'
) where
import QuickBooks.Authentication
import QuickBooks.Types hiding (EmailAddress,emailAddress)
import Control.Applicative ((<$>),(<*>), (<|>))
import Control.Arrow (second)
import Data.ByteString.Char8 (pack)
import Data.Maybe (fromJust)
import Data.Text (Text)
import Network.HTTP.Client.TLS (tlsManagerSettings)
import Network.HTTP.Client (newManager)
import System.Environment (getEnvironment)
import Text.Email.Validate (EmailAddress, emailAddress)
import QuickBooks.Customer
import QuickBooks.Invoice ( createInvoiceRequest
, deleteInvoiceRequest
, readInvoiceRequest
, updateInvoiceRequest
, sendInvoiceRequest
)
import QuickBooks.Item
import QuickBooks.Logging (apiLogger, getLogger)
import Data.Yaml (ParseException, decodeFileEither)
-- $setup
--
-- >>> import Data
--
-- >>> :set -XOverloadedStrings
-- >>> maybeTestOAuthToken <- lookupTestOAuthTokenFromEnv
-- >>> let oAuthToken = maybe (error "") id maybeTestOAuthToken
-- |
--
-- >>> :{
-- do eitherQueryCustomer <-
-- queryCustomer oAuthToken "Rondonuwu Fruit and Vegi"
-- case eitherQueryCustomer of
-- Right (QuickBooksCustomerResponse (customer:_)) ->
-- print (customerId customer)
-- _ ->
-- putStrLn "Nothing"
-- :}
-- Just "21"
queryCustomer
:: OAuthToken
-> Text
-> IO (Either String (QuickBooksResponse [Customer]))
queryCustomer tok =
queryQuickBooks tok . QueryCustomer
queryCustomer'
:: APIConfig
-> AppConfig
-> OAuthToken
-> Text
-> IO (Either String (QuickBooksResponse [Customer]))
queryCustomer' apiConfig appConfig tok =
queryQuickBooks' apiConfig appConfig tok . QueryCustomer
-- |
--
-- >>> :{
-- do eitherQueryItem <- queryItem oAuthToken "Hours"
-- case eitherQueryItem of
-- Right (QuickBooksItemResponse (item:_)) ->
-- print (itemId item)
-- _ ->
-- putStrLn "Nothing"
-- :}
-- Just "2"
queryItem
:: OAuthToken
-> Text
-> IO (Either String (QuickBooksResponse [Item]))
queryItem tok =
queryQuickBooks tok . QueryItem
queryItem'
:: APIConfig
-> AppConfig
-> OAuthToken
-> Text
-> IO (Either String (QuickBooksResponse [Item]))
queryItem' apiConfig appConfig tok =
queryQuickBooks' apiConfig appConfig tok . QueryItem
-- | Create an invoice.
--
-- Example:
--
-- >>> import Data.Maybe (fromJust)
-- >>> :{
-- do resp <- createInvoice oAuthToken testInvoice
-- case resp of
-- Left err -> putStrLn $ "My custom error message: " ++ err
-- Right (QuickBooksInvoiceResponse invoice) -> do
-- deleteInvoice oAuthToken (fromJust (invoiceId invoice)) (fromJust (invoiceSyncToken invoice))
-- putStrLn "I created an invoice!"
-- :}
-- I created an invoice!
--
-- Note that we deleted the item we created using 'deleteInvoice'.
createInvoice :: OAuthToken -> Invoice -> IO (Either String (QuickBooksResponse Invoice))
createInvoice tok = queryQuickBooks tok . CreateInvoice
-- | Like createInvoice but accepts an APIConfig rather than reading it from the environment
createInvoice' :: APIConfig -> AppConfig -> OAuthToken -> Invoice -> IO (Either String (QuickBooksResponse Invoice))
createInvoice' apiConfig appConfig tok = queryQuickBooks' apiConfig appConfig tok . CreateInvoice
-- | Retrieve the details of an invoice that has been previously created.
--
-- Example:
--
-- First, we create an invoice (see 'createInvoice'):
--
-- >>> import Data.Maybe (fromJust)
-- >>> Right (QuickBooksInvoiceResponse cInvoice) <- createInvoice oAuthToken testInvoice
--
-- Then, we read the invoice and test that it is the same invoice we created:
--
-- >>> let cInvoiceId = fromJust (invoiceId cInvoice)
-- >>> :{
-- do eitherReadInvoice <- readInvoice oAuthToken cInvoiceId
-- case eitherReadInvoice of
-- Left _ -> return False
-- Right (QuickBooksInvoiceResponse rInvoice) -> return (cInvoice == rInvoice)
-- :}
-- True
--
-- Finally, we delete the invoice we created:
--
-- >>> deleteInvoice oAuthToken cInvoiceId (fromJust (invoiceSyncToken cInvoice))
readInvoice :: OAuthToken -> InvoiceId -> IO (Either String (QuickBooksResponse Invoice))
readInvoice tok = queryQuickBooks tok . ReadInvoice
-- | Like readInvoice but accepts an APIConfig rather than reading it from the environment
readInvoice' :: APIConfig -> AppConfig -> OAuthToken -> InvoiceId -> IO (Either String (QuickBooksResponse Invoice))
readInvoice' apiConfig appConfig tok = queryQuickBooks' apiConfig appConfig tok . ReadInvoice
-- | Update an invoice.
--
-- Example:
--
-- First, we create an invoice (see 'createInvoice'):
--
-- >>> import Data.Maybe (fromJust)
-- >>> Right (QuickBooksInvoiceResponse cInvoice) <- createInvoice oAuthToken testInvoice
--
-- Then, we update the customer reference of the invoice:
--
-- >>> let nInvoice = cInvoice { invoiceCustomerRef = Reference Nothing Nothing "1" }
-- >>> :{
-- do eitherUpdateInvoice <- updateInvoice oAuthToken nInvoice
-- case eitherUpdateInvoice of
-- Left _ -> return False
-- Right (QuickBooksInvoiceResponse uInvoice) ->
-- return (invoiceCustomerRef cInvoice == invoiceCustomerRef uInvoice)
-- :}
-- False
--
-- Finally, we delete the invoice we created:
--
-- >>> deleteInvoice oAuthToken (fromJust (invoiceId cInvoice)) (fromJust (invoiceSyncToken cInvoice))
updateInvoice :: OAuthToken -> Invoice -> IO (Either String (QuickBooksResponse Invoice))
updateInvoice tok = queryQuickBooks tok . UpdateInvoice
-- | Like updateInvoice but accepts an APIConfig rather than reading it from the environment
updateInvoice' :: APIConfig -> AppConfig -> OAuthToken -> Invoice -> IO (Either String (QuickBooksResponse Invoice))
updateInvoice' apiConfig appConfig tok = queryQuickBooks' apiConfig appConfig tok . UpdateInvoice
-- | Delete an invoice.
--
-- Example:
--
-- First, we create an invoice (see 'createInvoice'):
--
-- >>> import Data.Maybe (fromJust)
-- >>> Right (QuickBooksInvoiceResponse cInvoice) <- createInvoice oAuthToken testInvoice
--
-- Then, we delete it:
--
-- >>> let cInvoiceId = fromJust (invoiceId cInvoice)
-- >>> let cInvoiceSyncToken = fromJust (invoiceSyncToken cInvoice)
-- >>> :{
-- do eitherDeleteInvoice <- deleteInvoice oAuthToken cInvoiceId cInvoiceSyncToken
-- case eitherDeleteInvoice of
-- Left e -> putStrLn e
-- Right _ -> putStrLn "I deleted an invoice!"
-- :}
-- I deleted an invoice!
deleteInvoice :: OAuthToken -> InvoiceId -> SyncToken -> IO (Either String (QuickBooksResponse DeletedInvoice))
deleteInvoice tok iId = queryQuickBooks tok . DeleteInvoice iId
-- | Like deleteInvoice but accepts an APIConfig rather than reading it from the environment
deleteInvoice' :: APIConfig -> AppConfig -> OAuthToken -> InvoiceId -> SyncToken -> IO (Either String (QuickBooksResponse DeletedInvoice))
deleteInvoice' apiConfig appConfig tok iId = queryQuickBooks' apiConfig appConfig tok . DeleteInvoice iId
-- | Send an invoice via email.
--
-- Example:
--
-- First, we create an invoice (see 'createInvoice'):
--
-- >>> import Data.Maybe (fromJust)
-- >>> Right (QuickBooksInvoiceResponse cInvoice) <- createInvoice oAuthToken testInvoice
--
-- Then, we send the invoice via email:
--
-- >>> let cInvoiceId = fromJust (invoiceId cInvoice)
-- >>> let testEmail = fromJust (emailAddress "test@test.com")
-- >>> :{
-- do eitherSendInvoice <- sendInvoice oAuthToken cInvoiceId testEmail
-- case eitherSendInvoice of
-- Left e -> putStrLn e
-- Right _ -> putStrLn "I sent an invoice!"
-- :}
-- I sent an invoice!
--
-- Finally, we delete the invoice we created:
--
-- >>> deleteInvoice oAuthToken cInvoiceId (fromJust (invoiceSyncToken cInvoice))
sendInvoice :: OAuthToken -> InvoiceId -> EmailAddress -> IO (Either String (QuickBooksResponse Invoice))
sendInvoice tok invId = queryQuickBooks tok . SendInvoice invId
sendInvoice' :: APIConfig
-> AppConfig
-> OAuthToken
-> InvoiceId
-> EmailAddress
-> IO (Either String (QuickBooksResponse Invoice))
sendInvoice' apiConfig appConfig tok invId =
queryQuickBooks' apiConfig appConfig tok . SendInvoice invId
-- | Get temporary tokens to request permission.
--
-- Example:
--
-- >>> :{
-- do eitherTempTokens <- getTempTokens "localhost"
-- case eitherTempTokens of
-- Left e -> putStrLn e
-- Right _ -> putStrLn "I got my request tokens!"
-- :}
-- ...
-- I got my request tokens!
getTempTokens :: CallbackURL -> IO (Either String (QuickBooksResponse OAuthToken))
getTempTokens =
queryQuickBooksOAuth Nothing . GetTempOAuthCredentials
getTempTokens' :: AppConfig -> CallbackURL -> IO (Either String (QuickBooksResponse OAuthToken))
getTempTokens' appConfig =
queryQuickBooksOAuth' appConfig Nothing . GetTempOAuthCredentials
-- | Exchange oauth_verifier for access tokens
getAccessTokens :: OAuthToken -> OAuthVerifier -> IO (Either String (QuickBooksResponse OAuthToken))
getAccessTokens tempToken =
queryQuickBooksOAuth (Just tempToken) . GetAccessTokens
getAccessTokens' :: AppConfig -- Your application's consumer key and consumer secret
-> OAuthToken -- The temporary OAuth tokens obtained from getTempTokens
-> OAuthVerifier -- The OAuthVerifier returned by QuickBooks when it calls your callback
-> IO (Either String (QuickBooksResponse OAuthToken))
getAccessTokens' appConfig tempToken = do
queryQuickBooksOAuth' appConfig (Just tempToken) . GetAccessTokens
-- | Invalidate an OAuth access token and disconnect from QuickBooks.
cancelOAuthAuthorization :: OAuthToken -> IO (Either String (QuickBooksResponse ()))
cancelOAuthAuthorization tok =
queryQuickBooksOAuth (Just tok) DisconnectQuickBooks
cancelOAuthAuthorization' :: AppConfig
-> OAuthToken
-> IO (Either String (QuickBooksResponse ()))
cancelOAuthAuthorization' appConfig tok =
queryQuickBooksOAuth' appConfig (Just tok) DisconnectQuickBooks
queryQuickBooks :: OAuthToken -> QuickBooksQuery a -> IO (Either String (QuickBooksResponse a))
queryQuickBooks tok query = do
apiConfig <- readAPIConfig
appConfig <- readAppConfig
queryQuickBooks' apiConfig appConfig tok query
queryQuickBooks' :: APIConfig -> AppConfig -> OAuthToken -> QuickBooksQuery a -> IO (Either String (QuickBooksResponse a))
queryQuickBooks' apiConfig appConfig tok query = do
manager <- newManager tlsManagerSettings
logger <- getLogger apiLogger
let ?appConfig = appConfig
let ?apiConfig = apiConfig
let ?manager = manager
let ?logger = logger
case query of
CreateInvoice invoice -> createInvoiceRequest tok invoice
ReadInvoice _invoiceId -> readInvoiceRequest tok _invoiceId
UpdateInvoice invoice -> updateInvoiceRequest tok invoice
DeleteInvoice _invoiceId syncToken -> deleteInvoiceRequest tok _invoiceId syncToken
SendInvoice _invoiceId emailAddr -> sendInvoiceRequest tok _invoiceId emailAddr
QueryCustomer queryCustomerName -> queryCustomerRequest tok queryCustomerName
QueryItem queryItemName -> queryItemRequest tok queryItemName
queryQuickBooksOAuth :: Maybe OAuthToken
-> QuickBooksOAuthQuery a
-> IO (Either String (QuickBooksResponse a))
queryQuickBooksOAuth maybeOAuthToken query = do
appConfig <- readAppConfig
queryQuickBooksOAuth' appConfig maybeOAuthToken query
queryQuickBooksOAuth' :: AppConfig
-> Maybe OAuthToken
-> QuickBooksOAuthQuery a
-> IO (Either String (QuickBooksResponse a))
queryQuickBooksOAuth' appConfig maybeOauthToken query = do
manager <- newManager tlsManagerSettings
logger <- getLogger apiLogger
let ?appConfig = appConfig
let ?manager = manager
let ?logger = logger
case query of
(GetTempOAuthCredentials callbackURL) -> getTempOAuthCredentialsRequest callbackURL
(GetAccessTokens oauthVerifier) -> getAccessTokensRequest (fromJust maybeOauthToken) oauthVerifier
DisconnectQuickBooks -> disconnectRequest (fromJust maybeOauthToken)
readAPIConfig :: IO APIConfig
readAPIConfig = do
env <- getEnvironment
case lookupAPIConfig env of
Just config -> return config
Nothing -> fail "The environment variables INTUIT_COMPANY_ID,INTUIT_TOKEN,INTUIT_SECRET, and INTUIT_HOSTNAME must be set"
readAppConfig :: IO AppConfig
readAppConfig = do
env <- getEnvironment
case lookupAppConfig env of
Just config -> return config
Nothing -> fail "The evironment variables INTUIT_CONSUMER_KEY and INTUIT_CONSUMER_SECRET must be set"
lookupAPIConfig :: [(String, String)] -> Maybe APIConfig
lookupAPIConfig environment = APIConfig <$> lookup "INTUIT_COMPANY_ID" env
<*> lookup "INTUIT_TOKEN" env
<*> lookup "INTUIT_SECRET" env
<*> lookup "INTUIT_HOSTNAME" env
<*> (lookup "INTUIT_API_LOGGING_ENABLED" env <|> Just "true")
where env = map (second pack) environment
readAPIConfigFromFile :: FilePath -> IO (Either ParseException APIConfig)
readAPIConfigFromFile = decodeFileEither
readAppConfigFromFile :: FilePath -> IO (Either ParseException AppConfig)
readAppConfigFromFile = decodeFileEither
lookupAppConfig :: [(String, String)] -> Maybe AppConfig
lookupAppConfig environment = AppConfig <$> lookup "INTUIT_CONSUMER_KEY" env
<*> lookup "INTUIT_CONSUMER_SECRET" env
where env = map (second pack) environment