<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Perfonec Computers</title>
    <description>The latest articles on DEV Community by Perfonec Computers (@perfonec_computers).</description>
    <link>https://dev.to/perfonec_computers</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4104218%2Ffddc1160-ba63-4e42-8cc6-754cf60946cd.jpg</url>
      <title>DEV Community: Perfonec Computers</title>
      <link>https://dev.to/perfonec_computers</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/perfonec_computers"/>
    <language>en</language>
    <item>
      <title>A Small Python Script to Catch E-Invoice Data Problems Before They Reach the Network</title>
      <dc:creator>Perfonec Computers</dc:creator>
      <pubDate>Mon, 02 Nov 2026 11:13:34 +0000</pubDate>
      <link>https://dev.to/perfonec_computers/a-small-python-script-to-catch-e-invoice-data-problems-before-they-reach-the-network-3319</link>
      <guid>https://dev.to/perfonec_computers/a-small-python-script-to-catch-e-invoice-data-problems-before-they-reach-the-network-3319</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I used AI to help draft this article. I reviewed the content and tested the script myself. I work at an accounting software partner in Dubai, and this post is a general tutorial.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The UAE is moving to structured electronic invoices. Instead of emailing a PDF, a system produces invoice data in a defined format, and an accredited service provider sends it to the customer. The &lt;a href="https://mof.gov.ae/en/about-us/initiatives/einvoicing/einvoicing-accredited-service-providers-asps/" rel="noopener noreferrer"&gt;Ministry of Finance publishes the list of accredited providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In practice, most rejected invoices are not caused by software bugs. They come from bad master data: a customer with no tax number, an item called "Services", an address stored in one text box. This post shows a small script that finds these problems in a CSV export before you send anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the script checks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Customer tax registration numbers (TRN) exist and are 15 digits&lt;/li&gt;
&lt;li&gt;Customer address has a street, a city, and a country&lt;/li&gt;
&lt;li&gt;Item names are not generic words such as "Services" or "Goods"&lt;/li&gt;
&lt;li&gt;Unit codes come from an allowed list&lt;/li&gt;
&lt;li&gt;VAT category codes come from an allowed list&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The data
&lt;/h2&gt;

&lt;p&gt;Export two CSV files from your accounting system. &lt;code&gt;customers.csv&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name,trn,street,city,country
Alpha Trading LLC,100123456789012,Sheikh Zayed Road,Dubai,AE
Beta Logistics,,Industrial Area 3,Sharjah,AE
Gamma Foods,12345,,Abu Dhabi,AE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;items.csv&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name,unit,vat_category
Monthly IT support retainer,HUR,S
Services,Box,S
Steel pipe 6 metre,MTR,Z
Goods,EA,X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The script
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="c1"&gt;# Edit these to match the specification your provider gives you.
&lt;/span&gt;&lt;span class="n"&gt;GENERIC_NAMES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;services&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;goods&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;misc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;other&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;supply&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;ALLOWED_UNITS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C62&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KGM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LTR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MTR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MTQ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HUR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DAY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;ALLOWED_VAT_CATEGORIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;E&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;O&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;TRN_PATTERN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^\d{15}$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# UAE TRNs are 15 digits
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_customers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;problems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row_no&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;row &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row_no&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;trn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;trn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;problems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;missing TRN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;TRN_PATTERN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;problems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TRN is not 15 digits: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;trn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;street&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                    &lt;span class="n"&gt;problems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;missing &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;problems&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;problems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row_no&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;row &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;row_no&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;GENERIC_NAMES&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;problems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item name too generic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ALLOWED_UNITS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;problems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unit &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; is not an allowed code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vat_category&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ALLOWED_VAT_CATEGORIES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;problems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VAT category &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; not recognised&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;problems&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;all_problems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;check_customers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;check_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_problems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_problems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; issue(s) found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python validate_data.py customers.csv items.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the sample data above, the output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Beta Logistics: missing TRN
Gamma Foods: TRN is not 15 digits: 12345
Gamma Foods: missing street
Services: item name too generic
Services: unit 'BOX' is not an allowed code
Goods: item name too generic
Goods: VAT category 'X' not recognised

7 issue(s) found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Limits worth knowing
&lt;/h2&gt;

&lt;p&gt;This script checks format only. It does not confirm that a TRN belongs to the customer, and it does not replace the validation your accredited provider performs. The allowed lists at the top are examples. Use the code lists in the specification your provider supplies, and update them when the specification changes.&lt;/p&gt;

&lt;p&gt;Start with customers you invoice most often. Fixing a small group of high-volume records usually removes most of the problem.&lt;/p&gt;

&lt;p&gt;If you adapt this for your own system, I would be interested to hear which checks you added.&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>automation</category>
      <category>beginners</category>
    </item>
    <item>
      <title>QBESync Setup Guide — UAE E-Invoicing for QuickBooks Desktop and Online</title>
      <dc:creator>Perfonec Computers</dc:creator>
      <pubDate>Tue, 27 Oct 2026 11:05:31 +0000</pubDate>
      <link>https://dev.to/perfonec_computers/qbesync-setup-guide-uae-e-invoicing-for-quickbooks-desktop-and-online-3ef4</link>
      <guid>https://dev.to/perfonec_computers/qbesync-setup-guide-uae-e-invoicing-for-quickbooks-desktop-and-online-3ef4</guid>
      <description>&lt;p&gt;Disclosure: I am Akanksha Surana, CEO of Perfonec Computers in Dubai. Perfonec developed QBESync — the UAE Peppol e-invoicing connector for QuickBooks. We are the Authorised QuickBooks Dealer and Pro Advisor in UAE. This guide is written to help UAE QuickBooks users understand the complete setup process.&lt;/p&gt;

&lt;p&gt;The Problem — QuickBooks Has No Native UAE Peppol Support&lt;/p&gt;

&lt;p&gt;UAE mandatory e-invoicing requires every VAT-registered business to send B2B invoices through the UAE Peppol network in PINT AE XML format from 2027. QuickBooks does not have this built in.&lt;/p&gt;

&lt;p&gt;QBESync is consequently the connector that bridges QuickBooks and the UAE Peppol network. Without it — or another approved connector — UAE QuickBooks users cannot comply with the mandatory requirement.&lt;/p&gt;

&lt;p&gt;This guide covers the complete QBESync setup — all 9 steps, the full PINT AE data audit with field-by-field QuickBooks fixes, the 7-invoice pilot testing checklist, and what happens at go-live.&lt;/p&gt;

&lt;p&gt;Quick Reference&lt;br&gt;
Product:        QBESync — UAE Peppol e-invoicing connector for QuickBooks&lt;br&gt;
Developer:      Perfonec Computers — Authorised QuickBooks Dealer UAE&lt;br&gt;
QB versions:    Desktop 2017–2024 | QuickBooks Online | QB Enterprise&lt;br&gt;
Setup time:     4 to 8 weeks (data audit is the longest step)&lt;br&gt;
Pilot phase:    Open from 1 Jul 2026 — ZERO fines during testing&lt;br&gt;
Deadline 1:     1 Jan 2027 — revenue above AED 50M&lt;br&gt;
Deadline 2:     1 Jul 2027 — all other VAT-registered SMEs&lt;br&gt;
Contact:        +971 4 386 6199 | &lt;a href="mailto:sales@perfonec.com"&gt;sales@perfonec.com&lt;/a&gt;&lt;br&gt;
Website:        &lt;a href="http://www.qbeinvoice.com" rel="noopener noreferrer"&gt;www.qbeinvoice.com&lt;/a&gt;&lt;br&gt;
Architecture — How QBESync Works&lt;/p&gt;

&lt;p&gt;Before the setup steps, here is what QBESync actually does under the hood:&lt;/p&gt;

&lt;p&gt;QuickBooks (Desktop or Online)&lt;br&gt;
        │&lt;br&gt;
        │  Invoice confirmed&lt;br&gt;
        ▼&lt;br&gt;
    QBESync&lt;br&gt;
        │&lt;br&gt;
        ├── Reads invoice data via SDK API (Desktop) or REST API (Online)&lt;br&gt;
        ├── Maps fields to PINT AE XML schema (17 mandatory fields)&lt;br&gt;
        ├── Validates XML against UAE PINT AE business rules&lt;br&gt;
        ├── Signs and packages for Peppol transmission&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
    ASP (Approved Service Provider)&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
    UAE Peppol Network&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
    Buyer's ERP / Accounting System&lt;br&gt;
        │&lt;br&gt;
        ▼&lt;br&gt;
    Delivery confirmation → back to QBESync → status updated in QuickBooks&lt;/p&gt;

&lt;p&gt;QB Desktop connection: Local SDK API — more stable, faster for bulk data operations during setup.&lt;/p&gt;

&lt;p&gt;QB Online connection: Intuit REST API (OAuth 2.0) — cloud-based, suitable for remote teams.&lt;/p&gt;

&lt;p&gt;The 17 Mandatory PINT AE Fields&lt;/p&gt;

&lt;p&gt;Every UAE B2B invoice must contain these 17 fields in the PINT AE XML. Missing or incorrect values cause rejection at the ASP or Peppol network level.&lt;/p&gt;

&lt;p&gt;BT-1    Invoice number&lt;br&gt;
BT-2    Invoice issue date&lt;br&gt;
BT-3    Invoice type code (380 = invoice, 381 = credit note)&lt;br&gt;
BT-5    Invoice currency code&lt;br&gt;
BT-25   Preceding invoice reference (credit notes only)&lt;br&gt;
BT-29   Seller TRN (Tax Registration Number)&lt;br&gt;
BT-35   Seller street address&lt;br&gt;
BT-36   Seller city&lt;br&gt;
BT-38   Seller country code (AE)&lt;br&gt;
BT-46   Buyer TRN&lt;br&gt;
BT-50   Buyer street address&lt;br&gt;
BT-51   Buyer city&lt;br&gt;
BT-53   Buyer country code&lt;br&gt;
BT-110  Invoice total VAT amount&lt;br&gt;
BT-112  Invoice total amount with VAT&lt;br&gt;
BT-130  Item unit code (UN/CEFACT)&lt;br&gt;
BT-151  VAT category code (S / Z / E / O)&lt;br&gt;
BT-152  VAT rate (5 / 0)&lt;br&gt;
BT-153  Item description&lt;br&gt;
All 9 Setup Steps&lt;br&gt;
Step 1 — Version Check (Day 1)&lt;br&gt;
QB Desktop 2017    ✅ Supported — local SDK API&lt;br&gt;
QB Desktop 2018    ✅ Supported — local SDK API&lt;br&gt;
QB Desktop 2019    ✅ Supported — local SDK API&lt;br&gt;
QB Desktop 2020    ✅ Supported — local SDK API&lt;br&gt;
QB Desktop 2021    ✅ Supported — local SDK API&lt;br&gt;
QB Desktop 2022    ✅ Supported — local SDK API&lt;br&gt;
QB Desktop 2023    ✅ Supported — local SDK API&lt;br&gt;
QB Desktop 2024    ✅ Supported — local SDK API&lt;br&gt;
QB Online          ✅ Supported — Intuit REST API&lt;br&gt;
QB Enterprise      ✅ Supported — confirm with Perfonec&lt;br&gt;
QB Desktop 2016-   ❌ Not supported — upgrade required&lt;/p&gt;

&lt;p&gt;Perfonec runs this check on Day 1 at no cost.&lt;/p&gt;

&lt;p&gt;Step 2 — PINT AE Data Audit (Week 1–3)&lt;/p&gt;

&lt;p&gt;This is the longest step and the most important. Between 20 and 40 percent of UAE QuickBooks customer records are typically missing BT-46 (buyer TRN). Furthermore, generic item descriptions, unstructured addresses, and non-standard unit codes all cause schema validation failure.&lt;/p&gt;

&lt;p&gt;Here is the full audit table — what to check in QuickBooks, the common problem, and the fix:&lt;/p&gt;

&lt;p&gt;BT-46 — Buyer TRN&lt;/p&gt;

&lt;p&gt;Where:   Customer record → Tax Registration No field&lt;br&gt;
Problem: Missing from 20–40% of UAE customer records&lt;br&gt;
Fix:     Collect UAE TRN from all VAT-registered customers&lt;br&gt;
Risk:    HIGH — invoice rejects immediately without buyer TRN&lt;/p&gt;

&lt;p&gt;BT-153 — Item Description&lt;/p&gt;

&lt;p&gt;Where:   Products and Services → Name field&lt;br&gt;
Problem: Services, Goods, Item, Consulting — too generic&lt;br&gt;
Fix:     Rename to specific: "IT Support Monthly",&lt;br&gt;
         "Steel Pipe 48mm x 6m", "Warehouse Storage per Pallet"&lt;br&gt;
Risk:    HIGH — FTA business rules reject generic descriptions&lt;/p&gt;

&lt;p&gt;BT-130 — Unit Code&lt;/p&gt;

&lt;p&gt;Where:   Products → Unit of Measure field&lt;br&gt;
Problem: Each, Box, Lot, Piece — not valid UN/CEFACT codes&lt;br&gt;
Fix:     Map to UN/CEFACT standard codes:&lt;br&gt;
         EA  = each/unit&lt;br&gt;
         KGM = kilogram&lt;br&gt;
         MTR = metre&lt;br&gt;
         LTR = litre&lt;br&gt;
         HUR = hour&lt;br&gt;
         DAY = day&lt;br&gt;
         MTQ = cubic metre&lt;br&gt;
Risk:    HIGH — schema validation fails on invalid unit codes&lt;/p&gt;

&lt;p&gt;BT-35 — Seller Street Address&lt;/p&gt;

&lt;p&gt;Where:   Company settings → address fields&lt;br&gt;
Problem: Full address stored as one unstructured text block&lt;br&gt;
Fix:     Split into street name, city, country code (AE)&lt;br&gt;
Risk:    MEDIUM — address structure required by PINT AE schema&lt;/p&gt;

&lt;p&gt;BT-50 — Buyer Street Address&lt;/p&gt;

&lt;p&gt;Where:   Customer record → billing address&lt;br&gt;
Problem: Full address in one text block per customer&lt;br&gt;
Fix:     Split into street, city, country code&lt;br&gt;
         across every customer record&lt;br&gt;
Risk:    MEDIUM — same schema requirement as seller address&lt;/p&gt;

&lt;p&gt;BT-151 — VAT Category Code&lt;/p&gt;

&lt;p&gt;Where:   Tax code on each product or service&lt;br&gt;
Problem: Standard 5% applied to zero-rated or exempt supplies&lt;br&gt;
Fix:     S = Standard rate 5%&lt;br&gt;
         Z = Zero-rated (international freight, exports)&lt;br&gt;
         E = Exempt&lt;br&gt;
         O = Out of scope (outside UAE VAT)&lt;br&gt;
Risk:    HIGH — wrong category causes FTA rejection&lt;/p&gt;

&lt;p&gt;Free Zone Beneficiary Field&lt;/p&gt;

&lt;p&gt;Where:   Customer record → custom free zone flag&lt;br&gt;
Problem: Missing for JAFZA, DMCC, IFZA, DIFC, RAKEZ buyers&lt;br&gt;
Fix:     Tag all free zone customers — QBESync populates&lt;br&gt;
         the PINT AE free zone field automatically&lt;br&gt;
Risk:    HIGH — free zone invoices reject without this field&lt;/p&gt;

&lt;p&gt;BT-5 — Currency / AED Equivalent&lt;/p&gt;

&lt;p&gt;Where:   Invoice currency setting&lt;br&gt;
Problem: AED equivalent missing on USD and EUR invoices&lt;br&gt;
Fix:     No manual fix needed — QBESync calculates and&lt;br&gt;
         populates AED equivalent automatically per V1.1&lt;br&gt;
Risk:    LOW — QBESync handles this automatically&lt;br&gt;
Step 3 — Data Quality Fixes (Week 2–4)&lt;/p&gt;

&lt;p&gt;The client team works through the fix list Perfonec produces after the audit. Perfonec orders fixes by rejection risk — HIGH risk first, MEDIUM second, LOW last.&lt;/p&gt;

&lt;p&gt;Typical fix timeline by business size:&lt;/p&gt;

&lt;p&gt;Small business   (&amp;lt;100 customers, &amp;lt;50 products):  3–5 days&lt;br&gt;
Medium business  (100–500 customers):             1–2 weeks&lt;br&gt;
Large business   (500+ customers, many products): 2–3 weeks&lt;/p&gt;

&lt;p&gt;The bottleneck is almost always collecting missing buyer TRNs — not renaming items or fixing addresses. Getting TRNs from customers requires chasing accounts contacts. Consequently, start this step as early as possible.&lt;/p&gt;

&lt;p&gt;Step 4 — ASP Registration (Week 2–3)&lt;/p&gt;

&lt;p&gt;Perfonec registers the business with an Approved Service Provider. The ASP provisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Company Peppol participant ID&lt;/li&gt;
&lt;li&gt;Peppol network endpoint (SMP registration)&lt;/li&gt;
&lt;li&gt;Digital certificate for invoice signing&lt;/li&gt;
&lt;li&gt;ASP connection credentials for QBESync configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perfonec handles this step entirely. The client team does not interact with the ASP directly. Registration takes 3 to 5 business days after submission.&lt;/p&gt;

&lt;p&gt;Step 5 — QBESync Installation and Configuration (Week 3–4)&lt;/p&gt;

&lt;p&gt;QB Desktop installation:&lt;/p&gt;

&lt;p&gt;bash&lt;/p&gt;

&lt;h1&gt;
  
  
  QBESync installs as a Windows service
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Connects to QuickBooks via local SDK API
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Requires QuickBooks to be open on the same machine
&lt;/h1&gt;

&lt;h1&gt;
  
  
  or accessible on the same local network
&lt;/h1&gt;

&lt;p&gt;QBESync_config.json settings covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ASP endpoint URL and credentials&lt;/li&gt;
&lt;li&gt;Company TRN&lt;/li&gt;
&lt;li&gt;VAT code mappings (S / Z / E / O)&lt;/li&gt;
&lt;li&gt;Free zone customer tags&lt;/li&gt;
&lt;li&gt;Multi-currency AED equivalent rules&lt;/li&gt;
&lt;li&gt;Invoice type settings (all 7 types)&lt;/li&gt;
&lt;li&gt;Incoming Peppol invoice endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;QB Online connection:&lt;/p&gt;

&lt;p&gt;OAuth 2.0 flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Perfonec initiates QBESync → QB Online OAuth&lt;/li&gt;
&lt;li&gt;Client authorises in QB Online&lt;/li&gt;
&lt;li&gt;QBESync stores refresh token&lt;/li&gt;
&lt;li&gt;REST API polling interval configured&lt;/li&gt;
&lt;li&gt;ASP webhook endpoint configured&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Configuration takes 1 to 2 days once all data fixes are complete.&lt;/p&gt;

&lt;p&gt;Step 6 — Pilot Phase Testing (Week 4–6)&lt;/p&gt;

&lt;p&gt;The pilot phase opened 1 July 2026. Zero fines apply to any failure during pilot. The same failure after go-live triggers AED 100 per invoice.&lt;/p&gt;

&lt;p&gt;Test all 7 invoice types:&lt;/p&gt;

&lt;p&gt;Type 1 — Standard B2B invoice&lt;/p&gt;

&lt;p&gt;How:    Raise sales invoice to UAE VAT-registered test customer&lt;br&gt;
Check:  PINT AE XML generated ✓&lt;br&gt;
        All 17 fields populated ✓&lt;br&gt;
        Schema validation passed ✓&lt;br&gt;
        Peppol status: Delivered ✓&lt;/p&gt;

&lt;p&gt;Type 2 — Credit note (type 381)&lt;/p&gt;

&lt;p&gt;How:    Issue credit memo in QuickBooks against test invoice&lt;br&gt;
Check:  BT-3 = 381 ✓&lt;br&gt;
        BT-25 = original invoice number ✓&lt;br&gt;
        Peppol status: Delivered ✓&lt;/p&gt;

&lt;p&gt;Type 3 — Advance payment invoice&lt;/p&gt;

&lt;p&gt;How:    Raise deposit invoice before full order invoice&lt;br&gt;
Check:  Advance payment flag per V1.1 ✓&lt;br&gt;
        Correct amounts and VAT treatment ✓&lt;/p&gt;

&lt;p&gt;Type 4 — Zero-rated export invoice&lt;/p&gt;

&lt;p&gt;How:    Raise invoice to overseas buyer (country code ≠ AE)&lt;br&gt;
Check:  BT-151 = Z ✓&lt;br&gt;
        BT-152 = 0 ✓&lt;br&gt;
        BT-53 ≠ AE ✓&lt;br&gt;
        VAT amount = 0 ✓&lt;/p&gt;

&lt;p&gt;Type 5 — Free zone invoice&lt;/p&gt;

&lt;p&gt;How:    Raise invoice to JAFZA or DMCC test customer&lt;br&gt;
Check:  Free zone beneficiary field populated ✓&lt;br&gt;
        Peppol status: Delivered ✓&lt;/p&gt;

&lt;p&gt;Type 6 — Multi-currency invoice (USD / EUR)&lt;/p&gt;

&lt;p&gt;How:    Raise USD or EUR invoice to international customer&lt;br&gt;
Check:  BT-5 = USD or EUR ✓&lt;br&gt;
        AED equivalent amounts populated ✓&lt;br&gt;
        Exchange rate applied per V1.1 ✓&lt;/p&gt;

&lt;p&gt;Type 7 — Incoming supplier Peppol invoice&lt;/p&gt;

&lt;p&gt;How:    Ask test supplier to send Peppol invoice to your endpoint&lt;br&gt;
Check:  QBESync receives XML ✓&lt;br&gt;
        Validates against PINT AE schema ✓&lt;br&gt;
        Creates bill in QuickBooks ✓&lt;br&gt;
Step 7 — Fix Pilot Issues (Week 5–7)&lt;/p&gt;

&lt;p&gt;Every rejection during pilot produces a specific error code. The most common rejection codes and fixes:&lt;/p&gt;

&lt;p&gt;BT-46 missing or invalid&lt;br&gt;
→ Check customer TRN — re-collect if wrong&lt;/p&gt;

&lt;p&gt;BT-153 fails business rules&lt;br&gt;
→ Item description still too generic — rename&lt;/p&gt;

&lt;p&gt;BT-130 invalid unit code&lt;br&gt;
→ UN/CEFACT mapping missed — re-map&lt;/p&gt;

&lt;p&gt;BT-151 / BT-152 mismatch&lt;br&gt;
→ VAT category and rate do not match — fix tax code&lt;/p&gt;

&lt;p&gt;Free zone beneficiary missing&lt;br&gt;
→ Customer not tagged as free zone — tag and retest&lt;/p&gt;

&lt;p&gt;AED equivalent calculation error&lt;br&gt;
→ Exchange rate source misconfigured — fix in QBESync config&lt;/p&gt;

&lt;p&gt;Most pilot issues resolve within 1 to 3 days of receiving the rejection report.&lt;/p&gt;

&lt;p&gt;Step 8 — Team Training (Week 6–7)&lt;/p&gt;

&lt;p&gt;Perfonec trains the accounts team on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Normal invoice flow after go-live&lt;br&gt;
(no change — confirm invoice as usual)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checking Peppol delivery status in QuickBooks&lt;br&gt;
(new status field on every B2B invoice)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handling a rejection&lt;br&gt;
(how to read the rejection reason and fix)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Raising credit note type 381&lt;br&gt;
(different from standard credit memo — walk-through)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Incoming supplier Peppol invoice workflow&lt;br&gt;
(how to find, review, and post to QuickBooks)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Training takes half a day. Perfonec provides a written reference guide for the accounts team afterwards.&lt;/p&gt;

&lt;p&gt;Step 9 — Go-Live (Week 7–8)&lt;/p&gt;

&lt;p&gt;After all 7 types pass pilot and training is complete — go live.&lt;/p&gt;

&lt;p&gt;Go-live checklist:&lt;br&gt;
  ✅ All 7 invoice types passed pilot&lt;br&gt;
  ✅ Team training complete&lt;br&gt;
  ✅ ASP production credentials active&lt;br&gt;
  ✅ QBESync switched from sandbox to production&lt;br&gt;
  ✅ First live B2B invoice confirmed and transmitted&lt;br&gt;
  ✅ Delivery confirmation received from buyer&lt;br&gt;
  ✅ Incoming supplier Peppol endpoint tested live&lt;/p&gt;

&lt;p&gt;Perfonec monitors the first two weeks of production — watching delivery status, resolving any production issues, and confirming the first live invoice of each type transmits correctly.&lt;/p&gt;

&lt;p&gt;UAE E-Invoicing Deadlines&lt;br&gt;
Revenue &amp;gt; AED 50M:&lt;br&gt;
  ASP deadline:   30 Oct 2026&lt;br&gt;
  Go-live:        1 Jan 2027&lt;br&gt;
  Fines begin:    2 Jan 2027&lt;/p&gt;

&lt;p&gt;All other VAT SMEs:&lt;br&gt;
  ASP deadline:   31 Mar 2027&lt;br&gt;
  Go-live:        1 Jul 2027&lt;br&gt;
  Fines begin:    2 Jul 2027&lt;/p&gt;

&lt;p&gt;Pilot phase (all businesses):&lt;br&gt;
  Open from:      1 Jul 2026&lt;br&gt;
  Fines:          ZERO during pilot&lt;/p&gt;

&lt;p&gt;Fines after deadline:&lt;br&gt;
  Non-implementation: AED 5,000/month (no cap)&lt;br&gt;
  Per non-compliant invoice: AED 100 (capped AED 5,000/month)&lt;br&gt;
  Maximum combined: AED 10,000/month&lt;br&gt;
Common Questions&lt;/p&gt;

&lt;p&gt;Why does the data audit take longer than the technical setup?&lt;/p&gt;

&lt;p&gt;The technical setup is predictable — 1 to 2 days regardless of business size. The data audit depends on years of QuickBooks usage and data entry habits. Furthermore, collecting missing buyer TRNs requires chasing customer contacts — which is outside Perfonec’s control. Consequently, starting early is the most impactful thing a UAE QuickBooks business can do right now.&lt;/p&gt;

&lt;p&gt;QB Desktop or QB Online for QBESync?&lt;/p&gt;

&lt;p&gt;QB Desktop is consequently recommended for higher invoice volumes and larger customer databases — the local SDK API is more stable and bulk data editing during the audit is significantly faster. QB Online works well for lower-volume businesses and remote teams.&lt;/p&gt;

&lt;p&gt;What if a customer refuses to provide their TRN?&lt;/p&gt;

&lt;p&gt;If a UAE VAT-registered buyer refuses to provide their TRN — the invoice cannot go through Peppol. The responsibility to provide a TRN is on the buyer. Furthermore, without their TRN, the invoice will reject at the ASP validation stage regardless of all other fields being correct. Consequently, collecting TRNs from all UAE VAT-registered customers before go-live is a commercial priority — not just a technical one.&lt;/p&gt;

&lt;p&gt;Resources&lt;/p&gt;

&lt;p&gt;Full guide with downloadable PDF:&lt;br&gt;
&lt;a href="https://perfonec.com/qbesync-setup-guide-uae-einvoicing-quickbooks/" rel="noopener noreferrer"&gt;https://perfonec.com/qbesync-setup-guide-uae-einvoicing-quickbooks/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What Is QBESync:&lt;br&gt;
&lt;a href="https://perfonec.com/what-is-qbesync-uae-peppol-quickbooks/" rel="noopener noreferrer"&gt;https://perfonec.com/what-is-qbesync-uae-peppol-quickbooks/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;UAE E-Invoicing Complete Guide:&lt;br&gt;
&lt;a href="https://perfonec.com/uae-einvoicing-2027-complete-business-guide/" rel="noopener noreferrer"&gt;https://perfonec.com/uae-einvoicing-2027-complete-business-guide/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free readiness check:&lt;br&gt;
📞 +971 4 386 6199&lt;br&gt;
📧 &lt;a href="mailto:sales@perfonec.com"&gt;sales@perfonec.com&lt;/a&gt;&lt;br&gt;
💬 WhatsApp: +971 56 408 5594&lt;br&gt;
🌐 &lt;a href="http://www.qbeinvoice.com" rel="noopener noreferrer"&gt;www.qbeinvoice.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;QuickBooks is a product of Intuit Inc. Based on UAE Ministry of Finance Electronic Invoicing Guidelines Version 1.1 (June 2026), Ministerial Decisions No. 243 and 244 of 2025, and Cabinet Decision No. 106 of 2025. For general information only — not legal or tax advice.&lt;/p&gt;

</description>
      <category>quickbook</category>
      <category>einvoicing</category>
      <category>quickbookseinvoicing</category>
      <category>quickbooksonline</category>
    </item>
    <item>
      <title>Integrating UAE Peppol E-Invoicing with Odoo — What Developers and Implementers Need to Know</title>
      <dc:creator>Perfonec Computers</dc:creator>
      <pubDate>Tue, 22 Sep 2026 07:34:28 +0000</pubDate>
      <link>https://dev.to/perfonec_computers/integrating-uae-peppol-e-invoicing-with-odoo-what-developers-and-implementers-need-to-know-350c</link>
      <guid>https://dev.to/perfonec_computers/integrating-uae-peppol-e-invoicing-with-odoo-what-developers-and-implementers-need-to-know-350c</guid>
      <description>&lt;p&gt;UAE mandatory e-invoicing went live in pilot phase on 1 July 2026. Every UAE VAT-registered business sending B2B invoices must now route them through the UAE Peppol network as PINT AE XML. For Odoo developers and implementers working with UAE clients, this creates a specific integration challenge — Odoo does not ship with a native UAE Peppol access point. The transmission layer has to be added externally.&lt;/p&gt;

&lt;p&gt;This post covers what the UAE Peppol infrastructure looks like technically, what PINT AE XML requires at the field level, how the Odoo data model maps to those fields, where data quality issues typically appear, and what a complete implementation involves from a developer perspective.&lt;/p&gt;

&lt;p&gt;The UAE Peppol Network — Four-Corner Architecture&lt;/p&gt;

&lt;p&gt;UAE e-invoicing uses the standard Peppol four-corner model:&lt;/p&gt;

&lt;p&gt;Corner 1 — Seller ERP (Odoo)&lt;br&gt;
Corner 2 — Seller ASP (Accredited Service Provider)&lt;br&gt;
Corner 3 — Buyer ASP&lt;br&gt;
Corner 4 — Buyer ERP&lt;/p&gt;

&lt;p&gt;The ASP at Corner 2 is the developer integration point. It validates the invoice against the PINT AE schema, handles the Peppol transmission, and returns a status response. For Odoo, this means building or configuring a connector between the account.move model and the ASP API.&lt;/p&gt;

&lt;p&gt;The standard integration pattern is:&lt;/p&gt;

&lt;p&gt;account.move (state = posted)&lt;br&gt;
    → intercept confirmation event&lt;br&gt;
    → read invoice data from Odoo ORM&lt;br&gt;
    → map to PINT AE fields&lt;br&gt;
    → serialize to XML&lt;br&gt;
    → POST to ASP API endpoint&lt;br&gt;
    → receive status response&lt;br&gt;
    → write status back to account.move&lt;br&gt;
PINT AE XML — Schema and Mandatory Fields&lt;/p&gt;

&lt;p&gt;PINT AE is the UAE's national extension of Peppol BIS Billing 3.0. It is defined under the UAE Ministry of Finance Electronic Invoicing Guidelines Version 1.1, published June 2026.&lt;/p&gt;

&lt;p&gt;Every B2B invoice must pass schema validation before the ASP will transmit it. The 17 mandatory PINT AE fields and their BT codes are:&lt;/p&gt;

&lt;p&gt;BT-1    Invoice number&lt;br&gt;
BT-2    Invoice date (ISO 8601 — YYYY-MM-DD)&lt;br&gt;
BT-3    Invoice type code&lt;br&gt;
        380 = standard invoice&lt;br&gt;
        381 = credit note&lt;br&gt;
BT-5    Invoice currency code (ISO 4217)&lt;br&gt;
BT-27   Supplier name&lt;br&gt;
BT-29   Supplier TRN (15-digit UAE VAT number)&lt;br&gt;
BT-35   Supplier street address&lt;br&gt;
BT-37   Supplier city&lt;br&gt;
BT-40   Supplier country code (must be "AE")&lt;br&gt;
BT-44   Buyer name&lt;br&gt;
BT-46   Buyer TRN (15-digit UAE VAT number)&lt;br&gt;
BT-50   Buyer street address&lt;br&gt;
BT-52   Buyer city&lt;br&gt;
BT-55   Buyer country code&lt;br&gt;
BT-110  Sum of invoice line net amounts&lt;br&gt;
BT-112  Invoice total VAT amount&lt;br&gt;
BT-131  Invoice line net amount (per line)&lt;/p&gt;

&lt;p&gt;Every invoice line must also carry one of four PINT AE VAT category codes:&lt;/p&gt;

&lt;p&gt;S    Standard rate — 5% UAE VAT&lt;br&gt;
Z    Zero-rated — exports, certain food, international transport&lt;br&gt;
E    Exempt — bare land, residential property resale&lt;br&gt;
O    Out of scope — outside UAE territory, certain financial services&lt;/p&gt;

&lt;p&gt;And every unit of measure must use UN/CEFACT codes:&lt;/p&gt;

&lt;p&gt;EA    Each (countable items)&lt;br&gt;
KGM   Kilogram&lt;br&gt;
LTR   Litre&lt;br&gt;
MTR   Metre&lt;br&gt;
HUR   Hour (services billed hourly)&lt;br&gt;
DAY   Day&lt;br&gt;
MTK   Square metre&lt;br&gt;
Mapping PINT AE Fields to the Odoo Data Model&lt;/p&gt;

&lt;p&gt;Here is the direct mapping from PINT AE BT codes to Odoo models and fields:&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;
&lt;h1&gt;
  
  
  PINT AE → Odoo field mapping
&lt;/h1&gt;

&lt;p&gt;PINT_AE_MAPPING = {&lt;br&gt;
    'BT-1':   ('account.move',      'name'),&lt;br&gt;
    'BT-2':   ('account.move',      'invoice_date'),&lt;br&gt;
    'BT-3':   ('account.move',      'move_type'),&lt;br&gt;
    'BT-5':   ('account.move',      'currency_id'),&lt;br&gt;
    'BT-27':  ('res.company',       'name'),&lt;br&gt;
    'BT-29':  ('res.company',       'vat'),&lt;br&gt;
    'BT-35':  ('res.company',       'street'),&lt;br&gt;
    'BT-37':  ('res.company',       'city'),&lt;br&gt;
    'BT-40':  ('res.company',       'country_id'),&lt;br&gt;
    'BT-44':  ('res.partner',       'name'),&lt;br&gt;
    'BT-46':  ('res.partner',       'vat'),&lt;br&gt;
    'BT-50':  ('res.partner',       'street'),&lt;br&gt;
    'BT-52':  ('res.partner',       'city'),&lt;br&gt;
    'BT-55':  ('res.partner',       'country_id'),&lt;br&gt;
    'BT-110': ('account.move',      'amount_untaxed'),&lt;br&gt;
    'BT-112': ('account.move',      'amount_tax'),&lt;br&gt;
    'BT-131': ('account.move.line', 'price_subtotal'),&lt;br&gt;
}&lt;/p&gt;
&lt;h1&gt;
  
  
  Unit of measure maps through product_uom on account.move.line
&lt;/h1&gt;
&lt;h1&gt;
  
  
  to uom.uom — requires custom l10n_ae_uom_code field on uom.uom
&lt;/h1&gt;
&lt;h1&gt;
  
  
  VAT category code maps through tax_ids on account.move.line
&lt;/h1&gt;
&lt;h1&gt;
  
  
  to account.tax — requires custom l10n_ae_vat_type field on account.tax
&lt;/h1&gt;

&lt;p&gt;The Most Common Data Quality Issues&lt;/p&gt;

&lt;p&gt;In practice, the PINT AE schema is not the hard part. The hard part is data quality in existing Odoo databases. These are the five failures that appear most consistently across UAE Odoo implementations:&lt;/p&gt;

&lt;p&gt;Issue 1 — BT-46 missing (buyer TRN)&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;
&lt;h1&gt;
  
  
  Check for partners without TRN
&lt;/h1&gt;

&lt;p&gt;partners_without_trn = env['res.partner'].search([&lt;br&gt;
    ('customer_rank', '&amp;gt;', 0),&lt;br&gt;
    ('vat', '=', False),&lt;br&gt;
    ('country_id.code', '=', 'AE'),&lt;br&gt;
])&lt;/p&gt;
&lt;h1&gt;
  
  
  Typical result: 20-40% of customer records
&lt;/h1&gt;

&lt;p&gt;Fix: Bulk update via partner import, add a constrains check on res.partner to require vat when country_id = AE and customer_rank &amp;gt; 0.&lt;/p&gt;

&lt;p&gt;Issue 2 — BT-50/BT-52 combined in one field&lt;/p&gt;

&lt;p&gt;Many UAE Odoo accounts have the full address stored in street as a single string — "Office 401, Al Barsha 1, Dubai". PINT AE requires street and city as separate fields.&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;
&lt;h1&gt;
  
  
  Identify affected partners
&lt;/h1&gt;

&lt;p&gt;partners_with_combined_address = env['res.partner'].search([&lt;br&gt;
    ('customer_rank', '&amp;gt;', 0),&lt;br&gt;
    ('city', '=', False),&lt;br&gt;
    ('street', '!=', False),&lt;br&gt;
])&lt;/p&gt;

&lt;p&gt;Fix: Write a migration script to split the address or use partner import with separate columns.&lt;/p&gt;

&lt;p&gt;Issue 3 — Missing UN/CEFACT codes on uom.uom&lt;/p&gt;

&lt;p&gt;Standard Odoo UoM records do not include UN/CEFACT codes. The PINT AE XML serialiser needs this to populate the unit code on each invoice line.&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;
&lt;h1&gt;
  
  
  Add custom field to uom.uom
&lt;/h1&gt;

&lt;p&gt;class UoM(models.Model):&lt;br&gt;
    _inherit = 'uom.uom'&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;l10n_ae_uom_code = fields.Char(
    string='UN/CEFACT Code',
    help='Required for UAE PINT AE e-invoicing'
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Then populate via a data XML file:&lt;/p&gt;

&lt;p&gt;xml&lt;br&gt;
&lt;br&gt;
    EA&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
    KGM&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Issue 4 — account.tax records without PINT AE category&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
class AccountTax(models.Model):&lt;br&gt;
    _inherit = 'account.tax'&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;l10n_ae_vat_type = fields.Selection([
    ('S', 'Standard Rate (5%)'),
    ('Z', 'Zero Rated'),
    ('E', 'Exempt'),
    ('O', 'Out of Scope'),
], string='PINT AE VAT Category')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Issue 5 — Fiscal position not assigned to customer records&lt;/p&gt;

&lt;p&gt;UAE fiscal position must be set on res.partner for all UAE VAT-registered customers. Without it, tax lines will not carry the correct PINT AE category.&lt;/p&gt;

&lt;p&gt;python&lt;/p&gt;

&lt;h1&gt;
  
  
  Bulk assign UAE fiscal position
&lt;/h1&gt;

&lt;p&gt;uae_fiscal_pos = env['account.fiscal.position'].search([&lt;br&gt;
    ('name', 'ilike', 'UAE')&lt;br&gt;
], limit=1)&lt;/p&gt;

&lt;p&gt;uae_partners = env['res.partner'].search([&lt;br&gt;
    ('customer_rank', '&amp;gt;', 0),&lt;br&gt;
    ('country_id.code', '=', 'AE'),&lt;br&gt;
    ('property_account_position_id', '=', False),&lt;br&gt;
])&lt;br&gt;
uae_partners.write({&lt;br&gt;
    'property_account_position_id': uae_fiscal_pos.id&lt;br&gt;
})&lt;br&gt;
The ASP Integration — Request and Response Pattern&lt;/p&gt;

&lt;p&gt;The ASP API call follows this pattern for most UAE FTA-approved ASPs:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
import requests&lt;br&gt;
import json&lt;/p&gt;

&lt;p&gt;def submit_to_asp(invoice_xml: str, invoice_id: str) -&amp;gt; dict:&lt;br&gt;
    """&lt;br&gt;
    Submit PINT AE XML to ASP endpoint.&lt;br&gt;
    Returns status dict with transmission result.&lt;br&gt;
    """&lt;br&gt;
    headers = {&lt;br&gt;
        'Content-Type': 'application/xml',&lt;br&gt;
        'Authorization': f'Bearer {ASP_API_KEY}',&lt;br&gt;
        'X-Invoice-ID': invoice_id,&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;response = requests.post(
    url=ASP_ENDPOINT,
    data=invoice_xml.encode('utf-8'),
    headers=headers,
    timeout=30,
)

if response.status_code == 200:
    return {
        'status': 'sent',
        'peppol_id': response.json().get('peppolId'),
        'timestamp': response.json().get('sentAt'),
    }
elif response.status_code == 422:
    return {
        'status': 'rejected',
        'errors': response.json().get('validationErrors', []),
    }
else:
    response.raise_for_status()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The status response should be written back to account.move via a custom field and surfaced in the invoice chatter so the accounts team can see it without going to a separate dashboard.&lt;/p&gt;

&lt;p&gt;Credit Notes — Type 381&lt;/p&gt;

&lt;p&gt;Credit notes in PINT AE use document type code 381 and must reference the original invoice:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
def get_invoice_type_code(move):&lt;br&gt;
    """Map Odoo move_type to PINT AE BT-3 value."""&lt;br&gt;
    if move.move_type == 'out_invoice':&lt;br&gt;
        return '380'&lt;br&gt;
    elif move.move_type == 'out_refund':&lt;br&gt;
        return '381'&lt;br&gt;
    raise ValueError(f'Unexpected move_type: {move.move_type}')&lt;/p&gt;

&lt;p&gt;def get_preceding_invoice_ref(move):&lt;br&gt;
    """Get original invoice reference for credit notes (BT-25)."""&lt;br&gt;
    if move.move_type == 'out_refund' and move.reversed_entry_id:&lt;br&gt;
        return move.reversed_entry_id.name&lt;br&gt;
    return None&lt;br&gt;
Testing Sequence for the Pilot Phase&lt;/p&gt;

&lt;p&gt;The UAE e-invoicing pilot phase runs from 1 July 2026 with no financial penalties for validation failures. This is the right window to run a complete integration test. The recommended sequence:&lt;/p&gt;

&lt;p&gt;Test 1 — Standard B2B invoice&lt;br&gt;
         All 17 BT fields populated&lt;br&gt;
         Tax line with category S&lt;/p&gt;

&lt;p&gt;Test 2 — Zero-rated export invoice&lt;br&gt;
         BT-55 buyer country ≠ AE&lt;br&gt;
         Tax line with category Z&lt;/p&gt;

&lt;p&gt;Test 3 — Credit note&lt;br&gt;
         move_type = out_refund&lt;br&gt;
         BT-3 = 381&lt;br&gt;
         BT-25 = original invoice name&lt;/p&gt;

&lt;p&gt;Test 4 — Mixed-tax invoice&lt;br&gt;
         Lines with S and Z categories on same invoice&lt;/p&gt;

&lt;p&gt;Test 5 — Free zone invoice&lt;br&gt;
         Custom beneficiary field populated for DMCC/JAFZA/IFZA/DIFC&lt;/p&gt;

&lt;p&gt;Test 6 — Multi-currency invoice&lt;br&gt;
         currency_id ≠ AED&lt;br&gt;
         AED equivalent amounts included per Version 1.1&lt;/p&gt;

&lt;p&gt;Test 7 — Advance payment invoice&lt;br&gt;
         Per UAE E-Invoicing Guidelines Version 1.1&lt;br&gt;
         Separate document flow from sale.order&lt;/p&gt;

&lt;p&gt;Each test should be run against the ASP's sandbox Peppol endpoint — not a mock — to catch schema validation errors that local pre-validation may not catch.&lt;/p&gt;

&lt;p&gt;Mandatory Deadlines for Implementers&lt;br&gt;
Go-live (revenue AED 50M+):   1 January 2027&lt;br&gt;
Fines begin (large biz):      2 January 2027&lt;br&gt;
Go-live (all other SMEs):     1 July 2027&lt;br&gt;
Fines begin (SMEs):           2 July 2027&lt;br&gt;
Pilot phase (no penalties):   From 1 July 2026 — open now&lt;/p&gt;

&lt;p&gt;Penalty under Cabinet Decision No. 106 of 2025: AED 5,000/month for non-implementation. AED 100/non-compliant invoice capped at AED 5,000/month. Maximum combined: AED 10,000/month.&lt;/p&gt;

&lt;p&gt;Summary&lt;/p&gt;

&lt;p&gt;UAE Peppol e-invoicing for Odoo is a well-scoped integration problem. The PINT AE schema is clearly defined. The Odoo data model maps cleanly to the BT fields once the custom extensions are in place. The real work is data quality — specifically BT-46 (buyer TRN), address structure (BT-50/BT-52), UN/CEFACT unit codes, and fiscal position assignment.&lt;/p&gt;

&lt;p&gt;A pre-validation step before the ASP call is worth implementing — it catches the most common data issues at confirmation time and reduces ASP API errors significantly.&lt;/p&gt;

&lt;p&gt;The pilot phase is the right time to run the full test sequence. No penalties apply during pilot regardless of how many validation failures occur.&lt;/p&gt;

&lt;p&gt;Based on UAE Ministry of Finance Electronic Invoicing Guidelines Version 1.1 (June 2026) and Peppol BIS Billing 3.0. For technical reference only — not legal advice.&lt;/p&gt;

&lt;p&gt;Written by Akanksha Surana — Odoo Ready Partner with Odoo Middle East.&lt;/p&gt;

</description>
      <category>odoo</category>
      <category>python</category>
      <category>xml</category>
      <category>api</category>
    </item>
    <item>
      <title>Zoho Books Is Now an FTA-Approved ASP — What This Means for UAE Businesses</title>
      <dc:creator>Perfonec Computers</dc:creator>
      <pubDate>Thu, 17 Sep 2026 07:43:16 +0000</pubDate>
      <link>https://dev.to/perfonec_computers/zoho-books-is-now-an-fta-approved-asp-what-this-means-for-uae-businesses-28g9</link>
      <guid>https://dev.to/perfonec_computers/zoho-books-is-now-an-fta-approved-asp-what-this-means-for-uae-businesses-28g9</guid>
      <description>&lt;p&gt;This is a significant update for every UAE business using Zoho Books.&lt;/p&gt;

&lt;p&gt;Zoho Books has been approved by the UAE FTA as an Accredited Service Provider — or ASP — for UAE mandatory e-invoicing. As a result, UAE Peppol e-invoicing now works directly within Zoho Books. No external ASP subscription is needed anymore.&lt;/p&gt;

&lt;p&gt;Disclosure: Perfonec Computers is the authorised Zoho Partner in UAE. We supply Zoho Books licences and handle UAE e-invoicing setup. This guide is written to inform UAE Zoho Books users about this update.&lt;/p&gt;

&lt;p&gt;What Is an ASP and Why Does This Matter?&lt;/p&gt;

&lt;p&gt;An ASP, or Accredited Service Provider, is an FTA-approved company that validates UAE invoices, converts them to PINT AE XML format, and sends them through the UAE Peppol network to the buyer.&lt;/p&gt;

&lt;p&gt;Previously, Zoho Books did not have its own Peppol access point. Therefore, every UAE Zoho Books user needed to find, appoint, and pay for a separate external ASP subscription on top of their Zoho Books licence fee.&lt;/p&gt;

&lt;p&gt;That has now changed. Zoho Books is the ASP. The direct Peppol connection is built into Zoho Books itself. Therefore, you no longer need a separate ASP at all.&lt;/p&gt;

&lt;p&gt;What Changed — Before and After&lt;/p&gt;

&lt;p&gt;Before this approval, UAE Zoho Books users needed two things — a Zoho Books licence and a separate external ASP subscription. The ASP had to be selected, appointed, and managed separately. Invoice data left Zoho Books to reach the ASP first before going to the Peppol network.&lt;/p&gt;

&lt;p&gt;Now, everything happens inside Zoho Books. When you confirm a B2B invoice, Zoho Books creates the PINT AE XML directly, checks all 17 mandatory PINT AE fields, and sends the invoice through the UAE Peppol network on its own. As a result, there is no extra ASP cost, no extra vendor to manage, and no API link to set up between Zoho Books and an outside ASP.&lt;/p&gt;

&lt;p&gt;In short — Zoho Books now does what TallyPrime 7.0 has done from the start. Both platforms have direct built-in Peppol e-invoicing with no external ASP needed.&lt;/p&gt;

&lt;p&gt;How the Invoice Journey Works Now&lt;/p&gt;

&lt;p&gt;After setup, your team confirms invoices in Zoho Books exactly as always. The whole Peppol process happens in the background automatically. Here is the full journey:&lt;/p&gt;

&lt;p&gt;Invoice confirmed in Zoho Books — your team, same as always&lt;br&gt;
Zoho Books creates PINT AE XML directly — automatic&lt;br&gt;
Zoho Books checks all 17 PINT AE mandatory fields — automatic&lt;br&gt;
PINT AE XML sent through UAE Peppol network directly from Zoho Books — automatic&lt;br&gt;
Invoice delivered to buyer's accounting system — automatic&lt;br&gt;
Send status updated in Zoho Books dashboard — automatic&lt;/p&gt;

&lt;p&gt;Your team does nothing new after go-live.&lt;/p&gt;

&lt;p&gt;What This Means for Cost&lt;/p&gt;

&lt;p&gt;Before this update, UAE Zoho Books users paid for a Zoho Books licence plus a separate ASP subscription. Now, the ASP function is built into Zoho Books. Therefore, there is no extra ASP cost at all on the right plans.&lt;/p&gt;

&lt;p&gt;Professional plan (~AED 110/month) — direct Peppol supported, no extra ASP cost&lt;/p&gt;

&lt;p&gt;Premium plan (~AED 165/month) — direct Peppol supported, no extra ASP cost&lt;/p&gt;

&lt;p&gt;Elite plan (~AED 220/month) — direct Peppol supported, no extra ASP cost&lt;/p&gt;

&lt;p&gt;Standard plan (~AED 55/month) — does NOT support direct Peppol — plan upgrade needed&lt;/p&gt;

&lt;p&gt;Free tier — does NOT support direct Peppol&lt;/p&gt;

&lt;p&gt;How Zoho Books Now Compares to Other UAE Platforms&lt;/p&gt;

&lt;p&gt;With this approval, the UAE e-invoicing platform picture looks like this:&lt;/p&gt;

&lt;p&gt;TallyPrime 7.0 — native Peppol built in, no external ASP needed. Best for trading, manufacturing, and stock-heavy businesses.&lt;/p&gt;

&lt;p&gt;Zoho Books — now an approved ASP with direct Peppol built in, no external ASP needed. Best for small businesses, startups, and businesses already using the Zoho suite.&lt;/p&gt;

&lt;p&gt;QuickBooks — needs QBESync by Perfonec to connect to UAE Peppol. Best for SMEs and professional services.&lt;/p&gt;

&lt;p&gt;Odoo ERP — needs OESync by Perfonec to connect to UAE Peppol. Best for growing businesses needing full ERP.&lt;/p&gt;

&lt;p&gt;Sage 50 — needs middleware and external ASP. Best for distribution and trading companies.&lt;/p&gt;

&lt;p&gt;Therefore, UAE businesses now have two strong no-external-ASP options — TallyPrime 7.0 and Zoho Books. The right choice depends on your business type and size.&lt;/p&gt;

&lt;p&gt;What You Still Need to Set Up&lt;/p&gt;

&lt;p&gt;This is the most important point to understand. Zoho Books being an approved ASP does not mean e-invoicing is automatically ready in your account. You still need to configure Zoho Books for UAE e-invoicing before you can go live.&lt;/p&gt;

&lt;p&gt;Here is what needs to be done:&lt;/p&gt;

&lt;p&gt;Plan check — Professional, Premium, or Elite plan required. Standard plan users need to upgrade first.&lt;/p&gt;

&lt;p&gt;PINT AE data audit — all 17 mandatory PINT AE fields must be correctly set up in Zoho Books. The most commonly missing field is the buyer TRN in Zoho Books Contacts. Additionally, addresses must be in a structured format, product names must be specific, and UN/CEFACT unit codes must be set up on all products.&lt;/p&gt;

&lt;p&gt;VAT code mapping — all Zoho Books tax rates must be mapped to the four PINT AE VAT category codes. S for standard rated at 5%, Z for zero-rated, E for exempt, and O for out of scope.&lt;/p&gt;

&lt;p&gt;Enable direct Peppol — the direct Peppol connection must be turned on and configured in your Zoho Books account settings. This includes credit note type 381, advance payment invoices per Version 1.1, and free zone fields for DMCC, JAFZA, IFZA, and DIFC.&lt;/p&gt;

&lt;p&gt;Pilot phase testing — all invoice types must be tested through the direct Peppol connection during the pilot phase. The pilot phase has been open since 1 July 2026 with no financial fines for any errors. Therefore, now is the right time to test.&lt;/p&gt;

&lt;p&gt;Team training — your accounts team needs to know how to read the Peppol send status in Zoho Books and what to do if an invoice is rejected.&lt;/p&gt;

&lt;p&gt;What About Businesses Already Using Zoho Books with an External ASP?&lt;/p&gt;

&lt;p&gt;If you already set up an external ASP for your Zoho Books account, you can now move to the direct Peppol connection built into Zoho Books. As a result, you no longer need to pay for the external ASP subscription. Contact Perfonec to migrate your setup to the direct connection.&lt;/p&gt;

&lt;p&gt;UAE E-Invoicing Mandatory Deadlines&lt;/p&gt;

&lt;p&gt;Every UAE VAT-registered business must be live on UAE mandatory e-invoicing by their deadline:&lt;/p&gt;

&lt;p&gt;1 January 2027 — businesses with annual revenue above AED 50 million&lt;br&gt;
1 July 2027 — all other VAT-registered SMEs&lt;br&gt;
1 October 2027 — government entities&lt;br&gt;
From 1 July 2026 — pilot phase open now, no fines during testing&lt;/p&gt;

&lt;p&gt;The fine for not going live is AED 5,000 per month under Cabinet Decision No. 106 of 2025. Additionally, AED 100 per non-compliant invoice applies, capped at AED 5,000 per month. However, the pilot phase carries zero fines. Therefore, starting now is the safest approach.&lt;/p&gt;

&lt;p&gt;Do Fines Apply to Free Zone Zoho Books Users?&lt;/p&gt;

&lt;p&gt;Yes. All UAE VAT-registered free zone businesses — DMCC, JAFZA, IFZA, DIFC, ADGM, RAKEZ — are in scope for UAE mandatory e-invoicing. Furthermore, a free zone licence gives no exemption from the fine schedule under Cabinet Decision No. 106 of 2025.&lt;/p&gt;

&lt;p&gt;The Bottom Line&lt;/p&gt;

&lt;p&gt;Zoho Books being an FTA-approved ASP is a major positive change for UAE Zoho Books users. It removes the need for a separate ASP subscription, simplifies the setup process, and reduces the total cost of UAE e-invoicing compliance.&lt;/p&gt;

&lt;p&gt;However, you still need to configure Zoho Books correctly before going live. The PINT AE data audit, VAT code mapping, and direct Peppol setup all need to be done before your mandatory deadline. Perfonec handles all of this as the authorised Zoho Partner in UAE.&lt;/p&gt;

&lt;p&gt;The pilot phase is open now with zero fines. Therefore, the right time to start is today.&lt;/p&gt;

&lt;p&gt;About the Author&lt;/p&gt;

&lt;p&gt;Akanksha Surana is the CEO and Owner of Perfonec Computers, Dubai — authorised Zoho Partner, Certified QuickBooks Pro Advisor, Authorised Silver Partner for TallyPrime, developer of QBESync and OESync, and Odoo Ready Partner with Odoo Middle East (Cert n°0000699697). She has 9 years of experience setting up Zoho Books and UAE e-invoicing for UAE businesses.&lt;/p&gt;

&lt;p&gt;Free Zoho Books UAE e-invoicing readiness check — no cost, no commitment.&lt;/p&gt;

&lt;p&gt;📞 +971 4 386 6199 | &lt;a href="mailto:sales@perfonec.com"&gt;sales@perfonec.com&lt;/a&gt; | &lt;a href="http://www.perfonec.com" rel="noopener noreferrer"&gt;www.perfonec.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on UAE Ministry of Finance E-Invoicing Guidelines Version 1.1 (June 2026) and Cabinet Decision No. 106 of 2025. For general guidance only — not legal or tax advice.&lt;/p&gt;

</description>
      <category>zoho</category>
      <category>zohobooks</category>
      <category>zohoeinvoicing</category>
      <category>einvoixinguae</category>
    </item>
    <item>
      <title>Does TallyPrime Need an External ASP for UAE E-Invoicing?</title>
      <dc:creator>Perfonec Computers</dc:creator>
      <pubDate>Tue, 01 Sep 2026 11:25:17 +0000</pubDate>
      <link>https://dev.to/perfonec_computers/does-tallyprime-need-an-external-asp-for-uae-e-invoicing-4cfa</link>
      <guid>https://dev.to/perfonec_computers/does-tallyprime-need-an-external-asp-for-uae-e-invoicing-4cfa</guid>
      <description>&lt;p&gt;Businesses in the UAE preparing for e-Invoicing often have questions about how their existing accounting software will work with the new requirements. One common question among TallyPrime users is whether they need to purchase a separate external Accredited Service Provider (ASP).&lt;/p&gt;

&lt;p&gt;The answer is that a TallyPrime user does not necessarily need a separate third-party ASP. Tally Software Solutions FZCO is an accredited service provider under the UAE e-Invoicing framework, so businesses can choose Tally as their ASP where applicable.&lt;/p&gt;

&lt;p&gt;TallyPrime supports accounting and invoicing workflows that can be used as part of the UAE e-Invoicing process. However, businesses still need to complete the required onboarding, configuration, and authorization steps.&lt;/p&gt;

&lt;p&gt;Before getting started, TallyPrime users should check that their software version is supported for UAE e-Invoicing and ensure that important information such as the company's TRN, VAT details, customer records, supplier records, and accounting data is accurate.&lt;/p&gt;

&lt;p&gt;Using an integrated accounting and e-Invoicing workflow can also help reduce duplicate data entry and make it easier for businesses to manage their invoicing processes.&lt;/p&gt;

&lt;p&gt;The key point is that &lt;strong&gt;having an ASP is part of the UAE e-Invoicing framework, but the ASP does not necessarily have to be a separate external software provider.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Businesses using TallyPrime should review the latest UAE e-Invoicing requirements and Tally's current documentation before completing their implementation.&lt;/p&gt;

</description>
      <category>tallyprime</category>
      <category>tallyerp</category>
      <category>tallyeinvoicng</category>
    </item>
  </channel>
</rss>
