calcinai / xero-php Goto Github PK
View Code? Open in Web Editor NEWA php library for the Xero API, with a cleaner OAuth interface and ORM-like abstraction.
License: MIT License
A php library for the Xero API, with a cleaner OAuth interface and ORM-like abstraction.
License: MIT License
https://github.com/calcinai/xero-php/blob/master/src/XeroPHP/Models/Accounting/Item.php
should use:
use XeroPHP\Models\Accounting\Item\Sale;
(not Organization)
and line 112:
change to:
'SalesDetails' => array (false, self::PROPERTY_TYPE_OBJECT, 'Accounting\Item\Sale', true, false)
if I get a change, will do a PR, but to get the knowledge out for now
The base namespace for everything should be Calcinai\XeroPHP.
Unfortunately this is bigger change in the code and I don't won't to do it before someone says it's good thing to do.
It also needs to go to version 2.x, because there will be major BC break.
https://github.com/calcinai/xero-php/blob/master/src/XeroPHP/Models/Accounting/Item.php#L107
Any idea, off the top of your head, why the generator would leave this out?
Hi,
I have setup this package and used loadByGUID to fetch invoice from Invoice ID. But i want to fetch invoices which are set to AUTHORISED.
How i can set where condition for this, i think in this case i can not use function loadByGUID. can you suggest me regarding this.
Thanks
[...]Models/Accounting/TaxRate.php:
public static function getGUIDProperty(){
return '';
}
The GUID property on TaxRates should be TaxType
. Otherwise if you try to update an existing Tax Rate with the library, it tries to create a new one and gets a name conflict exception.
You have to login https://travis-ci.org/ with your github account and then add this repository.
All branches should be then checked with unit tests and we can add further checks in the future
Recently we had to generate url for Xero object in order to nicely forward user from our system to Xero directly to the object.
We did small class to do this that basically maps object to Xero urls. Should we open a pull request and add it to this library directly?
Xero uses URLs like this: https://go.xero.com/Contacts/View.aspx?contactID=F41CBE7C-DE57-4927-B8A0-49FB0151BE77
it's therefore easy to take contact with ContactId F41CBE7C-DE57-4927-B8A0-49FB0151BE77 and generate whole url.
What do you think?
There are a tonne of great changes in this repository since the last release on 28 June. Any change another release can be made, so we can take advantage of them without running against the bleeding edge? It's the little things like much __isset()
magic methods and fluent chaining support that are not in that last release, but are incredibly useful.
Thanks! (fingers crossed)
The PayrollAU\Employee model seems to be missing the DateOfBirth field.
(Thanks for the great library - much better than Xero's!)
According to the API docs a PUT request to /ContactGroups/ContactGroupID/Contacts is required. Can't see anywhere where this would be possible using the existing API. Happy to implement but just wondering if I've missed something
Contact BatchPayments
and the Balances
are coming across as empty strings instead of arrays or value objects.
In my getByGuid call:
"BatchPayments" => ""
"Balances" => ""
In the Xero online API query tool:
<BatchPayments>
<BankAccountNumber>12-34-56 12345678</BankAccountNumber>
<BankAccountName>NatWest</BankAccountName>
<Details>Banky</Details>
</BatchPayments>
<Balances>
<AccountsReceivable>
<Outstanding>180.70</Outstanding>
<Overdue>180.70</Overdue>
</AccountsReceivable>
<AccountsPayable>
<Outstanding>153.49</Outstanding>
<Overdue>153.49</Overdue>
</AccountsPayable>
</Balances>
The BatchPayments is set as a string here:
https://github.com/calcinai/xero-php/blob/master/src/XeroPHP/Models/Accounting/Contact.php#L320
Just so I'm not going about this wrong, if I wanted to create a new value object for this, would I do that manually, or would it need to be set in your metadata and used to generate a new set of models?
Hi,
I can't seem to find a way to delete contact persons - obviously there is a way since you can do it in the UI, but then I can't seem to find a way to do this in in the XeroPHP (or even the Xero docs - you can update a contact but what happens to contact persons?). Is there one?
Fetching a transaction I see this in the transaction:
[UpdatedDateUTC] => DateTime Object
(
[date] => 2015-08-17 12:34:28.260000
[timezone_type] => 3
[timezone] => Europe/London
)
I'm guessing the timezone comes from the account timezone, but this timestamp from Xero is special - it is a UTC time and should not be presented in the timezone of the account.
So far as I can see the "date" element is correct if interpreted as UTC, but wrong if interpreted as a "Europe/London" timezone time.
When an object in an array property of an object is updated directly, this doesn't set the dirty flag on the object itself.
The fix for this should be smart enough to ignore objects that exist directly.
Tail end of #28
What is the idea behind a call such as this one $this->xero->load('Accounting\\Organisation')->execute()
returning an array of Organisations? Is there a better way to structure the query API?
This issue has a long stream of my ramblings, trying to get to the bottom of how Xero accepts, stores and presents dates:
The result of my testing is: dates are provided by the API as strings exactly as typed by the user in the Xero admin pages. It makes no difference what the organisation timezone is set to, either when the records were created, or now - the date is as typed. I have not explored dates supplied in the PUT or POST API schemas, as I am just dealing with GET at the moment.
So when reading these dates, they need to be interpreted in the timezone of the organisation. Those timezones in Xero are UCT plus or minus an offset, and do not appear to have a DST. I don't know if non-DST timezones are supported by PHP - I suspect not (but would love to be corrected).
I think at the very least, when configuring the application, you should be able to provide the timezone to be used when interpreting these dates, e.g. "Europe/London".
The workaround for now is to set the default system timezone, which is a bit of a hack IMO (dates and time parsing should never rely on the local timezone when dealing with remote data - it needs to be more explicit).
Thoughts?
Given I have an existing invoice, how would I add an attachment to that invoice?
There is no attachment model, and looking at the Save()
method on the Application
class, there's no way for the file to be saved that I can determine.
I keep seeing hints that Xero's api allows batches.
https://community.xero.com/developer/discussion/5950207#answer5952304
https://community.xero.com/developer/discussion/37831/
A use use case in my application would be to create an invoice, attach a payment to that invoice, and create a bill. That's 3 accounting documents. Is it possible to do this in one atomic API call?
So I have an application (either public or partner) with a registered OAuth session. The system then goes to fetch some transactions in a scheduled job. The user is not around to re-authenticate if necessary, since access is via a batch job.
If the OAuth tokens have expired or have been withdrawn (i.e. regenerated on Xero) then this transaction fetch will fail. What is the best way to get the details of the problem?
I can catch exceptions and get the last exception message:
try {
$orgs = $xero->load('Accounting\\Organisation')->execute();
} catch (\Exception $e) {
echo $e->getMessage();
}
// Gives: "The access token has expired"
But is there a more formal way to get the error type rather than just a human-readable text (which may change with different languages anyway)? I guess I need to be able to distinguish between permanent errors (like the expiry above) and temporary errors such as a network being down, so we know whether to alert an admin or just reschedule the job for later. I'm assuming I don't need separate catch() terms for each of the dozen-or-so exceptions that are defined.
The example applications here demonstrate access with the user present, and so doesn't need to handle authorisation errors by the time it gets to inspecting the Xero account details.
Currently Tracking is a string in this library.
It should be a list of TrackingCategory objects.
Example with Tracking:
<Invoice>
<Type>ACCREC</Type>
<CurrencyCode>USD</CurrencyCode>
<Contact>
<ContactID>eaa28f49-6028-4b6e-bb12-d8f6278073fc</ContactID>
</Contact>
<Date>2009-08-30</Date>
<DueDate>2009-09-20</DueDate>
<LineAmountTypes>Inclusive</LineAmountTypes>
<LineItems>
<LineItem>
<Description>Consulting services as agreed</Description>
<Quantity>5.0000</Quantity>
<UnitAmount>99</UnitAmount>
<AccountCode>200</AccountCode>
<Tracking>
<TrackingCategory>
<Name>Activity/Workstream</Name>
<Option>Onsite consultancy</Option>
</TrackingCategory>
</Tracking>
</LineItem>
</LineItems>
</Invoice>
Hey guys,
I've been struggling trying to figure out how to update an invoice using the package. If I do the following:
$master = $xero->loadByGUID('Accounting\\Invoice', 'a0ff65bc-xxxx-xxxx');
$master->setStatus('DRAFT');
$xero->save($master)
I'm getting the following error:
Instance validation error: '0' is not a valid value for global::Xero.API.Library.DataContracts.LineAmountType
What is the correct process to update an Invoice using the package? Ultimately I need to replace all the current line items with new ones on an invoice that has no payments or activity.
Many thanks!
I wasn't sure of the best way of solving this. If I populate a new remote object using fromStringArray() and then call save on it, nothing happens. After looking at the code it is because the dirty flag isn't set so the first check during a save fails and the function just exits without any reason as to why.
I initially thought of setting the dirty flag during the fromStringArray, but as you are using fromStringArray() to populate objects after a read from Xero, this isn't appropriate. Any ideas on this?
Hi
I cant seem to find a way to set the Tracking Category option on a Line Item when attempting to create a new invoice. Has anybody had any luck?
eg. References to Lineitems in BankTransaction.php needs to be LineItems – otherwise it breaks on submission.
I think I'm hitting this issue. I've read the thread but I'm unclear on the best way to solve this.
https://community.xero.com/developer/question/32311
We all know that reading the source code is the best way to learn about a particular system, and the code is quite readable. However I think many developers would appreciate a separate documentation, or at least more examples like how to make a more complex query etc.
I can help with that but I'll defer to you whether we use Sphinx + ReadTheDocs or PHPDoc + GitHub Pages, or something else entirely.
Hi,
I am using this example in my model file vendor/calcinai/xero-php/examples/public.php and i am using database instead of session to store tokens and token_secret.
And i am using bellow code to import invoices to store in database also same time i am importing contacts info for particular invoices.
$invoices=$this->xero->load('Accounting\Invoice')->where('Status=="AUTHORISED" OR Status=="PAID"')->modifiedAfter(new \DateTime($this->timesince))->execute();
Now there are many invoices so some time token become expired during process and at that time i want to through exception and want to store error in my log file.
But when i try that, it can not be handled. so then i used directly send function like below code but then also it can not be halalled as it terminate script in parse function of response .php file.
$url = new URL($xero, \XeroPHP\Models\Accounting\Invoice::getResourceURI());
$request = new Request($xero, $url, Request::METHOD_GET);
$request->setParameter('where', $where);
try {
$request->send();
} catch (Exception $e){
echo($e->getCode());
print_r($request->getResponse()->getOAuthResponse());
}
So i want to know how can i handle exception when i got some error like token expired etc...
Is there any way that i can handle it with this?
$invoices=$this->xero->load('Accounting\Invoice')->where('Status=="AUTHORISED" OR Status=="PAID"')->modifiedAfter(new \DateTime($this->timesince))->execute();
Thanks,
Jay
This looks like a typo as it's the same check on both sides of the ||. However I'm not sure what it should be:
https://github.com/calcinai/xero-php/blob/master/src/XeroPHP/Remote/Request.php#L95
Hey There,
Many thanks for this module, it's great! - I was wondeirng what the process was for creating multiple records at a time. I need to do an initial sync of our data to Xero, and don't want to do individual requests for each object.
Using your example for contacts:
$contact = new \XeroPHP\Models\Accounting\Contact();
$contact->setName('Test Contact') ->setFirstName('Test') ->setLastName('Contact') ->setEmailAddress('[email protected]');
How would I create multiple contacts in one request?
Cheers,
Ben
Is anyone opened to use PSR-2 coding standard?
how do you pull a report I don't see a model for it?
So far I haven't noticed any issues with the generated models. I was just thinking, this might be a more robust source of truth.
https://github.com/XeroAPI/XeroAPI-Schemas/blob/master/v2.00/Invoice.xsd
PHP Fatal error: Cannot use XeroPHP\Models\Files\Object as Object because the name is already in use in /.../vendor/calcinai/xero-php/src/XeroPHP/Remote/Query.php on line 7
The line is:
use XeroPHP\Models\Files\Object;
This is a bit of a mystery. If I run this statement before running a query (a Payment load with some where-conditions):
echo \XeroPHP\Models\Accounting\Payment::PAYMENT_TYPE_ACCRECPAYMENT;
Then the above error happens when the query is autoloaded. If I run that echo statement after running the query, I don't see this error. This feels like some vague PHP namespace "feature", and I guess comes from loading the classes in a different order.
https://scrutinizer-ci.com/ can be used for free on open source projects like this.
It does a lot of things https://scrutinizer-ci.com/tour/all-features-in-one-glance
In this instance https://github.com/calcinai/xero-php/blob/master/src/XeroPHP/Models/Accounting/Invoice.php#L357 the parameter here should be LineItem
not LineItem[]
.
Here's another example of the same bug https://github.com/calcinai/xero-php/blob/master/src/XeroPHP/Models/Accounting/Invoice.php#L408
The PSR-0 autoload entry is as follows:
"psr-0": { "": "src/" }
This will catch ALL namespaces and even classes without namespaces. This can slow an application down with additional filesystem checks that are not necessary. The entry should look like this:
"psr-0": { "XeroPHP": "src/" }
or even:
"psr-4": { "XeroPHP": "src/XeroPHP/" }
or use both of these at the same time.
If the namespace is changed as per ticket #45 I would recommend dropping PSR-0 support and just going for PSR-4:
"psr-4": { "Calcinai\\XeroPHP": "src/" }
In this case the XeroPHP
directory under src
would be dropped, and Application.php
and friends would go straight into src
. You don't have to drop that directory for PSR-4, but you can so you might as well.
The class XeroPHP\Models\Accounting\Attachment
extends Object
and therefore must implement the abstract method isPageable()
.
Adding:
static function isPageable()
{
return false;
}
should fix this issue.
Update the Application::save()
method to accept either an object or an array of objects.
Alternatively, make a saveBatch()
method to handle it.
The method needs to asses each object, and determine which ones can be in the batch and which should be updated individually.
Parsing method not implemented for [text/html]
The readme doesn't indicate that a calling execute()
is necessary for a load. However, by following the code, I think it is. But when I do that, I think the computed URL is not correct because it ends up loading an IIS server 404 page on the Xero server.
The following is taking from http://developer.xero.com/documentation/api/invoices/
If you are entering many invoices in a single API call then we recommend you utilise our new response format that shows validation errors for each invoice. The new response messages for validating bulk API calls would mean a breaking change so to utilise this functionality you’ll need to append ?SummarizeErrors=false to the end of your API calls e.g. POST /api.xro/2.0/Invoices?SummarizeErrors=false
Below is an example of the altered response format. Note that each Invoice is now returned with a status element which will either contain the value OK or ERROR. If an invoice has a error then one or more validation errors will be returned.
<Response>
<Invoices>
<Invoice status="OK">
...
</Invoice>
<Invoice status="ERROR">
<ValidationErrors>
<ValidationError>
<Message>....</Message>
</ValidationError>
.....
</ValidationErrors>
</Invoice>
<Invoice status="OK">
...
</Invoice>
</Invoices>
</Response>
Hi ya,
I am using a wrapper for this awesome package to work through Laravel. I have been reading up and can't seem to make a payment on an invoice so that it is marked as PAID.
$xero = App::make('XeroPrivate');
$xero_invoice = $xero->loadByGUID('Accounting\\Invoice', $invoice->guid);
$payment = App::make('XeroPayment');
$payment->setInvoice($xero_invoice);
$payment->setDate(Carbon::now());
$payment->setAmount($invoice->total);
$xero->save($payment);
The Payment object is being created and the Invoice is correct too. However, I can't seem to get the above to work. I get a BadRequestException
:
A validation exception occurred (An error occurred in Xero. Check the API Status page http://status.developer.xero.com for current service status. Contact the API support team at [email protected] for more assistance)
The status of Xero is fine. So I am guessing that something is amiss with the request?
I've been through the code of the package and can't seem to see a way to setPayments
on an invoice. Am I going about this the wrong way? Any tips on paying an invoice?
Thanks a lot for putting the package together.
I would have put this in the other issue but maybe that one can be merged into this one if it's the same problem.
I think initially had it working on testing by having a tracking option called 'Web' under a tracking category called 'Distribution' and using $line_item->setTracking('Web')
.
This doesn't seem to be working on the live client even with the same names for the TC/TO, I'm not sure exactly how setTracking()
is supposed to know what specific TrackingCategory/TrackingOption it is dealing with, here are some things I have tried as arguments for in setTracking()
According to the Xero API the object that gets send should look something like this:
<Tracking>
<TrackingCategory>
<Name>Activity/Workstream</Name>
<Option>Onsite consultancy</Option>
</TrackingCategory>
</Tracking>
I'm not sure how the library is figuring this out, I'd be happy with something like
$line_item
->setTrackingCategory($category_name)
->setTrackingOption($option_name);
//or, since apparently the API doesn't let you get tracking objects directly (??)
$tracking_category = $this->api->loadByGUID('Accounting\\TrackingCategory', $category_id);
$tracking_option = filter_function($option_id, $tracking_category);
$line_item->setTracking($tracking_option);
Am I missing something obvious, maybe it's as simple as Category\Option? So in this case it would be ->setTracking('Distribution\Web');
? Or something.
Sorry for ramble, semi rubber ducking here.
Hi,
After copy this example
https://github.com/calcinai/xero-php/blob/master/examples/public.php
when I receive answer from XERO
I see exception
Token (token from oauth_token var) does not match an expected REQUEST token
I tried contact with xero but their support doesn't know why.
Do you know what can be wrong? My app in XERO is public.
It would awesome if we can throw an exception in case you use page filter for models that doesn't support it.
I guess it applies to other restrictions as well.
When acting as a partner application, OAuth kicks in the first time and results in the OAuth callback being called. Are there any examples of what this callback handler would look like?
Hi Michael,
Thanks for the package.
Is there a way to remove existent items from invoice before updating?
Probably being dumb here but not getting anywhere with the usual prodding and poking.
Steps I took:
use XeroPHP\Application\PrivateApplication;
//These are the minimum settings - for more options, refer to examples/config.php
$config = array(
'oauth' => array(
'callback' => 'http://xero.local.io',
'consumer_key' => 'k',
'consumer_secret' => 's',
'rsa_private_key' => 'file://../certs/privatekey.pem',
'rsa_public_key' => 'file://../certs/publickey.cer'
)
);
$xero = new PrivateApplication($config);
print_r($xero->load('Accounting\\Organisation')->execute());
PHP message: PHP Fatal error: Uncaught exception 'XeroPHP\Exception' with message 'Curl error: name lookup timed out' ..snip../vendor/calcinai/xero-php/src/XeroPHP/Remote/Request.php:102
This is with PHP 5.5, Curl 7.39, nginx, php-fpm, ubuntu
Additional checks can be done with https://insight.sensiolabs.com/ it's free for open source projects
Hello,
Are you pretty set that this must support 5.3?
I was thinking maybe the oauth stuff could be swapped out for Guzzle / https://github.com/guzzle/oauth-subscriber
But that would need 5.4+
Nick
Sorry to bug you again so soon, but either I'm not doing something right, or editing phones is buggy.
Say I have the following piece of (pseudo)code:
$phone = find($contact->getPhones(), function ($p) => $p->getPhoneType() === 'DEFAULT');
$phone->setPhoneNumber($_POST['phone']);
$xero->save($contact);
After running that code, the new phone number is not persisted to Xero. I looked at the requests the save call was making and it was sending an empty <Phones></Phones>
element. I don't have an exact case to reproduce the error but I tried stuff like $contact->fromStringArray($contact->toStringArray())
or adding another DEFAULT
phone, and nothing persisted.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.