Blog - Integrating with Kashflow using PHP

Posted by

I love technical stuff and hate bad design.

I'm on Twitter.

Integrating with Kashflow using PHP

Includes sample code and best practice recommendations

Kashflow is a leading provider of cloud-based accounting software - it completely replaces Sage or Quickbooks and is much easier and faster to use. In this blog post I'm going to explain how you can get your website to talk to Kashflow using PHP. If you read it all, you'll find out how to program your website to create a sales invoice on Kashflow when you sell something!

If you're a web developer, this is cool simply because it's cool. If you're a business owner or a financial type, this is cool because you can automate some of your boring finance stuff and spend more time on important things like Social Media and late lunches.

Our solution uses PHP's SoapClient class. Head on over to Kashflow's developer resources page for alternatives.

Contents

  1. Configure your Kashflow account
  2. How the API works
  3. Making it generic
  4. Handling more parameters
  5. Handling errors
  6. Creating a sales invoice using the API
  7. Downloads
  8. Bonus tips

Step 1 - Configure your Kashflow account

Before you do any coding, you need to activate the Kashflow API on your account. The API is the means by which your software will talk to Kashflow.

I recommend that you create yourself a Kashflow account specifically for testing the API - luckily there's a free trial so it won't cost you anything.

Activate the API:

  1. Login to your account
  2. Go to the "Settings" area
  3. Choose "API Settings"
  4. Tick the "Enable the API for my KashFlow account" box
  5. Optionally (but recommended), restrict access to particular IP addresses
  6. Press "update"

If you're not sure whether you've enabled API access successfully, you can use our Kashflow API test page. You'll need to allow connections from this server's IP address (ping switchplane.net to get the current IP).

We promise we won't steal your login details but we can't promise someone else won't - make sure you change your Kashflow password while using the tool.

Step 2 - How the API works

The Kashflow API is based on SOAP, which is a widely used standard for software to communicate. Kashflow will act as a SOAP server, and your code will be a SOAP client.

When you need to talk to Kashflow, your code will send a SOAP request and then receive a SOAP response. PHP 5 has a built-in class called SoapClient which makes this very easy:

$client = new SoapClient("https://securedwebapp.com/api/service.asmx?WSDL");

This one line of code looks up all the functionality that the Kashflow API offers, and makes a ready-to-use object which has all the same functions as the API. All of the complicated communication stuff happens automatically behind the scenes.

Each function created by SoapClient takes a single parameter which should be an array containing the information that the API requires. For example:

$parameters['UserName'] = "username";
$parameters['Password'] = "password";
$response = $client->GetAccountOverview($parameters);

Step 3 - Making it generic

Since all of the Kashflow API calls require your username and password, it makes sense to do some refactoring. What I'd really like is to do this:

$kashflow = new Kashflow("username","password");
$kashflow->getAccountOverview();

This is cool because I don't even have to think about any of the SOAP stuff - I can concentrate on what needs to happen rather than how. So I've created a class, as follows:

class Kashflow
{
	private $m_client   = NULL;
	private $m_username = "";
	private $m_password = "";

	public function __construct($username,$password)
	{
		$this->m_client   = new SoapClient("https://securedwebapp.com/api/service.asmx?WSDL");
		$this->m_username = $username;
		$this->m_password = $password;
	}

	public function getAccountOverview()
	{
		return $this->makeRequest("GetAccountOverview");
	}

	private function makeRequest($fn)
	{
		$parameters['UserName'] = $this->m_username;
		$parameters['Password'] = $this->m_password;
		return $this->m_client->$fn($parameters);
	}
}

The constructor handles the setup of the SoapClient object and stores the username and password for subsequent requests. This saves having to specify it every time I want to make a call.

The magic happens in makeRequest. I pass the name of the API function to makeRequest, which then builds the parameters and calls into Kashflow.

Step 4 - I need more parameters

So what happens if you need to pass more parameters to the Kashflow API? You need an enhanced version of makeRequest:

private function makeRequest($fn,$extra = NULL)
{
	$parameters['UserName'] = $this->m_username;
	$parameters['Password'] = $this->m_password;
	if(NULL != $extra)
		$parameters = array_merge($parameters,$extra);
	return $this->m_client->$fn($parameters);
}

This allows you to optionally pass additional parameters through to Kashflow, for example:

public function getSupplier($code)
{
	$parameters['SupplierCode'] = $code;
	return $this->makeRequest("GetSupplier",$parameters);
}

In the above example, the supplier code gets merged with the username and password before being passed on to Kashflow.

Step 5 - Handling errors

Errors happen all the time - you need to handle them! Let's update our class to throw an exception when the API call fails.

private static function handleResponse($response)
{
	if("NO" == $response->Status)
		throw(new Exception($response->StatusDetail));
	return $response;
}

private function makeRequest($fn,$extra = NULL)
{
	$parameters['UserName'] = $this->m_username;
	$parameters['Password'] = $this->m_password;
	if(NULL != $extra)
		$parameters = array_merge($parameters,$extra);
	return self::handleResponse($this->m_client->$fn($parameters));
}

makeRequest has been updated to delegate the error checking to handleResponse, which checks if Kashflow returned "NO", and either throws an exception or just returns the response.

Step 6 - Creating a sales invoice

Wouldn't it be cool if you could automatically create a sales invoice every time you sold something?

$kashflow = new Kashflow("username","password");
$i = new KashflowInvoice("customer id","invoice number","invoice date","due date");
$i->addLine("qty","net","vat","vat rate","nominal id","description","project id");
$kashflow->insertInvoice($i);

Don't worry about the KashflowInvoice class for now - the interesting bit is how the insertInvoice function prepares the data. At first glance, you would think that all you need to do is pass through $lines as part of the invoice data:

$lines[] = array
(
	"LineID"      => 0,
	"Quantity"    => "qty",
	"Description" => "description",
	"Rate"        => "unit net",
	"ChargeType"  => "nominal id",
	"VatAmount"   => "line vat",
	"VatRate"     => "vat rate",
	"Sort"        => 1,
	"ProductID"   => 0,
	"ProjID"      => "project id"
);

$parameters['Inv'] = array
(
	"InvoiceNumber" => "invoice number",
	"InvoiceDate"   => "invoice date",
	"DueDate"       => "due date",
	"CustomerID"    => "customer id",
	"Lines"         => $lines,
	"NetAmount"     => "net",
	"VATAmount"     => "tax"
);

$response = $this->makeRequest("InsertInvoice",$parameters);

But it's not that simple. The Kashflow API specification requires that the lines parameter is an array of InvoiceLine objects. SoapClient enforces this and will fail unless you obey!

The solution is to use PHP's SoapVar object to wrap the invoice lines, as follows:

$line = array
(
	"LineID"      => 0,
	"Quantity"    => "qty",
	"Description" => "description",
	"Rate"        => "unit net",
	"ChargeType"  => "nominal id",
	"VatAmount"   => "line vat",
	"VatRate"     => "vat rate",
	"Sort"        => 1,
	"ProductID"   => 0,
	"ProjID"      => "project id"
);

$lines[] = new SoapVar($line,0,"InvoiceLine","KashFlow");

$parameters['Inv'] = array
(
	"InvoiceNumber" => "invoice number",
	"InvoiceDate"   => "invoice date",
	"DueDate"       => "due date",
	"CustomerID"    => "customer id",
	"Lines"         => $lines,
	"NetAmount"     => "net",
	"VATAmount"     => "tax"
);

$response = $this->makeRequest("InsertInvoice",$parameters);

SoapClient knows that SoapVar means "an object". The SoapVar constructor's first parameter is the data, the second is encoding, the third is the name of the object, and the fourth is the namespace. The PHP documentation for SoapVar is a bit sketchy so I just pass 0 for encoding and it seems to work. If you know better, please comment!

Downloads

  • This zip file contains the Kashflow and KashflowInvoice classes, and a test script which raises a sales invoice.
  • You may use this code as you see fit, but at your own risk.

Bonus tips

The biggest concern with integration is enforcing synchronisation between the systems. For maximum integrity, use the following rules:

Run Kashflow automation on Cron - inevitably there will be times you cannot connect to Kashflow, such as scheduled maintenance or temporary connectivity problems. If you run the integration in the background, your users will never know if there's a problem and you can handle them transparently.

Do one thing at a time - what happens if one API call works and then there's an error with a second? You'll have lost the results of the successful API call. Try to do one thing at a time, save everything you need and then move on to the next action.

Do Kashflow work late and use database transactions - if Kashflow succeeds but your subsequent code fails, you'll be left with unsynchronised data. Your system will think that it hasn't sent information to Kashflow, but Kashflow will have been updated!

Handle this by assuming that your Kashflow call will work, and doing all your database updates first. Do the Kashflow call as late as possible, and then you can easily rollback. The last thing to do is to store the result of the call such as an invoice id.

Did you find this useful?

If you found this article useful, I'd appreciate any or all of the following!

Add a comment

:
:
:

Comments

Ian

April 3rd, 2018 11:24

Thanks Joel. I will look at that when I get past the bottleneck of thinkology in terms of InsertCustomer!! :( Regards. Ian

Joel

April 3rd, 2018 10:49

Hi Ian,

In Kashflow, the invoice will not be shown as paid until it has payments against it.

You'll need to post the invoice first, and then record a payment using the insertInvoicePayment method. (You might need to implement that yourself, I can't remember whether it's in the example code or not!)

Joel

Ian

April 2nd, 2018 09:42

Hi Joel,

OK - so I now have the Invoice creation issues resolved. Sometimes, our applications will need to generate the Invoice and mark it as paid within Kashflow as a product of the "member" having been onto the Website and made an immediate purchase with payment online via Worldpay.

I thought, in my moment of wisdom (or madness) that simply marking the "Paid" field with 1 and the AmountPaid field with the sum paid (in addition to all the other fields needed to create the Invoice, would do just that - i.e. create the Invoice and mark it as a Paid Invoice.

However, I think I'm missing a beat because when I action the following whilst the Invoice is created in Kashflow, it isn't marked as Paid.

$line = "";
$lines = "";
$invcdetl = "";
$nominalid = "8901512";
$line = array (
"LineID" => 0,
"Quantity" => 1,
"Description" => $desc,
"Rate" => number_format($authAmount,2),
"ChargeType" => intval($nominalid),
"VatAmount" => number_format(0,2),
"VatRate" => number_format(0,2),
"ProductID" => 0,
"Sort" => 1,
"ProjID" => 0
);
$lines[] = new SoapVar($line,0,"InvoiceLine","KashFlow");
$duedate = date("Y-m-d", strtotime(date("Y-m-d")) + (28*24*60*60));
$invcdetl = array (
"InvoiceDBID" => 0,
"InvoiceNumber" => intval($cartId),
"InvoiceDate" => date("Y-m-d"),
"DueDate" => date("Y-m-d"),
"Customer" => "",
"CustomerID" => intval($curmemberrec['kashflow_id']),
"Paid" => 1,
"CustomerReference" => "",
"EstimateCategory" => "",
"SuppressTotal" => 0,
"ProjectID" => 0,
"CurrencyCode" => "GBP",
"ExchangeRate" => 1.00,
"ReadableString" => "",
"Lines" => $lines,
"NetAmount" => number_format($authAmount,2),
"VATAmount" => number_format(0,2),
"AmountPaid" => number_format($authAmount,2),
"CustomerName" => "",
"PermaLink" => "",
"DeliveryAddress" => [],
"UseCustomDeliveryAddress" => 0
);
$parameters['UserName'] = "#########";
$parameters['Password'] = "#########";
$kashflow = new Kashflow($parameters['UserName'],$parameters['Password']);
$invoicresult = $kashflow->insertInvoice($invcdetl);

The $ variables come from elsewhere in the process, and are being parsed correctly from what I have seen in attempts to debug this issue. Any guidance greatly appreciated.

Regards and happy easter
Ian

Ian

January 5th, 2018 08:57

Hi Joel

Many thanks. I believe I can see where the issue might be and will womble off now to give that a go!
R's
I

Joel

January 5th, 2018 08:38

Kashflow expects them in database format i.e. 2018-01-05 08:30:00. In our implementation we don't include the time and that works fine i.e. 2018-01-05.

These are all the parameters from our current working implementation:

$parameters['Inv'] =
[
"InvoiceDBID" => 0,
"InvoiceNumber" => "invoice number",
"InvoiceDate" => "2018-01-05",
"DueDate" => "2018-01-05",
"Customer" => "",
"CustomerID" => 123456,
"Paid" => 0,
"CustomerReference" => "",
"EstimateCategory" => "",
"SuppressTotal" => 1,
"ProjectID" => 0,
"CurrencyCode" => "GBP",
"ExchangeRate" => 1.00,
"ReadableString" => "",
"Lines" => $lines,
"NetAmount" => 10.00,
"VATAmount" => 2.00,
"AmountPaid" => 0.00,
"CustomerName" => "",
"PermaLink" => "",
"DeliveryAddress" => [],
"UseCustomDeliveryAddress" => 0
];

And then each invoice line is built as follows:

$line =
[
"Quantity" => 1,
"Description" => "Description",
"Rate" => 10,
"ChargeType" => 123456,
"VatRate" => 20.00,
"VatAmount" => 2,
"ProductID" => 0,
"Sort" => 1,
"ProjID" => 0,
"LineID" => 0,
"ValuesInCurrency" => 0
];

$lines[] = new SoapVar($line,0,"InvoiceLine","KashFlow");

Ian

January 5th, 2018 01:23

Hi Joel,
Am starting to think am going crazy here! Checked the WSDL thingly - made sure that all the elements that are required are included in the array - and despite everything it still returns a 500 error (with nothing particularly helpful in Server Log!).

The only thing I can think of is that one of the elements - such as InvoiceDate and DueDate have wrongly formatted dates.

Yet I cannot for the life of me see anywhere where the date format is explained in Kashflow. In WSDL they are signed as datetime fields - does this mean that these elements are "Y-m_d h:i:s" or "d-m-Y h:i:s" ? or ist here some weird thing regarding Kashflow dates/times?

Regards
Ian

Joel

January 4th, 2018 08:53

Your array looks ok, but it's missing a lot of parameters. The Kashflow spec has changed over time since this article was published, and more information is required. Check the WSDL and make sure you're sending all the parameters that are required.

Ian

January 3rd, 2018 17:44

Hi Joel,
Ive been attempting to Insert and Invoice using your example, as modified for our needs. It doesn't work which probably means I've "over modified" it! :( Just wondering if you could give me a pointer please.

The "Inv" array is as follows (secure credentials redacted this time!! lol) :-
Array
(
[UserName] => "username"
[Password] => "password"
[Inv] => Array
(
[InvoiceDBID] => 0
[InvoiceNumber] => 1289
[InvoiceDate] => 03-01-2018
[DueDate] => 31-01-2018
[CustomerID] => 73083734
[Lines] => Array
(
[0] => SoapVar Object
(
[enc_type] => 0
[enc_value] => Array
(
[LineID] => 0
[Quantity] => 1
[Description] => EPIC Membership Renewal 1st May 2017 - 30th April 2018 **SPECIAL OFFER**
[Rate] => 32.00
[ChargeType] => Membership Subscription
[VatAmount] => 0.00
[VatRate] => 0.00
)

[enc_stype] => InvoiceLine
[enc_ns] => KashFlow
)

)

[NetAmount] => 32.00
)

)

Does this array look correct to you please?

The relevant added public function to my library is:-
public function insertInvoice($inv) {
$parameters['Inv'] = $inv;
return $this->makeRequest("InsertInvoice", $parameters);
}

$inv is parsed from the file that prepares the Invoice detail and parameters. The "array" shown above is echo'd out immediately prior to the line "return self::handleResponse($this->m_client->$fn($parameters));" from your example.

Many thanks in advance for any pointer you can give!

Rs
I

Joel

January 3rd, 2018 09:18

Hi Ian,

Firstly, don't publish your access credentials in blog comments you lunatic. I've redacted them!

Secondly, can you check your server logs to get more detail on the 500 error? I suspect it's because you're not passing the additional parameters when you call makeRequest from GetInvoicesForCustomer. I think it should be:

public function GetInvoicesForCustomer($custcode)
{
$parameters['CustomerID'] = $custcode;
return $this->makeRequest("GetInvoicesForCustomer",$parameters);
}

Hope that helps.

Joel

Ian

December 30th, 2017 10:54

Hi Joel,

I am a newby at the Kashflow API. Using your blog I built a small app to get a list of Customers and then to acquire from Kashflow Invoices for each customer using GetInvoicesForCustomer with the CustomerID parsed!! The code is below

include_once('_kashflowclass.php');

//$client = new SoapClient("https://securedwebapp.com/api/service.asmx?WSDL");
$parameters['UserName'] = "username";
$parameters['Password'] = "password";
$kashflow = new Kashflow("username","password");
$result = $kashflow->getCustomers();
if(!$result) {
echo "Died";
} else {
$customers = json_decode(json_encode($result), True);
echo "<hr><pre>";
print_r($customers);
echo "</pre>\n";
for($aa=0;$aa<count($customers['GetCustomersResult']['Customer']);$aa++) {
$invres = $kashflow->GetInvoicesForCustomer($customers['GetCustomersResult']['Customer'][$aa]['CustomerID']);
if(!$invres) {
echo "Died";
exit();
} else {
$invoices = json_decode(json_encode($invres), True);
echo "<hr><pre>";print_r($invoices);echo "</pre>";
}
}
}


OK - and the _kashflowclass.php looks like this:

class Kashflow
{
private $m_client = NULL;
private $m_username = "";
private $m_password = "";

public function __construct($username,$password)
{
$this->m_client = new SoapClient("https://securedwebapp.com/api/service.asmx?WSDL");
$this->m_username = $username;
$this->m_password = $password;
}
/*Functions that require no parameters*/
public function getAccountOverview() {
return $this->makeRequest("GetAccountOverview");
}
public function getCustomers() {
return $this->makeRequest("GetCustomers");
}
public function GetInvoices_Overdue() {
return $this->makeRequest("GetInvoices_Overdue");
}
public function GetNominalCodes() {
return $this->makeRequest("GetNominalCodes");
}
public function GetInvoices_Unpaid() {
return $this->makeRequest("GetInvoices_Unpaid");
}

/*Functions that require parameters*/
public function getCustomer($custcode) {
$extra = $custcode;
return $this->makeRequest("GetCustomer");
}
public function GetInvoicesForCustomer($custcode) {
$parameters['CustomerID'] = $custcode;
return $this->makeRequest("GetInvoicesForCustomer");
}
/*Error Handling*/
private static function handleResponse($response) {
if("NO" == $response->Status) {
throw(new Exception($response->StatusDetail));
}
return $response;
}

private function makeRequest($fn,$extra = NULL) {
$parameters['UserName'] = $this->m_username;
$parameters['Password'] = $this->m_password;
if(NULL != $extra) {
$parameters = array_merge($parameters,$extra);
}
return self::handleResponse($this->m_client->$fn($parameters));
}
}

My problem is that PHP is reporting a 500 Server error at $invres = $kashflow->GetInvoicesForCustomer($customers['GetCustomersResult']['Customer'][$aa]['CustomerID']);

But why - if I test the $customers array it has the right info!! Am I doing something wrong? A pointer would be gratefully received!

Rs
Ian

Joel

January 10th, 2017 12:05

Hi Rohit - the Insert Invoice documentation should give you the information you need:

https://www.kashflow.com/developers/soap-api/insertinvoice/

rohit

January 10th, 2017 11:11

i want to integrate this api using php
and i want all sales codes added in kashflow , and vat percentage through api.
is there any methods for that ?

Deliverr address while sending customer Invoice

September 26th, 2016 10:55

How to send custom delivery address while insert invoice, can anybody provide me code sample.

Jomi Garrucho

January 4th, 2016 17:33

Thanks Joel, the $parameters['supl'] worked like a charm, also added
"Created"=>date("Y-m-d\TH:i:s"),
"Updated"=>date("Y-m-d\TH:i:s"),

now it is 100% working, Thanks again, you are awesome!

Joel

January 4th, 2016 08:36

Hi Jomi,

The WSDL file states that the InsertSupplier command requires three parameters:

UserName of type string
Password of type string
supl of type Supplier

The Kashflow class takes care of UserName and Password, you just need to get supl right and it should work fine.

So, in your code, $parameters['supplier'] should be $parameters['supl'], to match the WSDL.

I've copied the relevant snippet of the WSDL file below so you can see.

Cheers,

Joel


https://securedwebapp.com/api/service.asmx?WSDL

<s:element name="InsertSupplier">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="UserName" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="Password" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="supl" type="tns:Supplier"/>
</s:sequence>
</s:complexType>
</s:element>

Jomi Garrucho

December 29th, 2015 17:49

I trying to add a new supplier using your code. I am getting this error "Unhandled exception: Supplier cannot be empty" what did i do wrong here?

$parameters['supplier'] = array
(
"SupplierID"=>0,
"Code"=>$code,
"Name"=>$name,
"Contact"=>$contact,
"Telephone"=>$telephone,
"Mobile"=>$mobile,
"Fax"=>$fax,
"Email"=>$email,
"Address1"=>$address1,
"Address2"=>$address2,
"Address3"=>$address3,
"Address4"=>$address4,
"Postcode"=>$postcode,
"Website"=>$website,
"EC"=>$ec,
"VATNumber"=>$vat,
"CurrencyID"=>$currency,
"PaymentTerms"=>$terms,
"ContactTitle"=>$contacttitle,
"ContactFirstName"=>$contactfname,
"ContactLastName"=>$contactlname
);
return $this->makeRequest("InsertSupplier",$parameters);

dbb

November 25th, 2014 17:19

Thanks for getting back so quickly Joel,

I'll try Fiddler when I have some time. For now, I'm getting around the problem, but it's slow to add each line as it's own soap request. Not great in terms of failed conneciton handling either.

I'm not getting an error at all, even when I use the SoapUI 5.0 program to run the request. I've seen plenty of errors before I got it working, so I know the error return works!

I really couldn't see a difference between the two.

When I find the time to go back over the script, I'll look at it in depth, and report back here with my findings.

Thanks again, and best of luck with whatever is on your plate right now!

Joel

November 25th, 2014 10:33

Hi dbb,

I'm afraid I'm not familiar with powershell so can't really offer specific advice.

However, here's some things I would do to tackle the problem:

1) Are you getting an error back from the Kashflow API? The most likely problem is that your parameters are wrong - check that your types are correct (match the WSDL) and check your spelling!

2) If that doesn't work you need to identify where the problem is. It could potentially be the Kashflow API, something in powershell, or a bug in how you use it.

3) Use something like Fiddler (http://www.telerik.com/fiddler) to sniff the raw SOAP requests and responses and see what's actually being sent and received.

4) Compare the difference between InsertInvoice and UpdateInvoice and see what's going on.

Joel

dbb

November 24th, 2014 17:36

Good evening,

I'm having a frustrating time getting this working. I'm working in powershell, which I'm sure is not optimal, but I'm getting most things to work with it.

WHen I create an invoice, even though I set the UseCustomDeliveryAddress boolean to 1, it will not save either the value of 1, or the custom address.

If I create the invoice, then use the UpdateInvoice method, all is fine.

Similarly, I cannot get powershell to add any lines to the invoice at creation. I beleive this may have something to do with parsing the xsitype that is required (here:http://www.kashflow.com/developers/soap-api/insertinvoice/)

Thanks for any help anyone can offer!

Joel

November 5th, 2014 10:28

HI James - which method are you calling?

Whenever you make a SOAP request, the server at Kashflow's end will check to make sure that you are adhering to the specification (i.e. as defined in the WSDL).

In this case, the specification is saying that the request is missing a property called UseCustomDeliveryAddress.

Try setting $parameters['UseCustomDeliveryAddress'] to 0.

James

October 31st, 2014 16:02

I've followed this tutorial and I am getting the following error:

Unhandled exception: SOAP-ERROR: Encoding: object has no 'UseCustomDeliveryAddress' property

How can this be resolved? Thanks in advance.

Joel

September 25th, 2014 16:25

Hi Peter,

Yes, it is *exactly* the same as InsertCustomer except you need to specify the CustomerID in the customer object.

Can you see in the InsertCustomer function that the CustomerID is passed as a zero?

Instead of passing zero, pass the CustomerID that Kashflow gave you when you inserted a customer.

You can also get the ID from the Kashflow GetCustomers, GetCustomer, and GetCustomerByEmail functions.

Personally, I prefer to store the ID against my customer record as it's also used on other API calls.

Hope that helps!

Joel

Peter

September 25th, 2014 16:10

Hi Joel

Thanks for the class!

One thing you don't cover is how to UpdateCustomer, I'm assuming it is basically InsertCustomer but with an extra variable passed for CustomerID but i cannot find anywhere how Kashflow want this passing?

Thanks
Peter

Joel

August 5th, 2014 13:05

Hi Altreus,

Yes, we did know - it should be easier to integrate with than the SOAP method. It's much more suited to JS apps, for instance.

I've got access to the documentation, although as it's not ready for production we're not currently using it.

However, there is one particular feature that we really need regarding allocation of payments, and as soon as that's done we will upgrade our systems.

I'll write a second article at that point! Hopefully you'll find it as useful as the SOAP one :)

Altreus

August 5th, 2014 12:34

Hey Joel

Did you know Kashflow are developing a RESTful API? This is definitely a system that REST is suited to and SOAP is not.

When that's ready, do you think you'll do a new post about it? I look forward to that - you're the only person I've found on the internet so far that has actually given me a reasonable amount of information on the subject!

Davey

July 24th, 2014 19:38

Sure is Joel at least to someone who understands php coding I guess. :-(

Thanks Anyway for your help

Davey

Joel

July 24th, 2014 11:17

Well, the exception message is making it pretty clear what the problem is!

$customer['Name'] is a blank string.

Davey

July 23rd, 2014 14:57

OK I've been talking with kashflow for the past couple of weeks trying to resolve the error and they kindly altered their code so that now the reason of the failure will be displayed so it helps others narrow down the problem.

The error now is showing as

Unhandled exception: Customer name cannot be empty

But they say they cannot help me any further as they did not write the code in the file that send the information to them.

So does anyone have any idea of what needs modified ?

Below is the Code used for Insert Customer

public function insertCustomer($customer){

$parameters['custr'] = array(
"CustomerID" => 0,
"Code" => '',
"Name" => $customer['Name'],
"Contact" => '',
"Telephone" => $customer['Telephone'],
"Mobile" => '',
"Fax" => '',
"Email" => $customer['Email'],
"Address1" => $customer['Address1'],
"Address2" => $customer['Address2'],
"Address3" => $customer['Address3'],
"Address4" => $customer['Address4'],
"Postcode" => $customer['Postcode'],
"Website" => '',
"EC" => 0,
"OutsideEC" => 0,
"Notes" => '',
"Source" => $this->m_source,
"Discount" => 0,
"ShowDiscount" => 0,
"PaymentTerms" => 0,
"ExtraText1" => '',
"ExtraText2" => '',
"ExtraText3" => '',
"ExtraText4" => '',
"ExtraText5" => '',
"ExtraText6" => '',
"ExtraText7" => '',
"ExtraText8" => '',
"ExtraText9" => '',
"ExtraText10" => '',
"ExtraText11" => '',
"ExtraText12" => '',
"ExtraText13" => '',
"ExtraText14" => '',
"ExtraText15" => '',
"ExtraText16" => '',
"ExtraText17" => '',
"ExtraText18" => '',
"ExtraText19" => '',
"ExtraText20" => '',
"CheckBox1" => 0,
"CheckBox2" => 0,
"CheckBox3" => 0,
"CheckBox4" => 0,
"CheckBox5" => 0,
"CheckBox6" => 0,
"CheckBox7" => 0,
"CheckBox8" => 0,
"CheckBox9" => 0,
"CheckBox10" => 0,
"CheckBox11" => 0,
"CheckBox12" => 0,
"CheckBox13" => 0,
"CheckBox14" => 0,
"CheckBox15" => 0,
"CheckBox16" => 0,
"CheckBox17" => 0,
"CheckBox18" => 0,
"CheckBox19" => 0,
"CheckBox20" => 0,
"Created" => date( "Y-m-d\TH:i:s"),
"Updated" => date( "Y-m-d\TH:i:s"),
"CurrencyID" => 0,
"ContactTitle" => '',
"ContactFirstName" => '',
"ContactLastName" => '',
"CustHasDeliveryAddress" => 1,
"DeliveryAddress1" => $customer['delAddress1'],
"DeliveryAddress2" => $customer['delAddress2'],
"DeliveryAddress3" => $customer['delAddress3'],
"DeliveryAddress4" => $customer['delAddress4'],
"DeliveryPostcode" => $customer['delPostcode'],
"VATNumber" => ''
);
return $this->makeRequest("InsertCustomer",$parameters);
}



Thanks in Anticipation of assistance

Davey

July 14th, 2014 09:08

Is there anyone who could give me a price to sort this problem out ?

Its a little too technical for me to be honest

Davey

July 13th, 2014 14:51

Thanks Joel

I'll take a look at these now that I know where to start :-)

I Updated the Authourised IP in kashflow as that was the first thing I'd thought of

2) Checking for unicode ? That is out of my experience level but I'll see what I can find

The kashflow had been set up by our last host but never really worked, strangely it needed altered every 6 month or so at a charge to push the invoices through to kashflow so we are hoping the moving to a new host etc solves the problems of having to fork out every 6 months (Not cheap either)

Once again thanks for the help

Davey

Joel

July 11th, 2014 10:13

Hi Davey,

That error is an unhandled exception happening on Kashflow's server. The file path is the location in Kashflow's system, not yours.

It's a SoapException, which means your request was probably invalid.

There could be a few things causing this:

1) Even if you just switched servers, you could be running a different version of the SoapClient stuff on Linux and it could be formatting the request differently.

Can you sign up for a test account on Kashflow, and try our test integration?

2) Special characters. Check the request doesn't have any unicode or special characters. Even if it works at your end, Kashflow might not handle it.

3) Unlikely, but Kashflow may have changed their API without warning. This could potentially break an integration, although I would expect a better error message in this instance.

Finally, I would double check you've authorised your new server IP in your Kashflow account. I think you would get a different error message but you should check anyway.

Hope that helps.

Joel

Davey

July 11th, 2014 09:13

I'm getting the following error after checkout can anyone help with advice?

We have just moved server and everything was copied over so it has to be something silly

Unhandled exception: System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.NullReferenceException: Object reference not set to an instance of an object. at KashFlow.KashFlowAPI.InsertCustomer(String UserName, String Password, String& Status, String& StatusDetail, Customer custr) in c:\KashFlow\Applications\KashFlow.Accounting.SoapWebSvc\App_Code\Service.cs:line 3406 --- End of inner exception stack trace ---

Any suggestions where the problem lies as its saying its trying to connect to c:\KashFlow\Applications\KashFlow.Accounting.SoapWebSvc\App_Code\Service.cs but its a linux server so should not be calling C:

Thanks in anticipation of help

Davey

Joel

February 17th, 2014 11:31

Hi Al!

In the download, you'll see that the KashflowInvoice class has a function addLine. Just call that more than once to add multiple invoice lines.

To see how it works behind the scenes, look at the prepareInvoiceLines function inside the Kashflow class.

Joel

Al

February 14th, 2014 17:53

Hi Joel, Al again :) I wonder if you can help me?
Does your script add mulitple invoice lines? e.g. product 1, qty, total - product 2, qty, total?
I'm trying to understand $lines but I'm just not getting it. How can I insert an invoice when I have more than one invoice entry?
Many thanks

Joel

January 8th, 2014 09:33

You need to make sure you've added your testing server's IP address to the allowed list in your Kashflow settings.

Make sure you've precisely followed Step 1 in the article!

Cm Shafi

January 8th, 2014 08:03

I got this message when try to test.php : Unhandled exception: The IP address of 117.201.252.77 is not in the access list, what is the remedy

Joel

January 6th, 2014 13:25

Hi Jatin - that's a big question - you're better off trying Google or a PHP forum somewhere!

jatin singh

December 27th, 2013 10:59

hi, joel could u tell me how to display error message if a person not enter text into require field...

Neha

February 7th, 2012 11:59

Thanks Joel for your quick reply.
Yes i am using free trail version of kashflow and tried your program. it is working fine.

Joel

February 7th, 2012 11:07

Hi Neha - glad you like the blog. I can't really answer your question as I don't know what data you have.

However, if you are still learning how to integrate with external APIs, I would recommend that you start with something small and tackle one step at a time. Remember that you can use the technique in this article for any of the Kashflow API functions.

Kashflow have a two month free trial, so sign up and treat it as a test account - that way you're not messing around with real financial data.

The online manual has the details for all of the functionality in the API - see http://accountingapi.com/.

Neha

February 7th, 2012 08:52

Gr8 blog....

Neha

February 7th, 2012 08:49

Hi I am neha.I am learning kashflow api, so need some help.
I want to import all my site data to kashflow.how can i do this.

Thanks in advance

Joel

January 23rd, 2012 17:13

Hi Amer - take a look at the Kashflow API for the GetCustomer(s) and GetInvoice(s) families of methods. One of those is sure to help - http://accountingapi.com/manual.asp

You'll need to hack your version of the Kashflow class I've published to support the additional functionality you need.

Have fun!

Amer

January 23rd, 2012 09:29

This is really helpful! thank you Joel, How do I check if a client exists? the task I want to accomplish is to check if a client exists and and if a certain invoices exists then either insert or update the client.

Joel

July 18th, 2011 13:21

Yes, that sounds like it would work. I'd still recommend keeping a local copy of the Kashflow customer ID though.

Good luck!

Al

July 18th, 2011 12:30

Hey Joel, thanks for getting back to me, especially as I'm such a noob in this field! No I wasn't 100% sure either! I must apologise for not being clear, it is simply my confusion showing through! :-)
So, kashflow_customer_id is the number assigned to my customer which can be seen in the URL when on the edit customer screen as you describe, nothing to do with my kashflow account id.
So I would create a wrapper function that checks for existing email, then InsertCustomer if necessary and then create the invoice? Thanks again. Just noticed you're PR7, awesome! :-)

Joel

July 18th, 2011 11:24

No - I've deliberately named it that way to make it clear that Kashflow are responsible for the ID. When you synchronise data between two systems, you will have two ids: your own id, and the foreign id. To avoid confusion, you should give these different names i.e. "my_customer_id", "kashflow_customer_id".

Every Kashflow account has a different set of customer IDs - there's no way for me to know what IDs are in your account so I assigned a value of 1 to indicate that it's an integer value and to make it valid PHP code.

The comment is referring to Kashflow's GetCustomer API function. You'll need to update the Kashflow class in the download to support that function before you can use it. See http://accountingapi.com/manual_methods_GetCustomer.asp for documentation.

You're not restricted to just that function though - you can use any of the GetCustomer(s) API functions to get Kashflow's customer ID.

I would recommend that you keep Kashflow's customer ID in your own database of customers so you have it to hand when required. This is what we do as it cuts down on the number of requests when batch processing invoices for a lot of customers.

To create a customer, use InsertCustomer from the API (see http://accountingapi.com/manual_methods_InsertCustomer.asp for documentation). Again, you'll need to update the Kashflow class from the download. Be warned, you need to make sure you pass values for all of the CheckBox parameters otherwise you'll get a SOAP error for missing parameters.

Hope that helps as I wasn't 100% sure what you were after!

Al

July 18th, 2011 10:49

In the sample script download, shouldn't kashflow_customer_id be customer_id and the same for GetKashflowCustomerID and GetCustomerID. It's thrown me because line 16 of the source code assigns 1 to kashflow_customer_id but the comment explains the function GetCustomer should be used. Also, can you give a hint how to deal with new customers? Would we use GetCustomerByEmail. Many thanks for a great article.