API Guide

Our API (Application Programmer Interface) is very much fully implemented, because we ourselves use this very same API for all reads and writes in LawPracticeZA. In other words the main LawPracticeZA interface itself is a user of the API. This guide covers only a small portion of the available functionality - we’re still working on adding a full API reference to this documentation, however this guide will get you as far as being able to post fees, create/send invoices and query accounting transactions. Please drop us a line with any questions and to tell us what you integrations you’re working on: support@lawpracticza.com

Some configuration and setup

For the purposes of example in this guide, VUKA is the fictitious name of the system being integrated with LawPracticeZA. Assume that the database name / firm code for VUKA’s LawPracticeZA instance then is “vuka”.

It will be necessary to log in to VUKA’s LawPracticeZA instance via our main web interface (https://lawpracticeza.com/) and set up some records in LP and then persist their unique IDs in VUKA whether in config files / database / some other persistent but private, secure storage. These are some things you’ll need initially:

  • Get or create the credentials for an API user in the client’s LP instance. The API user should have bookkeeper access. It is not necessary to have fee earner access as bookkeeper can post fees on behalf of other users.

  • Create or get the relevant department_id(s) you will be working with

  • Create or get the relevant salesagent_id(s) - otherwise known as Fee Earners - that you will be posting fees on behalf of.

  • Create at least these posting codes (Masterfile Maintenance -> Posting Codes) in your system as per the requirements of the firm:

    • VUKA Fees - vattable

    • VUKA Disbursements - vattable

    • VUKA Fees - non-vattable

    • VUKA Disbursements - non-vattable

The database table for posting codes is product. So each of these records will have a product_id. I suggest persisting the product_id of these posting codes somewhere in VUKA so that you can easily post fees of the correct types.  We will leave it to VUKA to set the amount, date and description of each fee so it is only necessary to create the above permutations to cover whether Fee or Disbursement, and whether VAT is charged or not.

The database IDs are long unique strings that are generally hidden from the UI but are visible as parameter in the URLs - eg navigating to the update or detail view of a department it will be easy to pick out the ID from the URL of that page. Alternatively you can use the API list or listforselection methods to get JSON lists of objects to determine IDs - eg when logged in go to /api/salesagent/list to get a full list of all fee earner records.

Guide to the Guide

This guide uses examples of actual API interactions written in JavaScript using jQuery - this chosen due to JavaScript being the lingua franca of programming. You do not have to implement your integration with JavaScript - it should be trivial talk to our API from just about any programming language.

Data sent to the server is as regular GET or POST form variables (x-www-form-urlencoded). The response data is JSON encoded UTF8 strings. After each API call in this guide I’ve included an example of the API response.

The API is a REST-ful(ish) API. One can use GETs or POSTs interchangeably - generally one should use GETs for reading data and POSTs for writing data - or just always use POSTs if running into issues with length of querystrings or proxy caching issues. All the API requests take this URL form: https://lawpracticeza.com/api/object/method

Additional positional arguments may be appended after the method as path components to the URL. For example, the word ‘only’ is the first and only positional argument in this URL: https://lawpracticeza.com/api/company/update/only

Named arguments are simply query string parameters.

I’ve also represented these in this doc as function signatures, in case that’s more understandable to you.

HTTP response status codes are meaningful: 200 means the operation was successful, 406 means a user error such as a validation error, 500 means an application error / unhandled exception. See https://http.cat for an amusing illustration of HTTP statuses.

To try out these interactions open LawPracticeZA, log in if you’re not already logged in, and then open a browser console (F12 or Ctrl+Shift+J in most browsers). Cut and paste these code snippets into the console, and ensure that network requests are being logged so that you can inspect the parameters and return values.

Create your own API function

I suggest creating a single function through which you will make all your API calls so that you can set up the authentication and error handling in one place like the following one which uses jQuery’s $.ajax method to do the HTTP requests, setting the special X-token header if the global token variable has a value and throwing an exception if the status response is not a 200 (you may want to modify this to do something else like pop up an error dialog for example).

Note

If using nodejs at the command line, you can use najax to get a jQuery.ajax-like http client library:

npm install najax
npm install jquery
node

> $ = require('jquery');
> $.ajax = require('najax');
> //now use $.ajax like you would with jQuery in a browser
var token;
var apisrc = 'https://lawpracticeza.com/api';

function api(l, a, w) {
   a = a || {};
   w = w || {};
   w.method = w.method || 'POST';
   if (Array.isArray(l)) l = '/' + l.join('/');
   w.url = apisrc + l;
   if (token) w.headers = { 'X-token': token }
   w.data = a;
   w.timeout = 5000;
   w.dataType = 'json';
   w.error = function(xhr) {
     throw xhr.responseText;
   }
   return $.ajax(w);
}

This function takes as its first argument an array of positional arguments (or a path string) and the second argument is a javascript object of named parameters (field names and values). The remaining argument is for further options you may want to specify for the $.ajax call, eg global: false if you want to bypass global ajax event handlers. It returns a Deferred object which you can then use with resolve/cancel functions - eg .then()

Now we’re ready to log into our API for the first time - all these sample API calls use the api function defined above.

Log in to the API

access.login(database, login_code, password)

Login to LawPracticeZA and if successul receive a token to use to access privileged API functions.

Parameters:
  • database (str) – The database name of the LawPracticeZA instance, sometimes called “firm code”. In this example it is “vuka”.

  • login_code (str) – Username, excluding the ‘.’ + database suffix

  • password (str) – Passsword

Returns:

JSON token object

api(['access', 'login'], {'database': 'VUKA', 'login_code': 'apiuser',
'password': 'canonSuezLycrainks'}).then(function(response) {
   token = response.token;
});

Response:

{ token: 'bd4fc904-c9f7-4ca6-bd3c-4f62aa0ecf64' }

Check Login Status / Retrieve Login Information

Sessions are generally long-term but your application should check status at the beginning of a set of interactions and also on any HTTP 403 error received and handle the case where the user may have become logged out, by presenting them with option to re-log-in or update credentials if they have become obselete or password has changed, etc.

access.status()

Check status of currently logged-in user. Authentication methods are either 1) by sending access token as a custom HTTP header called “X-token” or, 2) by sending HTTP cookie called “token”.

Returns:

JSON object

api(['access', 'status']).then(function(response) {


});

Response:

{ signon_uid: null,
       pincode: null,
       freezedate: '2016-12-12',
       zones: ',legal,salesagent,allmatters,bookkeeper,loginmanager,auditor',
       activationdate: null,
       sessiontimeout: 315360000,
       login_uid: 'LGN_SYS_1_20713181404328',
       tagcode: null,
       archived: 0,
       loggedin: true,
       database: 'vuka',
       stampdate: '2017-11-05 12:38:10',
       rev: 0,
       login_code: 'apiuser',
       email: 'blackhole@lawpracticeza.c',
       token: '46f39732-1bcb-49bd-b2a2-607032b792c3',
       points: 0,
       gravatar: '9eaf01cc480ccf661edb3f92645ecaca',
       activationcode: null,
       sessiontimeupdated: true,
       login_name: 'API User' }

When logged in, like above, status returns some user information about the currently logged in user. When not logged in it looks like this:

response:

{ loggedin: false, database: null }

Retrieve Firm Details

company.detail('only')

Retrieve information about the firm.

Returns:

JSON object

The string ‘only’ in this case is a special value because there can only ever be one valid company (firm) per LP instance.

To retrieve information about the law firm you’re currently logged in to such as to get whether they are VAT registered, their firm name etc:

api(['company', 'detail', 'only']).then(function(response) {

});

Response:

{ data:
  { area_id: null,
    company_code: 'ABCLaw',
    tel: '+27 21-300-1073',
    uifnumber: null,
    fromname: 'ABCLaw',
    scrapdate: '9999-12-31 23:59:59',
    creditterms: null,
    streetaddress: null,
    vatflag: 'E',
    notify: 'info@lawpracticeza.com',
    defaultinvoiceprefix: null,
    logo: null,
    emailtemplate: null,er     login_uid: 'LGN_SYSTEM',
    tagline: null,
    hurrytweet: null,
    rev: 0,
    company_id: 'COMP_SYS_1_22627470809605',
    bankdetail: null,
    regnum: '8748567874/12',
    costcentrefields: 'productcategory_name,salesagent_name',
    tradingas: null,
    company_name: 'VUKA',
    default_department_id: 'DEPT__SYS_1_82637389569365',
    email: 'mfairfoot@VUKA.co.za',
    website: null,
    fax: '086 710 8657',
    receipttemplate: null,
    effectivedate: '1970-01-01',
    emailsig: 'ABCLaw',
    vatnumber: '23476283746',
    expirydate: '9999-12-31',
    backupemail: 'info@lawpracticeza.com',
    directions: null,
    postaladdress: '78 Strand Street,\\r\\nCape Town.\\r\\n8000',
    mailfrom: null,
    stampdate: '2017-11-02 14:31:34',
    notes: null,
    invoiceshowminutes: 1,
    town_uid: null,
    company_uid: 'COMP_SYS_1_22627470809605/9999-12-31/9999-12-31
23:59:59' } }

The vatflag field can be one of other: E (Even months), O (Odd Months) or N (Not registered for VAT)

Create a new Client (debtor)

customer.insert(**kwargs)

Create a new client (customer) record. See Schema for required and optional parameters.

Returns:

JSON object

api(['customer', 'insert'], {'customer_name': 'ACME Enterprises',
'customer_code': 'ACME100', 'postaladdress': '1 Freedom Way\\nMoon
Crescent\\nEldorado',
'department_id': 'DEPT__SYS_1_24294194765458'}).then(function(response)
{

});

Response:

{ id: 'CUST_API_1_39321029271287', success: true }

Create a new Matter

matter.insert(**kwargs)

Create a new matter record. See Schema for required and optional parameters

Returns:

JSON object

Fees can only be posted to a matter, not directly to a client - so first we must create at least one matter after creating the client.

api(['matter', 'insert'], {matter_name: 'Sale of 23 Firdale Road (ERF
27)', matter_code: 'ACM/2109/120',
customer_id: 'CUST_API_1_39321029271287',
owner_salesagent_id: 'SA__SYS_1_12291091625556',
dateopened: '2017-11-13'}).then(function(response) {


});

Response:

{ id: "MTR_API_1_61355827191366", success: true }

Update existing Matter

matter.update(**kwargs)

Update an existing matter. Every field must be supplied when updating a record.

Returns:

JSON object

Retrieve the existing matter record with lookups switched off, make any changes to the data, and then update the database with the changed data.

api(['matter', 'detail', 'MTR_SYS_1_76799379571032'],
{lookup: ""}).then(function(r) {
   var d = r.data;
   //make changes to the record:
   d['matter_name'] = 'Driving under the influence';
   api(['matter', 'update'], d).then(function(response) {

   });
});

Response:

{ id: 'MTR_SYS_1_76799379571032', success: true, recordcount: 1 }

Create a new Transfer

matterset.createtransfer(**kwargs)

Shortcut method to create the two pairs of matters linked to two pairs of clients required for a conveyancing transfer.

Returns:

JSON object

This is a shortcut method to create separate buyer and a seller matters under respective buyer & seller client records. The data passed must have attributes prefixed with buyer_ and seller_ respectively, as well as a field called matterset_name which you can fill with a reference or ID for the whole transaction. In this example, we pass existing customer records for both buyer and seller by specifying an existing buyer_customer_id and seller_customer_id, but this routine creates new matters for both the buyer and seller.

api(['matterset', 'createtransfer'],
{'buyer_customer_id': 'CUST_SYS_1_77118312361113',
'buyer_owner_salesagent_id': 'SA__SYS_1_12291091625556',
'buyer_matter_code': 'BO123', 'buyer_matter_name': 'Purchase of
B0123', 'seller_customer_id': 'CUST_SYS_1_33385024356314',
'seller_owner_salesagent_id': 'SA__SYS_1_12291091625556',
'seller_matter_code': 'S0123', 'seller_matter_name': 'Sale of
S0123', 'matterset_name': 'Transfer of B0123'}).then(function(response)
{

});

Response:

{ seller_matter_id: 'MTR_API_1_25128165461388',

 buyer_matter_id: 'MTR_API_1_6203268231442',

 matterset_uid: 'MS__API_1_41657935671694' }

Post or update a draft fee to a matter

matterdraftlineitem.upsert(**kwargs)

Updates a WIP draft invoice item if it exists or inserts it if it doesn’t exist. Existence is determined either by suppling our ID (matterdraftlineitem_uid) or external unique ID (srcid)

Returns:

JSON data

When posting a fee you’ll need to send unitprice, trantotal (the total of the transaction including VAT if applicable) and a tax amount and if these do not agree with the tax rate set on the posting code (product) or if the client’s tax setting overrides this (for example overseas client) then there will be an error.

This particular call also returns the stored record (in the detail property) along with some extra information for rapid presentation back into a list of captured fees due to requirements in our own application - this may be useful for you too otherwise just ignore it.

If matterdraftlineitem_uid is in data then the draft fee will be updated, otherwise it will be created.

If srcid is in data, and if a draft item exists with the same srcid, the draft item will be updated. Otherwise it will be created. srcid is a unique but nullable field, so it can be either NULL but if not NULL then it must be unique.

api(['matterdraftlineitem', 'insert'],
{matter_id: 'MTR_API_1_61355827191366',
product_id: 'PROD_SYS_1_94741331371456', unitprice: 4600, qty: 1,
trantotal: 5244, tax: 644, matterdraftlineitem_name: 'Registration of
Mortgage Bond',
salesagent_id: 'SA__SYS_1_12291091625556'}).then(function(response)
{

});

Response:

{ uid: 13,
 success: true,
 detail:
  { _exvat: 4600,
    trantotal: '5244.00',
    tax: '644.00',
    qty: 1,
    currency_name: 'South African Rand',
    salesagent_id: 'SA__SYS_1_12291091625556',
    matter_code: 'ACM/2109/120',
    product_code: 'VUKAFEE',
    salesinvoice_name: null,
    login_uid: 'LGN_SYS_1_20713181404328',
    customer_name: 'ACME Enterprises',
    login_code: 'apiuser',
    rev: 0,
    salesinvoice_uid: null,
    matter_id: 'MTR_API_1_61355827191366',
    percentage: 14,
    customer_id: 'CUST_API_1_39321029271287',
    product_name: 'VUKA Fee',
    status: 'Draft',
    customer_code: 'ACME100',
    matter_name: 'Sale of 23 Firdale Road (ERF 27)',
    trantotal_display: 'R5,244.00',
    price: 5244.000000000001,
    matterdraftlineitem_name: 'Registration of Mortgage Bond',
    taxtype_name: 'Sales Goods',
    date: '2017-11-13 16:42:45',
    salesagent_name: 'Clive Bredenkamp',
    unit_uid: 'qty',
    matterdraftlineitem_uid: 13,
    product_id: 'PROD_SYS_1_94741331371456',
    created: '2017-11-13 16:42:45',
    stampdate: '2017-11-13 16:42:45',
    unitprice: '4600.0000',
    taxtype_uid: 'TT_001',
    taxtype_code: 'SG',
    salescreditnote_name: null,
    cssclass: 'bgm-black',
    minutes: null,
    login_name: 'API User',
    currency_uid: 'ZAR',
    salescreditnote_uid: null } }

Delete a captured unbilled fee or disbursement

matterdraftlineitem.quickdelete(uid OR src)
Returns:

JSON object

Deletes a draft item by either its uid (matterdraftlineitem_uid) or its srcid. If deleting by srcid you must pass it as a named parameter.

api(['matterdraftlineitem', 'quickdelete'],
{srcid: '67424'}).then(function(response) {



});

Response:

{ numdeleted: 1}

List all captured unbilled fees or disbursements for a matter

matterdraftlineitem.childlist(‘matter’, matter_id[, lookup])

List all unbilled (WIP) invoice items for given matter.

Returns:

JSON object

Specifying a value for the optional lookup parameter will also include foreign name lookups in the data returned - for example the fee earner’s name, not just their salesagent_id

api(['matterdraftlineitem', 'childlist', 'matter',
'MTR_API_1_61355827191366']).then(function(response) {

});

Response:

{ data:
     [ { status: 'Draft',
             matterdraftlineitem_uid: 13,
             salesagent_id: 'SA__SYS_1_12291091625556',
             product_id: 'PROD_SYS_1_94741331371456',
             created: '2017-11-13 16:42:45',
             stampdate: '2017-11-13 16:42:45',
             rev: 0,
             taxtype_uid: 'TT_001',
             qty: 1,
             matterdraftlineitem_name: 'Registration of Mortgage Bond',
             matter_id: 'MTR_API_1_61355827191366',
             salesinvoice_uid: null,
             unitprice: '4600.0000',
             trantotal: '5244.00',
             date: '2017-11-13 16:42:45',
             tax: '644.00',
             customer_id: 'CUST_API_1_39321029271287',
             minutes: null,
             login_uid: 'LGN_SYS_1_20713181404328',
             currency_uid: 'ZAR',
             salescreditnote_uid: null } ],
    results: 1 }

Get current accounting balances for matter

matter.balances(matter_id)

Get business, trust and investment balance information for given matter.

Returns:

JSON object

api(['matter', 'balances',
'MTR_API_1_61355827191366']).then(function(response) {

});

Response:

Get statement of business entries for a matter

matter.statement3(matter_id)

Get list of the business transactions (ie not including trust nor investment) for given matter.

Returns:

JSON object

api(['matter', 'statement3',
'MTR_SYS_1_20872615341588']).then(function(response) {



});

Response:

{
 "data": [
   {
     "reference": "INV1002",
     "date": "2018-04-13",
     "salesinvoice_salesinvoice_uid": "SI__API_1_37440926887645",
     "narration": "Invoice",
     "object_display": "Invoice",
     "login_uid": "LGN_SYS_1_20713181404328",
     "description": "Invoice",
     "staff_id": null,
     "supplier_id": null,
     "amount_neg_display": "-R1,368.00",
     "accountentry_uid": 103,
     "object_name": "Invoice",
     "amount_display": "R5,515.09",
     "matter_id": "MTR_SYS_1_20872615341588",
     "customer_id": "CUST_SYS_1_10989112001592",
     "sent": 0,
     "department_id": "DEPT__SYS_1_82637389569365",
     "reversaluid": null,
     "account_id": "ACC_SYS_1_89892334896025",
     "object": "salesinvoice",
     "accum_ZAR": "5515.09",
     "parentuid": "SI__API_1_37440926887645",
     "accum_ZAR_display": "R5,515.09",
     "accountentry_name": "Advocates account: JJ Smith for opinion on
classified documents.",
     "salesagent_id": "SA__SYS_1_51422332387169",
     "product_id": null,
     "salesinvoice_salesinvoice_name": "INV1002",
     "stampdate": "2018-04-13 12:13:09",
     "journalentry_name": null,
     "taxtype_uid": null,
     "voucherdate": "2018-04-13",
     "objectuid": "SI__API_1_37440926887645",
     "amount": "5515.09",
     "bticode": "B",
     "salesinvoicelineitem_name": "Invoice",
     "currency_uid": "ZAR"
   }
 ]
}

This method returns only business transactions at the invoice level, ie not each line item of each invoice.

Get business, trust and investment accounting entries for a matter

matter.statement2(matter_id[, customer_id, startdate, stopdate])

Get a lot of information including lists of all accounting transactions for given matter. Accounting transactions will be included for each ledger (business, trust and investment). Additional information such as client and firm header information will be included for presentation purposes on letterhead etc.

This method returns all the information you would need to send a complete accounting statement, with sections for business, trust and investment. There is quite a lot of output so here is some explanation:

  • customer: an object containing all the client information data

  • company:  the law firm’s information for showing address info, VAT info, logo etc

  • matters: a list of matter objects. If  as in this example it was called with matter_id then will only contain single matter, but it is also possible to call this statement for a whole client (customer_id) by using named parameter. Then there could be multiple matters.

  • Each matter row contains general information about the matter as well as these keys:

    • B: which contains rows, a list of business transactions

    • T: which contains rows, a list of trust transactions

    • I: which contains rows,  list of investment transactions

api(['matter', 'statement2',
'MTR_API_1_50521349608080']).then(function(response) {



});

Returns

response: {
 "customer": {
   "website": null,
   "scrapdate": "9999-12-31 23:59:59",
   "surname": "Bartlett",
   "customer_code": "Buyer_K1234/T001_64518",
   "entitytype_name": null,
   "streetaddresslines": [ "8 Old Main Road, , , 3660"
   ],
   "creditterms": null,
   "extratelnumbers": null,
   "customer_uid": "CUST_API_1_62114584447723/9999-12-31/9999-12-31
23:59:59",
   "entitytype_uid": null,
   "tel": "908786677",
   "login_uid": "LGN_SYS_1_20713181404328",
   "customer_name": "Carl Bartlett",
   "bankaccount": null,
   "login_code": "apiuser",
   "firstname": "Carl",
   "postaladdresslines": [
     "8 Old Main Road, , , 3660"
   ],
   "bankname": null,
   "taxtype_code": null,
   "rev": 0,
   "cell": "908786677",
   "tradingas": null,
   "customer_id": "CUST_API_1_62114584447723",
   "email": "b@b.XO.ZA",
   "department_id": "DEPT__SYS_1_24294194765458",
   "sendstatement": 0,
   "bankbranch": null,
   "fax": null,
   "effectivedate": "2018-02-24",
   "taxnumber": "4547476576",
   "accountsemail": null,
   "expirydate": "9999-12-31",
   "birthday": null,
   "regnumber": "VAT 4445545454",
   "taxtype_name": null,
   "directions": null,
   "postaladdress": "8 Old Main Road, , , 3660",
   "password": null,
   "data": null,
   "soundex": "C640 B6343 ",
   "entitytype_code": null,
   "taxtype_uid": null,
   "stampdate": "2018-05-28 10:26:15",
   "notes": null,
   "title": "Mr/s",
   "streetaddress": "8 Old Main Road, , , 3660",
   "coord": null,
   "department_name": "Conveyancing",
   "idnumber": "888888 8888 88 8",
   "login_name": "API User"
 },
 "startdate": "1969-11-30",
 "all": false,
 "hidereversals": false,
 "database": "VUKA",
 "company": {
   "area_id": null,
   "company_code": "ABCLaw",
   "tel": "+27 21-300-1073",
   "uifnumber": null,
   "fromname": "ABCLaw",
   "scrapdate": "9999-12-31 23:59:59",
   "streetaddresslines": [],
   "creditterms": null,
   "bankdetaillines": [],
   "streetaddress": null,
   "vatflag": "E",
   "notify": "info@lawpracticeza.com",
   "defaultinvoiceprefix": null,
   "logo": null,
   "emailtemplate": null,
   "login_uid": "LGN_SYSTEM",
   "postaladdresslines": [
     "78 Strand Street,",
     "Cape Town.",
     "8000"
   ],
   "tagline": null,
   "hurrytweet": null,
   "rev": 0,
   "company_id": "COMP_SYS_1_22627470809605",
   "bankdetail": null,
   "regnum": "8748567874/12",
   "costcentrefields": "productcategory_name,salesagent_name",
   "tradingas": null,
   "company_name": "VUKA",
   "default_department_id": "DEPT__SYS_1_82637389569365",
   "email": "mfairfoot@VUKA.co.za",
   "website": null,
   "fax": "086 710 8657",
   "receipttemplate": null,
   "effectivedate": "1970-01-01",
   "emailsig": "ABCLaw",
   "vatnumber": "23476283746",
   "expirydate": "9999-12-31",
   "backupemail": "info@lawpracticeza.com",
   "directions": null,
   "postaladdress": "78 Strand Street,\\r\\nCape Town.\\r\\n8000",
   "mailfrom": null,
   "stampdate": "2017-11-02 14:31:34",
   "notes": null,
   "invoiceshowminutes": 1,
   "town_uid": null,
   "company_uid": "COMP_SYS_1_22627470809605/9999-12-31/9999-12-31
23:59:59"
 },
 "stopdate": "2018-05-30",
 "matters": [
   {
     "scrapdate": "9999-12-31 23:59:59",
     "lastone": true,
     "surname": "Bartlett",
     "matter_uid": "MTR_API_1_50521349608080/9999-12-31/9999-12-31
23:59:59",
     "reservetrust": null,
     "workphone": null,
     "matter_code": "Matter_asdasdasdasd1_Buyer_64518",
     "department_name": "Conveyancing",
     "deadfilenumber": null,
     "customer_name": "Carl Bartlett",
     "login_code": "apiuser",
     "effectivedate": "2018-05-25",
     "title": "Mr/s",
     "rev": 0,
     "login_uid": "LGN_SYS_1_20713181404328",
     "cell": "908786677",
     "matter_id": "MTR_API_1_50521349608080",
     "customer_id": "CUST_API_1_62114584447723",
     "feelevel_name": null,
     "department_id": "DEPT__SYS_1_24294194765458",
     "customer_code": "Buyer_K1234/T001_64518",
     "fax": null,
     "B": {
       "currency_uids": [
         "ZAR"
       ],
       "closingbalance": {
         "ZAR_words": "R0.00 is due by you.",
         "ZAR": "0.00",
         "ZAR_display": "R0.00"
       },
       "openingbalance": {
         "ZAR_words": "R0.00 is due by you.",
         "ZAR": "0.00",
         "ZAR_display": "R0.00"
       },
       "rows": []
     },
     "matter_name": "PROPERTY and ERF 37000 MITCHELLS PLAIN",
     "firstname": "Carl",
     "I": {
       "currency_uids": [
         "ZAR"
       ],
       "closingbalance": {
         "ZAR_words": "Investment Balance should not be in debit.",
         "ZAR": "0.00",
         "ZAR_display": "R0.00"
       },
       "openingbalance": {
         "ZAR_words": "Investment Balance should not be in debit.",
         "ZAR": "0.00",
         "ZAR_display": "R0.00"
       },
       "rows": []
     },
     "accountsemail": null,
     "expirydate": "9999-12-31",
     "feelevel_uid": null,
     "T": {
       "currency_uids": [
         "ZAR"
       ],
       "closingbalance": {
         "ZAR_words": "R383,000.00 is held in trust.",
         "ZAR": "-383000.00",
         "ZAR_display": "-R383,000.00"
       },
       "openingbalance": {
         "ZAR": "0.00",
         "ZAR_display": "R0.00"
       },
       "rows": [
         {
           "reference": "TR1004",
           "date": "2018-05-28",
           "narration": "Transfer Duty Paid",
           "object_display": "Trust Receipt",
           "login_uid": "LGN_SYS_1_20713181404328",
           "description": "Trust Receipt: Transfer Duty Paid",
           "staff_id": null,
           "supplier_id": null,
           "amount_neg_display": "R383,000.00",
           "trustreceipt_name": "Transfer Duty Paid",
           "accountentry_uid": 856,
           "object_name": "Trust Receipt",
           "amount_display": "-R383,000.00",
           "matter_id": "MTR_API_1_50521349608080",
           "customer_id": "CUST_API_1_62114584447723",
           "department_id": "DEPT__SYS_1_24294194765458",
           "reversaluid": null,
           "account_id": "ACC_SYS_1_89892334896025",
           "object": "trustreceipt",
           "accum_ZAR": "-383000.00",
           "parentuid": "TRR__API_1_8852081526208",
           "accum_ZAR_display": "-R383,000.00",
           "accountentry_name": "Transfer Duty Paid",
           "salesagent_id": null,
           "product_id": null,
           "taxtype_uid": null,
           "stampdate": "2018-05-28 10:17:03",
           "objectuid": "TRR__API_1_8852081526208",
           "amount": "-383000.00",
           "bticode": "T",
           "voucherdate": "2018-05-28",
           "currency_uid": "ZAR"
         }
       ]
     },
     "address": null,
     "owner_salesagent_id": "SA__SYS_1_12291091625556",
     "soundex": "P6163 A530 E610  M3242 P450 ",
     "dateopened": "2018-05-22",
     "stampdate": "2018-05-28 10:26:17",
     "bticodes": [
       "T"
     ],
     "owner_salesagent_name": "Clive Bredenkamp",
     "email": "b@b.XO.ZA",
     "cssclass": "bgm-black",
     "login_name": "API User"
   }
 ],
 "meta": {},
 "ageanalysis": {},
 "statementdate": "2018-05-29",
 "date": "2018-05-29",
 "_title": "Statement for Carl Bartlett as at 2018-05-29"
}

Invoice specific unbilled item(s) on a matter

matter.bill(matter_id, matterdraftlineitems=[“mdid0”, “mdid1”, etc.])

Tell the system to create a business tax invoice from the specified draft unbilled items for the matter.

Returns:

JSON object

Note: The value of the matterdraftlineitems parameter should be JSON string representation of an array of matterdraftlineitem_uids. In this example there is only one element: “13”

api(['matter', 'bill', 'MTR_API_1_61355827191366'],
{"matterdraftlineitems": '["13"]'}).then(function(response) {

});

Response:

{ what: 'salesinvoice', uid: 'SI__API_1_5797205714080' }

Invoice all unbilled items on a matter

matter.bill(matter_id, all=True)

Tell the system to create a business tax invoice from all the draft unbilled items for the matter.

Returns:

JSON object

api(['matter', 'bill', 'MTR_API_1_61355827191366'],
{"all": 1}).then(function(response) {

});

Response:

{ what: 'salesinvoice', uid: 'SI__ADM_1_13587111593649' }

Open a browser to view Sales Invoice

Provided the user is logged in to LP with access to view the invoice, you could present them with a link to - for example if the salesinvoice ID were SI__API_1_5797205714080 then:

https://lawpracticeza.com/salesinvoice/detail/SI__API_1_5797205714080

Or in PDF view:

https://lawpracticeza.com/salesinvoice/servepdf/SI__API_1_5797205714080

Send Sales Invoice

salesinvoice.send(salesinvoice_uid)

Send by email the invoice to the accounts email address on the matter. If not set, then it sends to the accounts email address on the client. If no accounts email found then sends to the client’s email address.

This will cause LP to send the invoice to email address(es) associated with the debtor - either the accounts email property, if set, or to the customer’s email address.

api(['salesinvoice', 'send',
'SI__API_1_5797205714080']).then(function(response) {

});

Response:

null