erpnext-api-client
This is a Haskell API client for
ERPNext. It aims to be a
light-weight library based on
http-client and
user-provided record types.
ERPNext has the concept of
DocTypes which
model entities like Customer, Sales Order, etc.
The ERPNext REST
API basically has
seven types of requests covering CRUD operations, remote method calls,
and file uploads.
CRUD operations on a given DocType are:
GET
list of all documents of a DocType (getDocTypeList
)
POST
to create a new document (postDocType
)
GET
a document by name (getDocType
)
PUT
to update a document by name (putDocType
)
DELETE
a document by name (deleteDocType
)
Note: Remote method calls and file uploads are not yet supported.
DocTypes in ERPNext can be extended and newly created by users. This is
why this library does not come with any predefined types for DocTypes.
Instead, users can provide their own types with only the fields they
need.
This library also provides tooling to generate Haskell record types for
DocTypes from a simple DSL (see section below).
Usage
This sample code makes a GET request for a named document of the given
DocType.
{-# LANGUAGE OverloadedStrings #-}
import ERPNext.Client
import Network.HTTP.Client
import Data.Aeson
import GHC.Generics
data Customer = Customer
{ name :: String
} deriving Generic
instance FromJSON Customer
instance ToJSON Customer
instance IsDocType Customer where
docTypeName = "Customer"
main :: IO ()
main = do
let config = mkConfig "https://erpnext.example.com" "api-key" (mkSecret "api-secret")
manager <- newManager defaultManagerSettings
response <- getDocType manager config "Company A" :: IO (ApiResponse Customer)
case response of
Ok _ _ c -> putStrLn $ name c
Err r _ -> print r
stack runhaskell example1.hs
Scope and Limits
Data records for DocTypes
Haskell record types for the DocTypes can be coded by hand which
requires some boiler plate like Aeson instances, handling null
or
missing values, and writing helper functions like mkCustomer
.
This library provides tooling to generate Haskell record types from a
simple DSL very similar to persistent's model definition
syntax.
- A script generates an OpenAPI
Specification file in
yaml
format.
- The
Haskell-OpenAPI-Client-Code-Generator
generates an Haskell API client, from which only the type
definitions can be used.
The resulting files are a separate Haskell package which can be added as
dependency. The resulting record types can be used together with this
API client but the IsDocType
instance must still be defined by hand.
The API client part generated from the OpenAPI spec can not be used.
Example models file:
SalesOrder
name Text
total Double
transaction_date Text
items [SalesOrderItem]
Required name
SalesOrderItem
name Text
Required name
$ ./scripts/gen-openapi-yaml.sh models > openapi.yaml
$ openapi3-code-generator-exe \
--specification openapi.yaml \
--package-name erpnext-api-client-models \
--module-name ERPNextAPI \
--force --output-dir api-client/
$ tree api-client/
api-client/
├── erpnext-api-client-models.cabal
├── src
│ ├── ERPNextAPI
│ │ ├── Common.hs
│ │ ├── Configuration.hs
│ │ ├── Operations
│ │ │ └── DummyOperation.hs
│ │ ├── SecuritySchemes.hs
│ │ ├── TypeAlias.hs
│ │ ├── Types <---- here are the generated types
│ │ │ ├── SalesOrder.hs
│ │ │ ├── SalesOrderItem.hs
│ │ └── Types.hs
│ └── ERPNextAPI.hs
└── stack.yaml
Note on TLS problems
If you're running ERPNext in your test environment, chances are that
your server does not have a valid TLS certificate signed by a trusted
CA.
In this case you can configure the HTTP connection manager's TLS
settings like this:
import Network.HTTP.Client
import Network.HTTP.Client.TLS (mkManagerSettings)
import Network.Connection (TLSSettings (..))
…
let tlsSettings =
mkManagerSettings
( TLSSettingsSimple
{ settingDisableCertificateValidation = True
, settingDisableSession = False
, settingUseServerName = False
}
)
Nothing
manager <- Network.HTTP.Client.newManager tlsSettings
…
stack runhaskell --package crypton-connection --package http-client-tls example1.hs