Giter Club home page Giter Club logo

twinfield's Introduction

Twinfield Build Status

A PHP library for Twinfield Integration. Use the Twinfield SOAP Services to have your PHP application communicate directly with your Twinfield account.

⚠️ Note that this library is not created or maintained by Twinfield. You can only get support on the code in this library here. For any questions related to your Twinfield administration or how to do certain things with the Twinfield API, contact your Twinfield account manager.

Installation

Install this Twinfield PHP library with Composer:

composer require 'php-twinfield/twinfield:^3.0'

Usage

Authentication

You need to set up a \PhpTwinfield\Secure\AuthenticatedConnection class with your credentials. When using basic username and password authentication, the \PhpTwinfield\Secure\WebservicesAuthentication class should be used, as follows:

Username and password

$connection = new Secure\WebservicesAuthentication("username", "password", "organization");

Some endpoints allow you to filter on the Office, but for instance the BrowseData endpoint doesn't. For this you need to switch to the correct office before making the request, you can do this after authentication like so:

$office = Office::fromCode("someOfficeCode");
$officeApi = new \PhpTwinfield\ApiConnectors\OfficeApiConnector($connection);
$officeApi->setOffice($office);

oAuth2

In order to use OAuth2 to authenticate with Twinfield, one should use the \PhpTwinfield\Secure\Provider\OAuthProvider to retrieve an \League\OAuth2\Client\Token\AccessToken object, and extract the refresh token from this object. Furthermore, it is required to set up a default \PhpTwinfield\Office, that will be used during requests to Twinfield. Please note: when a different office is specified when sending a request through one of the ApiConnectors, this Office will override the default.

Using this information, we can create an instance of the \PhpTwinfield\Secure\OpenIdConnectAuthentication class, as follows:

$provider    = new OAuthProvider([
    'clientId'     => 'someClientId',
    'clientSecret' => 'someClientSecret',
    'redirectUri'  => 'https://example.org/'
]);
$accessToken  = $provider->getAccessToken("authorization_code", ["code" => ...]);
$refreshToken = $accessToken->getRefreshToken();
$office       = \PhpTwinfield\Office::fromCode("someOfficeCode");

$connection  = new \PhpTwinfield\Secure\OpenIdConnectAuthentication($provider, $refreshToken, $office);

In the case you have an existing accessToken object you may pass that to the constructor or set it, to limit the amount of access token and validate requests, since the accessToken is valid for 1 hour.

$provider    = new OAuthProvider([
    'clientId'     => 'someClientId',
    'clientSecret' => 'someClientSecret',
    'redirectUri'  => 'https://example.org/'
]);
$accessToken  = $provider->getAccessToken("authorization_code", ["code" => ...]);
$refreshToken = $accessToken->getRefreshToken();
$office       = \PhpTwinfield\Office::fromCode("someOfficeCode");

$connection  = new \PhpTwinfield\Secure\OpenIdConnectAuthentication($provider, $refreshToken, $office, $accessToken);

or

$connection = new \PhpTwinfield\Secure\OpenIdConnectAuthentication($provider, $refreshToken, $office);
$connection->setAccessToken($accessToken);

Setting an access token will force a new validation request on every login.

It's also possible to provide callables, that will be called when a new access token is validated. This way you're able to store the new 'validated' access token object and your application can re-use the token within an hour. This way you can optimize the number of requests.

Be aware to store your access token in an appropriate and secure way. E.g. encrypting it.

$connection = new \PhpTwinfield\Secure\OpenIdConnectAuthentication($provider, $refreshToken, $office, $accessToken);
$connection->registerAfterValidateCallback(
    function(\League\OAuth2\Client\Token\AccessTokenInterface $accessToken) {
        // Store the access token.
    }
);

For more information about retrieving the initial AccessToken, please refer to: https://github.com/thephpleague/oauth2-client#usage

Getting data from the API

In order to communicate with the Twinfield API, you need to create an ApiConnector instance for the corresponding resource and use the get() or list() method.

The ApiConnector takes a Secure\AuthenticatedConnection object:

An example:

$connection = new Secure\WebservicesAuthentication("username", "password", "organization");
$customerApiConnector = new ApiConnectors\CustomerApiConnector($connection);

// Get one customer.
$office   = Office::fromCode('office code');
$customer = $customerApiConnector->get('1001', $office);

// Get a list of all customers.
$customer = $customerApiConnector->listAll($office);

Creating or updating objects

If you want to create or update a customer or any other object, it's just as easy:

$customer_factory = new ApiConnectors\CustomerApiConnector($connection);

// First, create the objects you want to send.
$customer = new Customer();
$customer
    ->setCode('1001')
    ->setName('John Doe')
    ->setOffice($office)
    ->setEBilling(false);

$customer_address = new CustomerAddress();
$customer_address
    ->setType('invoice')
    ->setDefault(false)
    ->setPostcode('1212 AB')
    ->setCity('TestCity')
    ->setCountry('NL')
    ->setTelephone('010-12345')
    ->setFax('010-1234')
    ->setEmail('[email protected]');
$customer->addAddress($customer_address);

// And secondly, send it to Twinfield.
$customer_factory->send($customer);

You can also send multiple objects in one batch, chunking is handled automatically.

Browse data

In order to get financial data out of Twinfield like general ledger transactions, sales invoices, and so on, you can use the the browse data functionality. More information about the browse data functionality in Twinfield can be found in the documentation.

Browse definition

You can retrieve the browse definition of a browse code as follows. You don't need to retrieve the browse definition for getting the browse data. It's only for viewing the browse definition of a browse code to know exactly which columns are available.

$connector = new BrowseDataApiConnector($connection);
$browseDefinition = $connector->getBrowseDefinition('000');

Browse fields

You can retrieve the browse fields as follows. You don't need to retrieve the browse fields for getting the browse data. It's only for viewing the definitions of all browse fields so you now what you can expect when retrieving browse data.

$connector = new BrowseDataApiConnector($connection);
$browseFields = $connector->getBrowseFields();

Browse data

You can retrieve browse data of a browse code as follows.

$connector = new BrowseDataApiConnector($connection);

// First, create the columns that you want to retrieve (see the browse definition for which columns are available)
$columns[] = (new BrowseColumn())
    ->setField('fin.trs.head.yearperiod')
    ->setLabel('Period')
    ->setVisible(true)
    ->setAsk(true)
    ->setOperator(Enums\BrowseColumnOperator::BETWEEN())
    ->setFrom('2013/01')
    ->setTo('2013/12');

$columns[] = (new BrowseColumn())
    ->setField('fin.trs.head.code')
    ->setLabel('Transaction type')
    ->setVisible(true);

$columns[] = (new BrowseColumn())
    ->setField('fin.trs.head.shortname')
    ->setLabel('Name')
    ->setVisible(true);

$columns[] = (new BrowseColumn())
    ->setField('fin.trs.head.number')
    ->setLabel('Trans. no.')
    ->setVisible(true);

$columns[] = (new BrowseColumn())
    ->setField('fin.trs.line.dim1')
    ->setLabel('General ledger')
    ->setVisible(true)
    ->setAsk(true)
    ->setOperator(Enums\BrowseColumnOperator::BETWEEN())
    ->setFrom('1300')
    ->setTo('1300');

$columns[] = (new BrowseColumn())
    ->setField('fin.trs.head.curcode')
    ->setLabel('Currency')
    ->setVisible(true);

$columns[] = (new BrowseColumn())
    ->setField('fin.trs.line.valuesigned')
    ->setLabel('Value')
    ->setVisible(true);

$columns[] = (new BrowseColumn())
    ->setField('fin.trs.line.description')
    ->setLabel('Description')
    ->setVisible(true);

// Second, create sort fields
$sortFields[] = new BrowseSortField('fin.trs.head.code');

// Get the browse data
$browseData = $connector->getBrowseData('000', $columns, $sortFields);

ApiConnector Configuration

The ApiConnector has a constructor second parameter that can be used to configure some aspects of its operation.

The ApiOptions has the following methods signature:

/**
 * This will allow you to enfornce the messages or the number of max retries.
 * Passing null you will use the default values.
 */
public function __construct(?array $messages = null, ?int $maxRetries = null);
/**
 * This will allow you to get all the exception messages
 */
public function getRetriableExceptionMessages(): array
/**
 * This will allow you to replace the exception messages that should be retried
 */
public function setRetriableExceptionMessages(array $retriableExceptionMessages): ApiOptions
/**
 * This will allow you to add new messages to the array of exception messages
 */
public function addMessages(array $messages): ApiOptions
/**
 * This will allow you to get the number of max retries
 */
public function getMaxRetries(): int
/**
 * This will allow you to set the number of max retries
 */
public function setMaxRetries(int $maxRetries): ApiOptions

❗ All the get methods will return a new instance with the configuration you changed.

Configuration Examples

Below are some examples on how to use the configuration object

$connector = new BrowseDataApiConnector(
    $connection,
    new ApiOptions(
        [
            "SSL: Connection reset by peer",
            "Bad Gateway"
        ], 
        3
    )
);

The example below will look for the default messages plus the "Bad Gateway" message.

$options = new ApiOptions(
    null, 
    3
);
$connector = new BrowseDataApiConnector(
    $connection,
    $options->addMessages(["Bad Gateway"])
);

Configuration default values

Attribute Default Value Description
Max retries 3 The number of retries that should happen before throwing an error.
Retriable exception messages [
"SSL: Connection reset by peer",
"Your logon credentials are not valid anymore. Try to log on again."
]
The exception messages that should be match in order to retry automatically.

Supported resources

Not all resources from the Twinfield API are currently implemented. Feel free to create a pull request when you need support for another resource.

Component get() listAll() send() delete() Mapper
Articles
BankTransaction
Customer
Electronic Bank Statements
Sales Invoices
Matching
Offices
Suppliers
Transactions:
Purchase, Sale, Journal, Cash
Users
Vat types
Browse Data

Links

Authors

twinfield's People

Contributors

alexjeen avatar arnoutdemooij avatar axlon avatar bart-mollie avatar casnelissen avatar casperboone avatar emilebons avatar john5 avatar manuelderuiter avatar mastercoding avatar mmolhoek avatar muhammedeminakbulut avatar mvdpanne avatar niektenhoopen avatar nodiscipline avatar remcotolsma avatar rickjeroen avatar robingeuze avatar rojtjo avatar stepan-popovych avatar stephangroen avatar superrembo avatar thijs-riezebeek avatar tychokamphuis-accommodatiehuur avatar versleijen avatar vswarte avatar willemstuursma avatar wimmie avatar wouter0100 avatar zogot 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

twinfield's Issues

Transaction line type should be set in the constructor

A transaction line should always have a type, and it should not be possible to change it. There are also some checks in other setters that depend on the line type being known. Best is to add it to the constructor.

Improvement make sure to honour the posting limits

When posting in batches to Twinfield (for instance multiple dimensions or multiple transactions). Make sure to honour it's element limitations:

https://c3.twinfield.com/webservices/documentation/#/GettingStarted/FUP

Advise is to limit the number of children within a parent to 25. So 25 elements within a <general> element, 25 elements within e.g. a <transactions> element and so on.

They are enforcing it starting this year :) Means that you can a SOAP error when you actually go over it with a number of %.

Incorrect return typehint for BankTransactionLine\Total

TypeError : Return value of PhpTwinfield\Transactions\BankTransactionLine\Total::setDebitCredit() must be an instance of PhpTwinfield\Transactions\TransactionLineFields\ValueFields, instance of PhpTwinfield\Transactions\BankTransactionLine\Total returned

You can't use traits as typehint.

Check the maximum length of fields

It would be nice if we would check the max length of fields before sending it to Twinfield. Twinfield returns an error if a field is too long.

We could either throw an exception as soon as the field is set, or we could truncate the fields before adding it to the XML file. I personally think that throwing an exception is preferable.

Create 1.0 release

I want to create a 1.0 release from the current develop branch. I might add some cleanups to the code, but it will be fully backwards compatible with the existing code. That also means it should run with php 5.4.

Things I want to do:

  • Add docblocks with @param and @return typehints to all methods that are part of the public interface of this library.
  • Fix the build.
  • Check if the readme file is up to date with the current code.

All existing users of this library should be encouraged to use this release.

Give example to add sales invoice and transactions(Purchase, Sale, Journal)

Dear Developers,

Actually it is not an issue but its my request.

Can you please add the example code for add invoice and transactions ? I have not found any example for that and i am new to twinfield, so please, please, please add some more examples like given in main page https://github.com/php-twinfield/twinfield so it will do great help to me and other newbies. Its my humble request to you.

Appreciate your work a lot. :)

make line IDs nullable

Twinfield doesn't require you to set line IDs manually:

<lines>
		<line type="total" id="1">
			<dim1>1010</dim1>
			<debitcredit>debit</debitcredit>
			<value>435.55</value>
		</line>
		<line type="detail" id="2">
			<dim1>1300</dim1>
			<dim2>1000</dim2>
			<debitcredit>credit</debitcredit>
			<value>435.55</value>
			<invoicenumber>11001770</invoicenumber>
			<description>Invoice paid</description>
		</line>
</lines>

These id attributes are optional. If you leave them empty, Twinfield will assign them and send them back in the return XML.

However, when I do not call setId() in my BankTransaction lines, then the client fails, because it always sets the id attribute.

Simply changing the code to

if ($line->getId() !== null) {
    $transaction->appendChild(new \DOMAttr("id", $line->getId()));
}

and allowing getId() to return null will make this work.

Autoload not working

Hi everyone,
I downloaded this project on my local computer using composer method, and then composer made the autoload.php file.

And then include the autoload file to my php script but I am getting class not found error.

Please see my below code

<?php require_once "vendor/autoload.php";
$config = new \PhpTwinfield\Secure\Config();

Error I am getting is

Fatal error: Class 'PhpTwinfield\Secure\Config' not found in C:\xampp\htdocs\twinfield\index.php on line 3

Is your autoload not working ? Or I have some error on it ? Can you please guide me ?

Documentation is very poor

First of all, your work for twinfield is much appreciated. But the documentation for the php is very poor. I am still not found where the secret key for twinfield? Can you give more detailed function with proper response.

For ex:

$empsode='1002';
$customer = $customerApiConnector->get($empcode);

When call this i get the error like this.

(1/1) FatalThrowableErrorType error: Too few arguments to function PhpTwinfield\ApiConnectors\CustomerApiConnector::get(), 1 passed in C:\xampp\htdocs\twinfield\app\Http\Controllers\ApiController.php on line 90 and exactly 2 expected

Instead of this we have to pass the office object as well on this call.

$office = new Office();
$office->setCode($officecode);
$customerApiConnector = new \PhpTwinfield\ApiConnectors\CustomerApiConnector($login); //Get the detail of ONLY /ONE customer
$customer = $customerApiConnector->get($empcode, $office);

when call like this than we get the object.

PhpTwinfield\Customer Object ( [code:PhpTwinfield\Customer:private] => 1002 [UID:PhpTwinfield\Customer:private] => 440a8044-7743-4876-9e77-d1a800378a51 [status:PhpTwinfield\Customer:private] => active [name:PhpTwinfield\Customer:private] => Anand Pandey [type:PhpTwinfield\Customer:private] => DEB [inUse:PhpTwinfield\Customer:private] => false [behaviour:PhpTwinfield\Customer:private] => normal [touched:PhpTwinfield\Customer:private] => 3 [beginPeriod:PhpTwinfield\Customer:private] => 0 [beginYear:PhpTwinfield\Customer:private] => 0 [endPeriod:PhpTwinfield\Customer:private] => 0 [endYear:PhpTwinfield\Customer:private] => 0 [website:PhpTwinfield\Customer:private] => www.demo.com [cocNumber:PhpTwinfield\Customer:private] => [vatNumber:PhpTwinfield\Customer:private] => [editDimensionName:PhpTwinfield\Customer:private] => true [dueDays:PhpTwinfield\Customer:private] => 30 [payAvailable:PhpTwinfield\Customer:private] => [payCode:PhpTwinfield\Customer:private] => [vatCode:PhpTwinfield\Customer:private] => [eBilling:PhpTwinfield\Customer:private] => [eBillMail:PhpTwinfield\Customer:private] => [creditManagement:PhpTwinfield\Customer:private] => PhpTwinfield\CustomerCreditManagement Object ( [responsibleUser:PhpTwinfield\CustomerCreditManagement:private] => [baseCreditLimit:PhpTwinfield\CustomerCreditManagement:private] => 0.00 [sendReminder:PhpTwinfield\CustomerCreditManagement:private] => 1 [reminderEmail:PhpTwinfield\CustomerCreditManagement:private] => [blocked:PhpTwinfield\CustomerCreditManagement:private] => [freeText1:PhpTwinfield\CustomerCreditManagement:private] => [freeText2:PhpTwinfield\CustomerCreditManagement:private] => 007 [freeText3:PhpTwinfield\CustomerCreditManagement:private] => [comment:PhpTwinfield\CustomerCreditManagement:private] => ) [addresses:PhpTwinfield\Customer:private] => Array ( [1] => PhpTwinfield\CustomerAddress Object ( [ID:PhpTwinfield\CustomerAddress:private] => 1 [type:PhpTwinfield\CustomerAddress:private] => invoice [default:PhpTwinfield\CustomerAddress:private] => true [name:PhpTwinfield\CustomerAddress:private] => Anand [contact:PhpTwinfield\CustomerAddress:private] => [country:PhpTwinfield\CustomerAddress:private] => IN [city:PhpTwinfield\CustomerAddress:private] => Indore [postcode:PhpTwinfield\CustomerAddress:private] => 452001 [telephone:PhpTwinfield\CustomerAddress:private] => [fax:PhpTwinfield\CustomerAddress:private] => [email:PhpTwinfield\CustomerAddress:private] => [email protected] [field1:PhpTwinfield\CustomerAddress:private] => [field2:PhpTwinfield\CustomerAddress:private] => lig [field3:PhpTwinfield\CustomerAddress:private] => indore [field4:PhpTwinfield\CustomerAddress:private] => [field5:PhpTwinfield\CustomerAddress:private] => [field6:PhpTwinfield\CustomerAddress:private] => ) ) [banks:PhpTwinfield\Customer:private] => Array ( ) [groups:PhpTwinfield\Customer:private] => [office:PhpTwinfield\Customer:private] => PhpTwinfield\Office Object ( [code:PhpTwinfield\Office:private] => NLA000068 [countryCode:PhpTwinfield\Office:private] => [name:PhpTwinfield\Office:private] => ) )

Oauth login

Hi,
When i use the oauth way of signing in to twinfield i get an error. This is my code:

public function login(\Illuminate\Http\Request $request) { $clientID = $request->get('clientID'); $clientSecret = $request->get('clientSecret'); $returnURL = $request->get('returnURL'); $Organization = $request->get('Organization'); $config = new \PhpTwinfield\Secure\Config(); $config->setOAuthParameters($clientID, $clientSecret, $returnURL, $Organization, true); $connection = new \PhpTwinfield\Secure\Connection($config); $logs = new \PhpTwinfield\Services\LoginService(); $newval = $logs->getSessionIdAndCluster($config); print_r($newval); }

error is: sorry, temp_token not set

regards

The `sendAll()` method in ApiConnectors should send in chunks of 25 objects

According to the Twinfield documentation there are some limits about how many objects can be sent at once:

Advise is to limit the number of children within a parent to 25. So 25 elements within a <general> element, 25 elements within e.g. a <transactions> element and so on. The maximum number of lines within a <transaction>/<lines> or <salesinvoice>/<lines> element is 500. As noted, no guarantee is given that XML messages exceeding these limits will be processed without problems.

Refactor client

  • Client should be able to re-use SoapClient over multiple requests
  • Client should support batching
  • Generic sending object would be great, lots of logic is now duplicated in the ApiConnectors.
  • Client should exception on errors

Start and close value should not be mandatory

The opening and closing balance for bank transactions and electronic bank statements (implemented in the StartAndCloseValueFields trait) should not be mandatory. From the docs:

Electronic bank statements

  • startvalue: Opening balance. If not provided, the opening balance will be based on the previous bank statement.
  • closevalue: Closing balance. If not provided, the closing balance will be based on the opening balance and the total amount of the transactions.

Bank transactions

  • startvalue: Opening balance. If not provided, the opening balance is set to zero.
  • closevalue: Closing balance. If not provided, the closing balance is set to zero.

Making these fields optional helps us by not having to keep track of the current balance for bank accounts.

There should be a separate setter for the currency, which is now set in setStartvalue().

Also note that ElectronicBankStatement::setTransactions() and BankTransaction::addLine() currently require the startvalue or closevalue to be set.

Not get the response after send new customer.

Hello fellow developers,

This is the code in which i want to create the new customer and it will also create when i loggedin with twinfield account UI, but not send the proper response that customer will create or not?
Please help me how can i do this?

Thanks in advance.

`
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use PhpTwinfield\Secure\Login;
use PhpTwinfield\Office;
use \PhpTwinfield\Services\LoginService;
use Exception;

public function createnewcustomer(Request $request){
$this->validate($request, [
'user' => 'required',
'password' => 'required',
'organisation' => 'required',
'empcode' => 'required',
'name' => 'required',
'postalcode' => 'required',
'city' => 'required',
'country' => 'required',
'email' => 'required'
]);
$user = $request->input('user');
$password = $request->input('password');
$org = $request->input('organisation');
$setcode = $request->input('empcode');
$name = $request->input('name');
$postalcode = $request->input('postalcode');
$fax = $request->input('fax');
$city = $request->input('city');
$phone = $request->input('telephone');
$country = $request->input('country');
$email = $request->input('email');
$setebill = $request->input('setEBilling');
$setebillmail = $request->input('setEBillMail');
$setvat = $request->input('setVatCode');
$setduedays = $request->input('setDueDays');
$setcocno = $request->input('setCocNumber');//It is depricated and use in address field5
$settype = $request->input('setType');
try
{
$config = new \PhpTwinfield\Secure\Config();
$config->setCredentials($user, $password , $org , '');
$login = new \PhpTwinfield\Secure\Login($config);
$customerFactory = new \PhpTwinfield\ApiConnectors\CustomerApiConnector($login);
// First, create the objects you want to send.

        $customer = new \PhpTwinfield\Customer();
        $customer
            ->setCode($setcode)
            ->setName($name)
            ->setType('DEB')
            ->setEBilling($setebill)
            ->setEBillMail($setebillmail)
            ->setDueDays($setduedays);
            
        $customerAddress = new \PhpTwinfield\CustomerAddress();
        $customerAddress 
            ->setType($settype)
            ->setDefault(false)
            ->setPostcode($postalcode)
            ->setCity($city)
            ->setCountry($country)
            ->setTelephone($phone)
            ->setField4($setvat)
            ->setField5($setcocno)
            ->setFax($fax)
            ->setEmail($email);
        $customer->addAddress($customerAddress);

        // And secondly, send it to Twinfield.
      $response =  $customerFactory->send($customer);
     print_r($response);

`

Both currently supported authentication protocols are deprecated

Twinfield offers OpenID now and the other methods (password and OAuth v1.0) are now deprecated. End of life is somewhere in 2018. Right now it also uses a LoginService to retrieve the session. When using OAuth 2.0 this is also not needed anymore. Also the OAuth v1.0 version is really poorly written (uses Javascript for a redirect)

The library will need to refactored significantly to provide the changes for OAuth. I propose keeping version 1.0 backwards compatible and leaving in the old authentication ways.

In 2.0 release, we should remove all the old mechanismes and implement OAuth:

https://c3.twinfield.com/webservices/documentation/#/ApiReference/Authentication/OpenIdConnect#General-information

I propose using thephpleageu/oauth2/client library for this:

https://github.com/thephpleague/oauth2-client

Sending a sales invoice returns a null response

I create a sales invoice object and send it to twinfield classic,
If i go to my twinfield classic > invoices section then i can see the invoice that i just created from laravel.
I use the code below to send my invoice.

$result = $invoiceApiConnector->send($invoice);
dd($result);

I would expect to get a complete invoice object back, according the twinfield API docs the result is an invoice object which includes the generated invoice_number which i need to generate a PDF in laravel.

Am i wrong in assuming i would get a response back?

This my full code, using the facade by willemo:

$officesApiConnector = \Twinfield::get('Office');
		
$offices = $officesApiConnector->listAll();
$office = $offices[0];
		
$customerApiConnector = \Twinfield::get('Customer');
$customer = $customerApiConnector->get('1001', $office);
		
$invoiceApiConnector = \Twinfield::get('Invoice');
		
$line = new \PhpTwinfield\InvoiceLine();
$line->setQuantity(1.00);
$line->setArticle(0);
$line->setUnitsPriceExcl(100.00);
$line->setUnits(1);
$line->setVatCode("VN");
$line->setDim1(8020);
$line->setDescription("Test");
$line->setAllowDiscountOrPremium(false);
		
$invoice = new \PhpTwinfield\Invoice();
$invoice->setCustomer($customer);
$invoice->setOffice($office);
$invoice->addLine($line);
$invoice->setStatus("concept");
$invoice->setPaymentMethod("bank");
$invoice->setInvoiceType("FACTUUR");
$invoice->setInvoiceDate("20180104");
$invoice->setCurrency("EUR");
$invoice->setBank("BNK");
$invoice->setPeriod('2018/01');
$invoice->setDueDate(\Carbon\Carbon::now()->addMonth());

// Send it to Twinfield.
$result = $invoiceApiConnector->send($invoice);
		
dd($result);
	
return view('index');

Update documentation for base example

Existing getting started gives some errors

$config = new \PhpTwinfield\Secure\Config();
$config->setCredentials('username', 'password', 'enviroment', 'office code');

// setup login
$login = new \PhpTwinfield\Secure\Login($config);

// get office
$office = \PhpTwinfield\Office::fromCode('officecode');

// get a list of all customers.
$customerApiConnector = new \PhpTwinfield\ApiConnectors\CustomerApiConnector($login);
$customerList = $customerApiConnector->listAll($office);

I also think you should make all office parameters optional, if it's not passed you can get the office code from the Secure/Config object (last parameter).

wrong code

When i install with composer i get the old code from pronamic.

Fix build

The build is currently not running. Make sure the tests run for PHP 5.4 to PHP 7.1.

`\PhpTwinfield\BankTransaction::addLine()` should not add to closeValue for line type 'total'

WHen calling \PhpTwinfield\BankTransaction::addLine(), this method adds the value of the line to the closeValue.

However, when adding a line of type Total this should not be done, because the total is just a summation of what's already in the Detail lines.

This currently leads to being unable to create a BankTransaction with a correct closing balance, unless the total line amounts to 0.

Support both ProcessXml and Finder service for one resource

The ApiConnectors should not extend from either ProcessXmlApiConnector or FinderApiConnector. Instead, there should be a ProcessXml service and a Finder service that both can be used by the same ApiConnector.

It is currently not possible to have a send() method (which uses the ProcessXml service) and a listAll() method (which uses the Finder service) in one ApiConnector.

TwinfieldReference interface

It would be nice if the library provided a TwinfieldReference interface that can be used when matching. Should look something like:

interface TwinfieldReference
{
    public function getOffice();

    public function getCode();

    public function getNumber();

    public function getLineNumber();
}

New customer not create

When i create the new customer with the given code. The error occur:

FatalThrowableError Call to undefined method PhpTwinfield\Customer::setType()

Seems the 'settype' method is not present in Customer.php. Please update the readme file as well, i am unable to update.

`$config = new \PhpTwinfield\Secure\Config();
$config->setCredentials($user, $password , $org , '');
$connection = new \PhpTwinfield\Secure\Connection($config);
$customer_factory = new ApiConnectors\CustomerApiConnector($connection);
// First, create the objects you want to send.
$customer = new Customer();

$customer = new \PhpTwinfield\Customer();
$customer
->setCode($setcode)
->setName($name)
->setType('DEB')
->setEBilling($setebill)
->setEBillMail($setebillmail)
->setDueDays($setduedays);

$customerAddress = new \PhpTwinfield\CustomerAddress();
$customerAddress
->setType($settype)
->setDefault(false)
->setPostcode($postalcode)
->setCity($city)
->setCountry($country)
->setTelephone($phone)
->setField4($setvat)
->setField5($setcocno)
->setFax($fax)
->setEmail($email);
$customer->addAddress($customerAddress);

// And secondly, send it to Twinfield.
$customerFactory->send($customer);`

DebitCredit field is wrong for lines of 'total' type

According to the docs:

If line type = total

  • In case of a 'normal' sales transaction debit.
  • In case of a credit sales transaction credit.

If line type = detail or vat

  • In case of a 'normal' sales transaction credit.
  • In case of a credit sales transaction debit.

Note that the values are switched for total lines.

Currently, when you set the value of a transaction using setValue(), the debitcredit field is also set. But for total lines it is not set correctly.

Total lines should always be the first line

When creating the XML that is send to Twinfield, the total lines should always be put first. It is currently in the same order as the lines are added to the transaction object.

Twinfield returns an error when the total line is not the first line.

Proposal to keep library compatible with PHP 7.0

I would propose to keep the library compatible with 7.0, we have a lot of machines still running the latest version of this. If this library only works for PHP 7.1 >= we would need to update all servers.

But it's up to you!

Rename and cleanup factories

The factories are not really factories. They are facades.

Rename all factories and move them all to a new \Twinfield\ApiConnector namespace.

Response::isSuccessful() returns always false for Electronic Bank Statements

Electronic Bank Statements are sent in a batch, and the result="1" from the response is set on each individual bank statement:

<?xml version="1.0"?>
<statements target="electronicstatements" importduplicate="0">
    <statement target="electronicstatements" result="1">
        <!-- ... -->
    </statement>
    <!-- etc... -->
</statements>

The Response::isSuccessful() method doesn't find the result="1" in the root element and returns false. This causes an exception even though the request was successful.

Probably the best way to implement this is to verify for all <statement /> tags that they are successful.

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.