GithubHelp home page GithubHelp logo

langleyfoxall / xero-laravel Goto Github PK

View Code? Open in Web Editor NEW
83.0 7.0 41.0 453 KB

๐Ÿ’ธ Access the Xero accounting system using an Eloquent-like syntax

Home Page: https://packagist.org/packages/langleyfoxall/xero-laravel

License: GNU Lesser General Public License v3.0

PHP 100.00%
xero php laravel eloquent accounting

xero-laravel's Introduction

๐Ÿ’ธ Xero Laravel

Xero Laravel allows developers to access the Xero accounting system using an Eloquent-like syntax.

Please note that this version of Xero Laravel supports the Xero OAuth 2.0 implementation. Older Xero apps using OAuth 1.x are no longer supported.

StyleCI

Installation

Xero Laravel can be easily installed using Composer. Just run the following command from the root of your project.

composer require langleyfoxall/xero-laravel

If you have never used the Composer dependency manager before, head to the Composer website for more information on how to get started.

Setup

First, run the following artisan command from the root of your project. This will publish the package configuration file.

php artisan vendor:publish --provider="LangleyFoxall\XeroLaravel\Providers\XeroLaravelServiceProvider"

You now need to populate the config/xero-laravel-lf.php file with the credentials for your Xero app(s). You can create apps and find the required credentials in the My Apps section of your Xero account.

If you only intend to use one Xero app, the standard configuration file should be sufficient. All you will need to do is add the following variables to your .env file.

XERO_CLIENT_ID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XERO_CLIENT_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XERO_REDIRECT_URI=https://example.com/xero-callback

OAuth 2.0 flow

In order for users to make use of your Xero app, they must first give your app permission to access their Xero account. To do this, your web application must do the following.

  1. Redirect the user to the Xero authorization URL.
  2. Capture the response from Xero, and obtain an access token.
  3. Retrieve the list of tenants (typically Xero organisations), and let the user select one.
  4. Store the access token and selected tenant ID against the user's account for future use.
  5. Before using the access token, check if it has expired and refresh it if necessary.

The controller below shows these steps in action.

<?php
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use LangleyFoxall\XeroLaravel\OAuth2;
use League\OAuth2\Client\Token\AccessToken;

class XeroController extends Controller
{
    private function getOAuth2()
    {
        // This will use the 'default' app configuration found in your 'config/xero-laravel-lf.php` file.
        // If you wish to use an alternative app configuration you can specify its key (e.g. `new OAuth2('other_app')`).
        return new OAuth2();
    }

    public function redirectUserToXero()
    {
        // Step 1 - Redirect the user to the Xero authorization URL.
        return $this->getOAuth2()->getAuthorizationRedirect();
    }

    public function handleCallbackFromXero(Request $request)
    {
        // Step 2 - Capture the response from Xero, and obtain an access token.
        $accessToken = $this->getOAuth2()->getAccessTokenFromXeroRequest($request);
        
        // Step 3 - Retrieve the list of tenants (typically Xero organisations), and let the user select one.
        $tenants = $this->getOAuth2()->getTenants($accessToken);
        $selectedTenant = $tenants[0]; // For example purposes, we're pretending the user selected the first tenant.

        // Step 4 - Store the access token and selected tenant ID against the user's account for future use.
        // You can store these anyway you wish. For this example, we're storing them in the database using Eloquent.
        $user = auth()->user();
        $user->xero_access_token = json_encode($accessToken);
        $user->tenant_id = $selectedTenant->tenantId;
        $user->save();
    }

    public function refreshAccessTokenIfNecessary()
    {
        // Step 5 - Before using the access token, check if it has expired and refresh it if necessary.
        $user = auth()->user();
        $accessToken = new AccessToken(json_decode($user->xero_access_token));

        if ($accessToken->hasExpired()) {
            $accessToken = $this->getOAuth2()->refreshAccessToken($accessToken);

            $user->xero_access_token = $accessToken;
            $user->save();
        }
    }
}

By default, only a limited number of scopes are defined in the configuration file (space separated). You will probably want to add to the scopes depending on your application's intended purpose. For example adding the accounting.transactions scope allows you to manage invoices, and adding the accounting.contacts.read allows you to read contact information.

Xero's documentation provides a full list of available scopes.

Usage

To use Xero Laravel, you first need to get retrieve your user's stored access token and tenant id. You can use these to create a new XeroApp object which represents your Xero application.

use LangleyFoxall\XeroLaravel\XeroApp;
use League\OAuth2\Client\Token\AccessToken;

$user = auth()->user(); 

$xero = new XeroApp(
            new AccessToken(json_decode($user->xero_oauth_2_access_token)),
            $user->xero_tenant_id
        );

You can then immediately access Xero data using Eloquent-like syntax. The following code snippet shows the available syntax. When multiple results are returned from the API they will be returned as Laravel Collection.

# Retrieve all contacts
$contacts = $xero->contacts()->get();                               
$contacts = $xero->contacts;

# Retrieve contacts filtered by name
$contacts = $xero->contacts()->where('Name', 'Bank West')->get();

# Retrieve an individual contact filtered by name
$contact = $xero->contacts()->where('Name', 'Bank West')->first();

# Retrieve an individual contact by its GUID
$contact = $xero->contacts()->find('34xxxx6e-7xx5-2xx4-bxx5-6123xxxxea49');

# Retrieve multiple contact by their GUIDS
$contacts = $xero->contacts()->find([
    '34xxxx6e-7xx5-2xx4-bxx5-6123xxxxea49',
    '364xxxx7f-2xx3-7xx3-gxx7-6726xxxxhe76',
]);

Available relationships

The list below shows all available relationships that can be used to access data related to your Xero application (e.g. $xero->relationshipName).

Note: Some of these relationships may not be available if the related service(s) are not enabled for your Xero account.

accounts
addresses
assetsAssetTypeBookDepreciationSettings
assetsAssetTypes
assetsOverviews
assetsSettings
attachments
bankTransactionBankAccounts
bankTransactionLineItems
bankTransactions
bankTransferFromBankAccounts
bankTransferToBankAccounts
bankTransfers
brandingThemes
contactContactPeople
contactGroups
contacts
creditNoteAllocations
creditNotes
currencies
employees
expenseClaimExpenseClaims
expenseClaims
externalLinks
filesAssociations
filesFiles
filesFolders
filesObjects
invoiceLineItems
invoiceReminders
invoices
itemPurchases
itemSales
items
journalJournalLines
journals
linkedTransactions
manualJournalJournalLines
manualJournals
organisationBills
organisationExternalLinks
organisationPaymentTerms
organisationSales
organisations
overpaymentAllocations
overpaymentLineItems
overpayments
payments
payrollAUEmployeeBankAccounts
payrollAUEmployeeHomeAddresses
payrollAUEmployeeLeaveBalances
payrollAUEmployeeOpeningBalances
payrollAUEmployeePayTemplateDeductionLines
payrollAUEmployeePayTemplateEarningsLines
payrollAUEmployeePayTemplateLeaveLines
payrollAUEmployeePayTemplateReimbursementLines
payrollAUEmployeePayTemplateSuperLines
payrollAUEmployeePayTemplates
payrollAUEmployeeSuperMemberships
payrollAUEmployeeTaxDeclarations
payrollAUEmployees
payrollAULeaveApplicationLeavePeriods
payrollAULeaveApplications
payrollAUPayItemDeductionTypes
payrollAUPayItemEarningsRates
payrollAUPayItemLeaveTypes
payrollAUPayItemReimbursementTypes
payrollAUPayItems
payrollAUPayRuns
payrollAUPayrollCalendars
payrollAUPayslipDeductionLines
payrollAUPayslipEarningsLines
payrollAUPayslipLeaveAccrualLines
payrollAUPayslipLeaveEarningsLines
payrollAUPayslipReimbursementLines
payrollAUPayslipSuperannuationLines
payrollAUPayslipTaxLines
payrollAUPayslipTimesheetEarningsLines
payrollAUPayslips
payrollAUSettingAccounts
payrollAUSettingTrackingCategories
payrollAUSettings
payrollAUSuperFundProducts
payrollAUSuperFundSuperFunds
payrollAUSuperFunds
payrollAUTimesheetTimesheetLines
payrollAUTimesheets
payrollUSEmployeeBankAccounts
payrollUSEmployeeHomeAddresses
payrollUSEmployeeMailingAddresses
payrollUSEmployeeOpeningBalances
payrollUSEmployeePayTemplates
payrollUSEmployeePaymentMethods
payrollUSEmployeeSalaryAndWages
payrollUSEmployeeTimeOffBalances
payrollUSEmployeeWorkLocations
payrollUSEmployees
payrollUSPayItemBenefitTypes
payrollUSPayItemDeductionTypes
payrollUSPayItemEarningsTypes
payrollUSPayItemReimbursementTypes
payrollUSPayItemTimeOffTypes
payrollUSPayItems
payrollUSPayRuns
payrollUSPaySchedules
payrollUSPaystubBenefitLines
payrollUSPaystubDeductionLines
payrollUSPaystubEarningsLines
payrollUSPaystubLeaveEarningsLines
payrollUSPaystubReimbursementLines
payrollUSPaystubTimeOffLines
payrollUSPaystubTimesheetEarningsLines
payrollUSPaystubs
payrollUSSalaryandWages
payrollUSSettingAccounts
payrollUSSettingTrackingCategories
payrollUSSettings
payrollUSTimesheetTimesheetLines
payrollUSTimesheets
payrollUSWorkLocations
phones
prepaymentAllocations
prepaymentLineItems
prepayments
purchaseOrderLineItems
purchaseOrders
receiptLineItems
receipts
repeatingInvoiceLineItems
repeatingInvoiceSchedules
repeatingInvoices
reportBalanceSheets
reportBankStatements
reportBudgetSummaries
reportProfitLosses
reportReports
reportTaxTypes
salesTaxBases
salesTaxPeriods
taxRateTaxComponents
taxRates
taxTypes
trackingCategories
trackingCategoryTrackingOptions
userRoles
users

xero-laravel's People

Contributors

asheywood avatar dextermb avatar divineomega avatar jameswilddev avatar jordenpowleywebdev avatar lfnicklangley avatar lnch avatar parkourben99 avatar securit avatar shirish71 avatar zimonline avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

xero-laravel's Issues

How to update some record on Xero?

Hi,
How to send updates to Xero from laravel app. For example, someone buys some item and we like to update QuantityOnHand on Xero. How to do the update?

Missing scope for inital set up when requesting tenants, incorrect examples and other feedback

Hey, thanks for the the library, looks very nice - however I'm just setting it up and have some feedback:

  1. You need to add accounting.settings.read to the default provided scopes in config/xero-laravel-lf.php, because you get 0 tenants back from the initial set up call example you provide which had me floundering for a bit.
  2. Some documentation (or link to) about setting up the token in Xero would be helpful
  3. Clear step to add the fields you need in the DB
  4. Your example code for accessing data is incorrect and has inconsistent data:

You save the access token and tenant like so:

$user->xero_access_token = json_encode($accessToken);
$user->tenant_id = $selectedTenant->tenantId;

but then call it:

$xero = new XeroApp(
            new AccessToken(json_decode($user->xero_oauth_2_access_token)),
            $user->xero_tenant_id
        );

$user->xero_access_token != $user->xero_oauth_2_access_token.

And when you try to access the data, AccessToken takes an array like so:

$xero = new XeroApp(
            new AccessToken(['access_token' => new AccessToken(['access_token' => json_decode($user->xero_access_token)->access_token]),
            $user->xero_tenant_id
        );

Also note that the token gets saved as an object, so you need to pass the access_token param or cast $user->xero_access_token to an array I guess.

Anyway, thanks! Hopefully the rest is plain sailing :)

Xero.php

    public function app($key = 'default')
    {
        if (!isset($this->apps[$key])) {
            $this->apps[$key] = $this->createApp($key);
        }

        return $this->apps[$key];
    }

Is this worth converting to a construct on the class? That way don't have to call the app()?

You are not permitted to access this resource

I have the API setup and the OAuth flow working. I have linked up the "Demo Company UK" as the tenant, but I still get the "You are not permitted to access this resource" error. My scopes look correct from the docs, see below.

    'apps' => [
        'default' => [
            'client_id'     => env('XERO_CLIENT_ID'),
            'client_secret' => env('XERO_CLIENT_SECRET'),
            'redirect_uri'  => env('XERO_REDIRECT_URI'),
            'scope'         => 'openid email profile offline_access accounting.transactions accounting.contacts accounting.reports.read',
        ],
    ],

In the Xero account under the "Demo Company UK" organisation my user looks to have the correct privileges here as well.
Adviser โ€ข Contact bank account admin, Payroll admin โ€ข Expenses (Admin)

Test function to make a test call, with the error

    public function testXero() {
        $xeroAccessToken = GlobalSetting::where('name', '=', 'xero_access_token')->first();
        $xeroTenantOrganisation = GlobalSetting::where('name', '=', 'xero_tenant_organisation_id')->first();

        $xero = new XeroApp(
            new AccessToken(
                array(
                    'access_token' => json_decode($xeroAccessToken->value)->id_token
                )
            ), $xeroTenantOrganisation->value
        );
        //dd( $xero ); //we have a succesfull connection here...
        
        # Retrieve all contacts
        $contacts = $xero->contacts()->get();                               

        dd($contacts); //error "You are not permitted to access this resource".
    }

Does anybody have any ideas?

You are not permitted to access this resource

I got 'You are not permitted to access this resource' from the following code.
Is there any method to solve this issue?

$access_token_object = json_decode($access_token);

$xero = new XeroApp(
new AccessToken(
array(
'access_token' => $access_token_object->access_token,
'refresh_token' => $access_token_object->refresh_token,
'expires' => $access_token_object->expires,
)
),
$xero_tenantid
);

Invalid `state` when adding additional scopes

Hey there,

The default scopes openid email profile offline_access works fine.
However, when I try to add new scopes openid email profile offline_access accounting.transactions accounting.settings.read, I start getting an exception Invalid state. Request may have been tampered with.

Any ideas?

syntax error, unexpected '?', expecting variable (T_VARIABLE)

while I am want to call the function below, getting the error syntax error, unexpected '?', expecting variable (T_VARIABLE), using PHP version PHP 7.4.27
public function redirectUserToXero() { return $this->getOAuth2()->getAuthorizationRedirect(); }
error

Eloquent-like syntax to filter records in Xero API request

When filtering quotes (and other data), what is the syntax as it does work as per the contacts example?

For example:
$invoices = $xero->invoices()->where('InvoiceNumber', 9871)->get();

Renders the following:
/api.xro/2.0/Invoices?where=InvoiceNumber%3D%3D9871

Ultimately this should be:
/api.xro/2.0/Invoices?InvoiceNumber=9871

Invoice items (invoiceLineItems?) + bills not found

Hello,

I've been using this package and overall I good results as expected, but now I have a problem when I want to get all bills and get all items in invoices.

I'm not sure if I'm not using the package correctly, or there is something wrong with my Xero account data, or it's methods in package are outdated, but I get next:

If I try to use "invoiceLineItems" - I get "Resource not found".

When I get all invoices with "invoices", LineItems property is always null, even when I'm sure that I've added items on Xero invoice.

image

Another problem is that I can't find method for getting all bills, I tried with "organisationBills" but I get "Resource not found", too.

Pushing new data to Xero

I see throughout the docs that this gets data from Xero via the API. I just tried it, works great.

Does this package have the ability to push new data to Xero. ie. Create new contacts?

Auth refreshing

How are developers working around access token renewal, given that:

An access tokens expire after 30 minutes

I've just rolled my own form to initiate the OAuth flow, save all the relevant credentials to the db, then using the eloquent retrieved event to auto-renew the access token if it has expired. I also have to override the config before using the package.

I couldn't think of any other way to persist the access token.

I suppose another option would be to add client_id, client_secret, redirect_uri and refresh_token to .env and then optimistically / pessimistically fetch a new access token on every request (assuming you don't hit the Xero API all that often).

I can see how the multi-tenancy complicates things quite a bit.

Full documentation to use this package.

I get the basic idea of using this package, but it would be great if there is a detailed documentation of this package as there are so many scopes and using each of them is very different. I Explored the calcinai/xero-php and Xero api explorer but understanding of CREATE scope is much complicated than that of GET scope.

I am trying to convert the idea given on calcinai/xero-php 's README page:

$xero_auth = XeroAuth::where('admin_id', $request->admin_id)->first();
        $this->createXeroApp($xero_auth->access_token, $xero_auth->tenant_id);

        $line_item = new LineItem();
        
        $contact = $this->xero->loadByGUID(Contact::class, $xero_auth->contact_id);
        $invoice = new Invoice($this->xero);
        $invoice->setContact($contact)
                ->setType('ACCREC')
                ->addLineItem($line_item);
        $invoice->save();

Suppose I have multiple LineItems that I wanted to add in a single invoice. I know the values required to set a LineItem is { "Description": "Acme Tires", "Quantity": 2, "UnitAmount": 20, "AccountCode": "200", "TaxType": "NONE", "LineAmount": 40 } but there is no proper documentation to show how to set these values and there is no example exists.

Can you please at least help me in creating an invoice and save it in xero crm?

How to capture Oauth2 response for access token?

Hi,

I'm having trouble understanding how to set up the Oauth2 authorisation in a controller. I have created a XeroController and used the redirectUserToXero() function to get a redirect response back from Xero. However, I am unable to pass the redirect response received from redirectUserToXero() into the handleCallbackFromXero() function. Is there a step I'm missing? I'm unsure how to make the two functions connect.

[SUGGESTION] Remove need for config file

Hiya, just a suggestion to remove an install step and keep things cleaner would be to remove the need for the config file and just use laravels services config file, placing the information in there (obviously still referencing the env file).

Error handling

Hi,

How are we handling for errors?

$xero->invoices()->find($invoiceId); uses the calcinai/xero-php exceptions, however I would ideally want it to return null if no invoice is found instead of throwing an error?

Any examples would be appreciated without try catching each thing?

TIA.

Project Directory

Hi i am getting the following error
Argument 1 passed to LangleyFoxall\XeroLaravel\Utils::normalizePath() must be an instance of LangleyFoxall\XeroLaravel\string, string given, called in /var/www/html/xeroapp/vendor/langleyfoxall/xero-laravel/src/Utils.php on line 23

Thanks

Accessing models issue

I'm trying to access the models but it gives me error of bad method call. When i debug i was getting relationship like below
relationshipToModelMap: array:148 [โ–ผ "\Accounts" => "XeroPHP\Models\Accounting\Account", "\Addresses" => "XeroPHP\Models\Accounting\Address"]

and when i try $xero->contacts()->get(); it throws bad method call exception.

After debugging i found that when we call the contacts() it creates the key name 'contacts' but when it try to check for key in the relationshiptomodel array its not available and it throws exception.

For this issue i need to change in PrivateXeroApp.php method populateRelationshipToModelMap where its getting relationship by replacing below
$relationship = Str::camel($prefix.Str::plural(str_replace([$modelsDirectory, '.php', '/'], ['', '', ''], $filename)));
with
$relationship = Str::camel($prefix.Str::plural(str_replace([$modelsDirectory, '.php', '\\'], ['', '', ''], $filename)));

could you please tell me if i'm doing something wrong. I can't change directly in that file so could you please update in your repo. Thank you

Is there a bug in the demo README code?

There's some example code in the readme (very helpful). When you initially save a token as a result of the callback, there's this:

public function handleCallbackFromXero(Request $request) {
        // Lots of stuff omitted
        $accessToken = $this->getOAuth2()->getAccessTokenFromXeroRequest($request);
        $user->xero_access_token = json_encode($accessToken);
}

And then later on, when you save the refresh token:


    public function refreshAccessTokenIfNecessary()
    {
        // Step 5 - Before using the access token, check if it has expired and refresh it if necessary.
        $user = auth()->user();
        $accessToken = new AccessToken(json_decode($user->xero_access_token));

        if ($accessToken->hasExpired()) {
            $accessToken = $this->getOAuth2()->refreshAccessToken($accessToken);
            // Should this line use a json_encode() ?
            $user->xero_access_token = $accessToken;
            $user->save();
        }
    }

In the refreshAccessTokenIfNecessary method, should the access token be saved JSON encoded, or as a raw string like that? It is JSON encode when it is first saved.

Sort option

Is there a way to sort when retrieving credit notes and other objects?

Error when trying to retrieve contacts

Hello,i am new to Xero and i am trying to get my head around how to go about things,i am asking this here because i have exhausted my options .So i did everything well since i can get my app to request and get access tokens and tenant ID and choose an app from xero,thanks to a flawless documentation.When i got to the usage section,i created a route ;'about' just to test things and pasted this to its controller
`public function about()
{
$user = auth()->user();

$xero = new XeroApp(
new AccessToken(json_decode($user->xero_oauth_2_access_token)),
$user->xero_tenant_id
);
$contacts = $xero->contacts()->get();
dd($contacts);
}`
trying to access the about page,i get this error: Argument 1 passed to League\OAuth2\Client\Token\AccessToken::__construct() must be of the type array, null given, called in C:\xampp\htdocs\foodiez\app\Http\Controllers\HomeController.php on line 88
what could i be doing wrong.
Also to anyone who will respond to this,how do we add a contact to xero with the syntax offered by this package

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.