FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour Belgium VAT-Controle function
Posts: 1487
Joined: Tue Jun 14, 2016 07:51 AM
Belgium VAT-Controle function
Posted: Fri Mar 27, 2020 09:18 PM

Hello Michel, Marc

Have any of you have a working Vat-controle for Belgium vat numbers ? I want to check if new customers are having a correct vat number and maybe the option of filling in the customer data from online data.

Marc Venken

Using: FWH 23.08 with Harbour
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: Belgium VAT-Controle function
Posted: Fri Mar 27, 2020 09:30 PM

You could always use our Italian control if the customer VAT number is created in the same way also in Belgium
I fiund this

https://vatcalculator.eu/belgium-vat-calculator/
but you perhaps mean another vat number

I found VAT number format: BE 0999.999.999 (10 digits)

https://github.com/DragonBe/vies/issues/76
there is a php source code

https://github.com/DragonBe/vies/blob/m ... sponse.php

Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
Posts: 1467
Joined: Mon Oct 10, 2005 11:26 AM
Re: Belgium VAT-Controle function
Posted: Fri Mar 27, 2020 10:50 PM
Hi Marc,

This is the function I have made to check a Belgian VAT-number.
The picture of the VAT-number = "BE0999.999.999"

The code is :
Code (fw): Select all Collapse
FUNCTION Contr_BTW(nBTWNUM,nLAND,nDlg,nONDNUM,nBTWLIST) // Controle Belgisch BTW-nummer

   LOCAL OldPar   := ALIAS()
   LOCAL TN1      := 0
   LOCAL TN2      := 0
   LOCAL cTel     := 0
   LOCAL cRet     := .T.

   IF ALLTRIM(nLAND) = "B" .AND. !EMPTY(&nBTWNUM) .AND. ALLTRIM(&nBTWNUM) <> ".   ." .AND. LEFT(UPPER(&nBTWNUM),2) = "BE"

      &nBTWNUM := LEFT(ALLTRIM(&nBTWNUM),14)

      FOR x = 4 TO LEN(ALLTRIM(SUBSTR(&nBTWNUM,4,11)))
          IF (SUBSTR(ALLTRIM(&nBTWNUM),x,1) < "0" .OR. SUBSTR(ALLTRIM(&nBTWNUM),x,1) > "9") .AND. ((x <> 7 .AND. x <> 11) .OR. ((x = 7 .OR. x = 11) .AND. SUBSTR(ALLTRIM(&nBTWNUM),x,1) <> "."))
             cTel++
          ENDIF
      NEXT
      IF cTel = 0
         IF LEN(ALLTRIM(&nBTWNUM)) = 12
            &nBTWNUM := LEFT(SUBSTR(ALLTRIM(&nBTWNUM),1,6) + "." + SUBSTR(ALLTRIM(&nBTWNUM),7,3) + "." + RIGHT(ALLTRIM(&nBTWNUM),10,3) + SPACE(20),20)
            nDlg:Update()
         ENDIF
         TN1 := VAL(SUBSTR(ALLTRIM(&nBTWNUM),3,4) + SUBSTR(ALLTRIM(&nBTWNUM),8,3) + SUBSTR(ALLTRIM(&nBTWNUM),12,1))
         TN2 := (( TN1 / 97) - INT(TN1 / 97)) * 97
         IF !(cTel = 0 .AND. INT(VAL(SUBSTR(ALLTRIM(&nBTWNUM),13,2))) = 97 - INT(TN2 + 0.5))
            cRet := .F.
         ENDIF
      ELSEIF LEFT(UPPER(&nBTWNUM),2) = "BE"
         cRet := .F.
      ENDIF

      IF cRet
         IF LEFT(UPPER(ALLTRIM(&nBTWNUM)),2) = "BE"
            IF !EMPTY(nONDNUM) .AND. (EMPTY(&nONDNUM) .OR. ALLTRIM(&nONDNUM) = ".   .")
               &nONDNUM := RIGHT(ALLTRIM(&nBTWNUM),12)
               IF !EMPTY(nBTWLIST) .AND. !EMPTY(&nBTWNUM)
                  &nBTWLIST := .T.
               ENDIF
            ENDIF
            nDlg:Update()
         ENDIF
      ENDIF

      IF LEN(ALLTRIM(&nBTWNUM)) < 14
         &nBTWNUM := LEFT(ALLTRIM(&nBTWNUM) + SPACE(14),14)
         cRet := .F.
      ENDIF

      IF !cRet
        MsgAlert("Dit Belgisch BTW-nummer is niet correct !!!","Opgelet")
      ENDIF

   ENDIF

RETURN(cRet)
Good luck.

Regards,

Michel D.
Genk (Belgium)


_____________________________________________________________________________________________

I use : FiveWin for (x)Harbour v. 25.12 - Harbour 3.2.0 (May 2025) - xHarbour Builder (January 2020) - Bcc773

Posts: 1487
Joined: Tue Jun 14, 2016 07:51 AM
Re: Belgium VAT-Controle function
Posted: Fri Mar 27, 2020 11:27 PM
I have seen this code working I believe (don't know where I got it)
Now it's not ok anymore

Code (fw): Select all Collapse
#include "fivewin.ch"
#if ! defined( DEFAULT_MAX_RECORDS )
#define DEFAULT_MAX_RECORDS   20000
#endif

Static cDoc , cHttp

Function Main()


   TRY
      cDoc := CreateObject( "MSXML2.DOMDocument" )
   CATCH
      Alert("Error object MSXML2.DOMDocument : " + Ole2TxtError())
      return NIL
   END

   TRY
      cHttp := CreateObject( "MSXML2.XMLHTTP" )
   CATCH
      Alert("Error object MSXML2.XMLHTTP : " + Ole2TxtError())
   END

   checkVies( "BE", "0452109872" )


Return nil


//=========================================================================================
Function  checkVies(cCountry, cVatNumber )

Local cResponse := " " ,hVar
Local cRequestXML := ""
local aData:={}

DEFAULT cCountry := "BE"
DEFAULT cVatNumber := "0452109872"
editvars cVatnumber


 cRequestXML := [<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ] +;
                                    [xmlns:tns1="urn:ec.europa.eu:types" ] +;
                                    [xmlns:impl="urn:ec.europa.eu:checkVat"> ] +;
                    [<soap:Header> ]+;
                    [</soap:Header> ]+;
                         [<soap:Body> ]+;
                            [<tns1:checkVat xmlns:tns1="urn:ec.europa.eu:types" ]+;
                                            [xmlns="urn:ec.europa.eu:types"> ]+;
                            [<tns1:countryCode>] + cCountry + [</tns1:countryCode> ] +;
                            [<tns1:vatNumber>] + cVatNumber + [</tns1:vatNumber> ] +;
                            [</tns1:checkVat> ]+;
                         [</soap:Body> ]+;
                    [</soap:Envelope> ]


      //
       //cHttp:Open( "POST","http://ec.europa.eu/taxation_customs/vies/services/checkVatService", .t.)
      // Wait...
      cHttp:Open( "POST","http://ec.europa.eu/taxation_customs/vies/services/checkVatService", .F.)

      cHttp:SetRequestHeader( "Content-Type"    , "application/x-www-form-urlencoded" )
      cHttp:setRequestHeader('User-Agent', 'node-soap')
      cHttp:setRequestHeader('Accept' , 'text/html,application/xhtml+xml,application/xml,text/xml;q=0.9,*/*;q=0.8')
      cHttp:setRequestHeader('Accept-Encoding', 'none')
      cHttp:setRequestHeader('Accept-Charset', 'utf-8')
      //cHttp:setRequestHeader('Connection', 'close')
      //cHttp:setRequestHeader('Host', 'http://ec.europa.eu/taxation_customs/vies/services/checkVatService')
      cHttp:setRequestHeader('SOAPAction', 'urn:ec.europa.eu:checkVat/checkVat')

      //MsgGet( 'Wait',,@cRequestXML)
      //cDoc:LoadXML( cRequestXML )
      //lXmlHttp.send(lXmlDoc);
      cHttp:send(cRequestXML)
     //cHttp:send(cDOc:xml )
     //If cHttp:status == 200
     // ? cResponse
     cResponse := cHttp:responseText
     ? cResponse
     aadd(aData,BETWEENTAGSARRAY("name","/name",cResponse))
     aadd(aData,BETWEENTAGSARRAY("address","/address",cResponse))

     //xbrowser( BETWEENTAGSARRAY("faultstring","/faultstring",cResponse) )
     xbrowser( aData )
    //endif

RETURN NIL

  FUNCTION BETWEENTAGSARRAY( cStartTag, cEndTag, cInputString, lIncludeTags )

   LOCAL nStartPoint, nEndPoint
   LOCAL nRecords := 00, nFetchLength := 00, aFoundText := Array( DEFAULT_MAX_RECORDS )
   LOCAL cMDML
   LOCAL cInputStringUpper := Upper( cInputString )
   LOCAL cStartTagUpper    := Upper( cStartTag    )
   LOCAL cEndTagUpper      := Upper( cEndTag      )

   hb_Default( @lIncludeTags, .F. )

   DO WHILE .T.

      // Find the starting point of the starting tag.
      nStartPoint := At( cStartTagUpper, SubStr( cInputStringUpper, 01 ) )
      IF nStartPoint > 00

         // Adjust starting point to end of starting tag
         nStartPoint += Len( cStartTagUpper )

         // If the first tag is found strip off string up to and including the starting tag itself
         cInputStringUpper := SubStr( cInputStringUpper, nStartPoint )
         cInputString      := SubStr( cInputString,      nStartPoint )

         // Find the starting point of the second tag, beginning from end of first tag.
         nEndPoint := At( cEndTagUpper, cInputStringUpper )
         IF nEndPoint > 00

            // If the second tag is found calculate its position from start of string.
            nFetchLength := nEndPoint - 1

            IF lIncludeTags
               cMDML := cStartTag + LTrim( SubStr( cInputString, 01, nFetchLength ) ) + cEndTag
            ELSE
               cMDML := LTrim( SubStr( cInputString, 01, nFetchLength ) )
            ENDIF

            IF ++nRecords <= DEFAULT_MAX_RECORDS
               aFoundText[ nRecords ] := cMDML
            ELSE
               // IF we get here it is gonna be oh so slow.
               AAdd( aFoundText, cMDML )
            ENDIF

            // clip off the front of the string then loop to find the next
            cInputStringUpper := SubStr( cInputStringUpper, nFetchLength + 01 )
            cInputString      := SubStr( cInputString,      nFetchLength + 01 )

         ELSE
            EXIT
         ENDIF
      ELSE
         EXIT
      ENDIF
   ENDDO
   IF nRecords < DEFAULT_MAX_RECORDS
      aFoundText := ASize( aFoundText, nRecords )
   ENDIF

   RETURN ( aFoundText )
Marc Venken

Using: FWH 23.08 with Harbour
Posts: 1467
Joined: Mon Oct 10, 2005 11:26 AM
Re: Belgium VAT-Controle function
Posted: Fri Mar 27, 2020 11:41 PM

Marc,

Do you know the principle of checking a Belgian VAT-number?

A Belgian VAT-number looks like BE0574.774.488.

Take "BE0" at the start away. You keep 574.774.488.
Get rid of the points. You keep 574774488.
Take the first 7 digits. You keep 5747744.
Divide this result by 97. The result is 59255.0927835.
You keep the decimals : 0.0927835.
Multiply this by 97 and round it with 0 decimals.
The result is 9.
Substract 9 from 97 and this results into 88.
IF 88 is equal to the last 2 figures of you VAT-number, the Belgian VAT-number is correct.

This is what my function does.

Regards,

Michel D.
Genk (Belgium)


_____________________________________________________________________________________________

I use : FiveWin for (x)Harbour v. 25.12 - Harbour 3.2.0 (May 2025) - xHarbour Builder (January 2020) - Bcc773

Posts: 10733
Joined: Sun Nov 19, 2006 05:22 AM
Re: Belgium VAT-Controle function
Posted: Sat Mar 28, 2020 03:46 AM
driessen wrote:Marc,

Do you know the principle of checking a Belgian VAT-number?

A Belgian VAT-number looks like BE0574.774.488.

Take "BE0" at the start away. You keep 574.774.488.
Get rid of the points. You keep 574774488.
Take the first 7 digits. You keep 5747744.
Divide this result by 97. The result is 59255.0927835.
You keep the decimals : 0.0927835.
Multiply this by 97 and round it with 0 decimals.
The result is 9.
Substract 9 from 97 and this results into 88.
IF 88 is equal to the last 2 figures of you VAT-number, the Belgian VAT-number is correct.

This is what my function does.


I tried to implement the above logic in this function:
Code (fw): Select all Collapse
function BelgiumVat( cVat )

   local lValid := .f.

   if Left( cVat, 3 ) == "BE0" .and. Len( cVat ) == 14
      cVat     := SubStr( cVat, 4 )
      if SubStr( cVat, 4, 1 ) == "." .and. SubStr( cVat, 8, 1 ) == "."
         cVat  := StrTran( cVat, ".", "" )
         if cVat == Str( Val( cVat ), 9 )
            if 97 - Val( Left( cVat, 7 ) ) % 97 == Val( Right( cVat, 2 ) )
               lValid   := .t.
            endif
         endif
      endif
   endif
return lValid

If you have time can this be checked with different vat numbers?

Even this check is valid, there is no guarantee that the number does exist. Checking with the Government's website is the final check.
Regards



G. N. Rao.

Hyderabad, India

Continue the discussion