Delphi Clinic | C++Builder Gate | Training & Consultancy | Delphi Notes Weblog | Dr.Bob's Webshop |
|
Checking EU VAT numbers via SOAP
In this article, I'll describe how to check European VAT numbers using a SOAP Web Services with Delphi XE2.
The same techniques can be used with older versions of Delphi as well (but I'm just using the latest-of-the-latest at the time of writing).
EU Rules
Before we start to "talk" technical, let me first explain the need: why do I need to check the validity of the VAT number of a company in Europe?
This has to do with the European Union and the VAT rules for the EU.
As you all probably know, I'm a reseller of Embarcadero Delphi and RAD Studio (as well as other software tools such as Prism / Oxygene, RemObjects SDK, and Hydra from RemObjects Software, TMS IntraWeb Components from TMS Software, and
For all electronic deliveries, I have to charge the local (Dutch) amount of 19% VAT for persons or companies in the European Union. Outside the EU, I do not have to charge VAT.
However, I also do not have to charge 19% VAT for companies in the EU (except for my own country The Netherlands) that provide their full company name and VAT number.
In these cases, I have to put their VAT number (as well as mine) on the invoice, and refer to EU rules for "VAT shifting".
I also have to report these VAT numbers (and the amounts) to the local Dutch tax office every quarter (possibly so they can cross-reference it with the local tax office of the associated EU countries).
This is where is gets tricky: if I accidentally report an incorrect VAT number, I have to pay the VAT myself.
So, to cut a long story short: it's important for me to verify the VAT number of every order from the EU to ensure that I do not have to charge 19% VAT.
VAT Number validation
The process of checking the validity of a VAT number is not that hard, but requires information from the different EU countries.
There is a website that can can help, and offers an interacive online form to check the VAT number.
However, that requires some manual input, which is not as easy (especially if the rest of the ordering and delivery process is automated).
Fortunately, the above website also publishes a SOAP web service that we can call.
The WSDL for this web service is available at http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl, and it contains a useful documentation tag with the following contents:
Specific disclaimer for this serviceThis information:
Usage: The countryCode input parameter must follow the pattern [A-Z]{2}
In case of problem, the returned FaultString can take the following specific values:
|
VAT Number Validation Web Service
Start Delphi - I'm using XE2, but any recent version should be good enough.
Since I'm using Delphi XE2, I'll create a FireMonkey application, so I can cross-compile the resulting client as a Mac OS X application and run it on my Mac Mini (just because I can).
So, after having started Delphi XE2, I do File | New - Other, and select the "FireMonkey HD Application" icon from the Object Repository:
This will produce a new FireMonkey HD Application. I've saved the form in MainForm.pas and the project itself in CheckVAT.dpr. The Caption of the MainForm is set to "CheckVAT", and I've placed a TEdit (called edVAT) and a TButton (called btnVAT) on the form:
Note that I made sure to place the TEdit and TButton on the Form, and not on each other (see the Structure View on the left side to confirm).
The next step consists of importing the Web Service. This is done using the WSDL Importer, found in the Object Repository. So, do File | New - Other again, and this time from the Web Services cateogory, double-click on the WSDL Importer:
In the dialog that follows, we can enter the WSDL URL, which is http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl as mentioned before.
Now press Next, Next and Finish to produce the import unit for the checkVatPortType web service. The interface definition for checkVatPortType is as follows:
checkVatPortType = interface(IInvokable) ['{0F901373-2432-32E2-C99D-95B53AE83C79}'] // Cannot unwrap: // - More than one strictly out element was found function checkVat(const parameters: checkVat): checkVatResponse; stdcall; // Cannot unwrap: // - More than one strictly out element was found function checkVatApprox(const parameters: checkVatApprox): checkVatApproxResponse; stdcall; end;
Never mind the "cannot unwrap" warnings, we can just create an instance of the "checkVat" input parameter and get the "checkVatResponse" back as result when calling the checkVat method.
The checkVat request structure is defined as follows:
checkVat = class(TRemotable) private FcountryCode: string; FvatNumber: string; public constructor Create; override; published property countryCode: string read FcountryCode write FcountryCode; property vatNumber: string read FvatNumber write FvatNumber; end;
Note that we should enter two different fields here: the countryCode as well as the vatNumber. So my VAT number "NL 195846266.B01" becomes "NL" for the countryCode, and "195846266B01" (without the dot or space) as the vatNumber.
The checkVatResponse response structure is defined as follows:
checkVatResponse = class(TRemotable) private FcountryCode: string; FvatNumber: string; FrequestDate: TXSDate; Fvalid: Boolean; Fname_: string; Fname__Specified: boolean; Faddress: string; Faddress_Specified: boolean; procedure Setname_(Index: Integer; const Astring: string); function name__Specified(Index: Integer): boolean; procedure Setaddress(Index: Integer; const Astring: string); function address_Specified(Index: Integer): boolean; public constructor Create; override; destructor Destroy; override; published property countryCode: string read FcountryCode write FcountryCode; property vatNumber: string read FvatNumber write FvatNumber; property requestDate: TXSDate read FrequestDate write FrequestDate; property valid: Boolean read Fvalid write Fvalid; property name_: string Index (IS_OPTN or IS_NLBL) read Fname_ write Setname_ stored name__Specified; property address: string Index (IS_OPTN or IS_NLBL) read Faddress write Setaddress stored address_Specified; end; companyTypeCode = type string;
Note that I'm not really interested in all these result fields. In fact, I only want to make sure the VAT number is a valid VAT number, and for that I should check the valid property (and optionally the vatNumber return field, as well as the address for example).
VAT Number Validation
The code to check the VAT number, in the OnClick event of the TButton, is as follows (note that we need to free both the "inp" and "outp" when we're done):
procedure TFormVAT.btnVATClick(Sender: TObject); var WS: checkVatPortType; inp: checkVat; outp: checkVatResponse; begin WS := GetcheckVatPortType; inp := checkVat.Create; try inp.countryCode := Copy(edVAT.Text,1,Pos(#32,edVAT.Text)-1); inp.vatNumber := Copy(edVAT.Text,Pos(#32,edVAT.Text)+1,255); outp := WS.checkVat(inp); if outp.valid then ShowMessage('OK: ' + outp.name_ + #13#10 + outp.address) else ShowMessage(outp.vatNumber + ' not OK') finally inp.Free; outp.Free end end;
And the result can be seen below (just before finishing the complete VAT number and clicking on the "Check VAT" button):
Delphi XE2 SOAP Clients can compile and work on Windows as well as Mac OS X. And since I made a FireMonkey HD Application, it's easy to add another Platform Target to the project group, and recompile the application for Mac OS X. Running on my Mac Mini, the resulting project can be seen below:
Using this little application, I can now verify VAT numbers on Windows and Mac OS X, calling the VAT number web service.
Summary
In this article, I've described how to check European VAT numbers using a SOAP Web Services with Delphi XE2.
I also demonstrated that Delphi XE2 SOAP clients can compile and work in both Windows and Mac OS X.
This article is translated to Serbo-Croatian language by Jovana Milutinovich from Webhostinggeeks.com, and it's translated to the Punjabi language by the Bydiscountcodes Team.