diff --git a/data/buyersdata_broken.csv b/data/buyersdata_broken.csv
index 226b6ea..85278f7 100644
--- a/data/buyersdata_broken.csv
+++ b/data/buyersdata_broken.csv
@@ -1,4 +1,23 @@
Office,Invoice,Receipt,OrgId,TaxId,Organization,City,Street,Zip,Country
-PL,A282_T000_260110_1,A28220260110102114,0,0,-customer not found-,,,,EU
-HR,A100_T000_260126_1,A10020260126101017,,,Test,Zagreb,Test 12,10000,HR
-BE,AN19_T000_260209_2,AN1920260104105115,,,,Heppen,Leopoldsburgsesteenweg 58,3971,BE
+Office,Invoice,Receipt,OrgId,TaxId,Organization,City,Street,Zip,Country
+HR,A275_T000_260205_5,A27520260110101225,94781370847,94781370847,DYNAMIC CARS,PREGRADA,JANKA LESKOVARA 69/2,49218,RH
+BE,AN03_T000_260209_7,AN0320260104101097,,,,ixelles,rue des chevaliers 14,1050,BE
+PL,A177_T000_260203_1,A17720260203101048,,,,,,,PL
+PL,A282_T000_260225_1,A28220260225102015,,,,,,,PL
+PL,A947_T000_260226_1,A94720260226102071,,,Sebastian Zawierucha,Wilczyn,15 Śwętne,62-550,PL
+PL,A947_T000_260226_2,A94720260226102070,,,Sebastian Zawierucha,Wilczyn,Świętna 15,62-550,PL
+BE,AN14_T000_260228_1,AN1420260228101013,,,,,,,BE
+PL,A900_T000_260302_2,A90020260228102300,,,KONSTANCJA RACZKOWSKA,PIASECZNO,WARSZAWSKA 23/21,05-500,PL
+PL,A109_T000_260304_1,A10920260304101111,,,DAWID HALCZAK,ZIELONA GORA,DUNIKOWSKIEGO 7,65-140,PL
+PL,F061_T000_260304_1,F06120260304101051,,,Nicola Di Ciano,Kraków,Rakowicka 20J/173,31-510,PL
+PL,A285_T000_260305_1,A28520260305101011,5651038584,5651038584,,sosnowica,ul. Parczewska 24,21-230,PL
+PL,A285_T000_260305_3,A28520260305101015,5651038584,5651038584,,sosnowica,ul. Parczewska 24,21-230,PL
+PL,A947_T000_260306_1,A94720260306102007,,,Sebastian Zawierucha,Wilczyn,Świętne 15,62-550,PL
+BE,AN03_T000_260307_1,AN0320260209101068,,,,,,,BE
+PL,A943_T000_260307_1,A94320260306104281,,,Larysa Zmura,SKARZEWY,KOŚCIERSKA 49,83-250,PL
+BE,AN05_T000_260309_1,AN0520260217101041,0778435688,0778435688,Logopedie Silke ,Erpe-Mere,oudenaardsesteenweg 740 b1,9420,b
+BE,AN05_T000_260309_2,AN0520260215101029,0759391719,0759391719,SRL Schiettecatte ,Ressaix,Rue Du Village 7,7134,b
+BE,AN05_T000_260309_3,AN0520260308101101,0757920188,0757920188,LG Cars ,Lievegem,Grote Baan 5,9920,B
+BE,AN05_T000_260309_5,AN0520260228101046,0778435688,0778435688,logopedie silke commv ,erpe-mere,oudenaardsesteenweg 740 b1,9420,b
+PL,A286_T000_260310_1,A28620260310101031,,,,,,,PL
+PL,A903_T000_260311_1,A90320260311102061,,,,,,,PL
diff --git a/data/stores_to_invoice.csv b/data/stores_to_invoice.csv
index c67ceef..17a8f08 100644
--- a/data/stores_to_invoice.csv
+++ b/data/stores_to_invoice.csv
@@ -622,13 +622,13 @@ A968,Tommy Hilfiger Warsaw (Piaseczno) Outlet Temp,PL,1,0,
F05Z,Tommy Hilfiger Sosnowiec Designer Outlet,PL,1,0,
F061,Tommy Hilfiger Krakow (Designer Outlet),PL,1,1,
F062,Calvin Klein Krakow (Designer Outlet),PL,1,0,
-A040,Calvin Klein Porto Outlet,PT,1,0,
-A053,Calvin Klein Underwear Alcabideche (Cascai Shopping Mall),PT,1,0,
-A059,Calvin Klein Underwear Lisbon (Vasco de Gama Mall),PT,1,0,
-A065,Calvin Klein Jeans Alcochete (Freeport) Outlet,PT,1,0,
-A064,Calvin Klein Underwear Alcochete (Freeport) Outlet,PT,1,0,
-A063,Calvin Klein Jeans Loule (Algarve) Outlet,PT,1,0,
-A1C1,Calvin Klein Porto (Norteshopping),PT,1,0,
+A040,Calvin Klein Porto Outlet,PT,,0,
+A053,Calvin Klein Underwear Alcabideche (Cascai Shopping Mall),PT,,0,
+A059,Calvin Klein Underwear Lisbon (Vasco de Gama Mall),PT,,0,
+A065,Calvin Klein Jeans Alcochete (Freeport) Outlet,PT,,0,
+A064,Calvin Klein Underwear Alcochete (Freeport) Outlet,PT,,0,
+A063,Calvin Klein Jeans Loule (Algarve) Outlet,PT,,0,
+A1C1,Calvin Klein Porto (Norteshopping),PT,,0,
AS04,Tommy Hilfiger Taby,SE,,0,
AA26,Tommy Hilfiger Stockholm (Bibliotek),SE,,0,
AS07,Tommy Hilfiger Jarfalla (Barkarby) Outlet,SE,,0,
diff --git a/flask_company_data.py b/flask_company_data.py
new file mode 100644
index 0000000..b5ea829
--- /dev/null
+++ b/flask_company_data.py
@@ -0,0 +1,211 @@
+from datetime import date
+import re
+
+import requests
+from flask import Flask, jsonify, render_template, request
+from requests import Session
+from zeep import Client
+from zeep.transports import Transport
+
+app = Flask(__name__)
+
+VIES_WSDL_URL = "https://ec.europa.eu/taxation_customs/vies/services/checkVatService.wsdl"
+POLISH_WL_API = "https://wl-api.mf.gov.pl/api/search/nip/{nip}?date={query_date}"
+
+
+def get_vies_client():
+ session = Session()
+ transport = Transport(session=session, timeout=20)
+ return Client(wsdl=VIES_WSDL_URL, transport=transport)
+
+
+def normalize_vat(country_code: str, vat_number: str):
+ country_code = (country_code or "").strip().upper()
+ vat_number = (vat_number or "").strip().replace(" ", "")
+
+ if vat_number.upper().startswith(country_code):
+ vat_number = vat_number[len(country_code):]
+
+ return country_code, vat_number
+
+
+def parse_polish_working_address(address: str):
+ if not address:
+ return "", "", ""
+
+ address = " ".join(address.split())
+ match = re.match(r"^(.*?)(\d{2}-\d{3})\s+(.+)$", address)
+
+ if match:
+ street = match.group(1).strip(" ,")
+ zip_code = match.group(2).strip()
+ city = match.group(3).strip()
+ return street, zip_code, city
+
+ return address, "", ""
+
+
+def parse_vies_address(address: str):
+ """
+ Assumption for VIES address:
+ - address is comma-separated
+ - first part = street
+ - last part = ZIP + CITY
+
+ Examples:
+ "Main Street 12, Some Region, 1000 Brussels"
+ "ul. Prosta 12, Warszawa, 00-838 Warszawa"
+ "Ilica 1, Zagreb, HR-10000 Zagreb"
+ """
+ if not address:
+ return "", "", ""
+
+ parts = [part.strip() for part in address.split(",") if part.strip()]
+ if not parts:
+ return "", "", ""
+
+ street = parts[0]
+ last_part = parts[-1]
+
+ # Examples:
+ # 1000 Brussels
+ # 00-838 Warszawa
+ # HR-10000 Zagreb
+ match = re.match(r"^([A-Z]{0,2}-?\d[\dA-Z -]*)\s+(.+)$", last_part)
+ if match:
+ zip_code = match.group(1).strip()
+ city = match.group(2).strip()
+ return street, zip_code, city
+
+ return street, "", last_part
+
+
+def check_vies(country_code: str, vat_number: str):
+ client = get_vies_client()
+ result = client.service.checkVat(countryCode=country_code, vatNumber=vat_number)
+
+ name = (result.name or "").strip()
+ address = (result.address or "").strip()
+
+ street, zip_code, city = parse_vies_address(address)
+
+ return {
+ "source": "vies",
+ "found": bool(result.valid),
+ "country": country_code,
+ "vat_number": vat_number,
+ "name": name if result.valid else "",
+ "street": street if result.valid else "",
+ "zip": zip_code if result.valid else "",
+ "city": city if result.valid else "",
+ "raw_address": address if result.valid else "",
+ }
+
+
+def check_polish_wl(nip: str):
+ today = date.today().isoformat()
+ url = POLISH_WL_API.format(nip=nip, query_date=today)
+
+ response = requests.get(url, headers={"Accept": "application/json"}, timeout=20)
+ response.raise_for_status()
+
+ data = response.json()
+ result = data.get("result", {})
+ subject = result.get("subject")
+
+ if not subject:
+ subjects = result.get("subjects") or []
+ subject = subjects[0] if subjects else None
+
+ if not subject:
+ return {
+ "source": "polish_wl",
+ "found": False,
+ "country": "PL",
+ "vat_number": nip,
+ "name": "",
+ "street": "",
+ "zip": "",
+ "city": "",
+ "raw_address": "",
+ }
+
+ name = (subject.get("name") or "").strip()
+ working_address = (subject.get("workingAddress") or "").strip()
+ street, zip_code, city = parse_polish_working_address(working_address)
+
+ return {
+ "source": "polish_wl",
+ "found": True,
+ "country": "PL",
+ "vat_number": nip,
+ "name": name,
+ "street": street,
+ "zip": zip_code,
+ "city": city,
+ "raw_address": working_address,
+ }
+
+
+@app.get("/")
+def index():
+ return render_template("index.html")
+
+
+@app.get("/api/company_lookup")
+def company_lookup():
+ country = request.args.get("country", "")
+ vat_number = request.args.get("vat_number", "")
+
+ country, vat_number = normalize_vat(country, vat_number)
+
+ if not country or not vat_number:
+ return jsonify({"error": "country and vat_number are required"}), 400
+
+ try:
+ vies_result = check_vies(country, vat_number)
+ except Exception as exc:
+ vies_result = {
+ "source": "vies",
+ "found": False,
+ "country": country,
+ "vat_number": vat_number,
+ "name": "",
+ "street": "",
+ "zip": "",
+ "city": "",
+ "raw_address": "",
+ "warning": f"VIES lookup failed: {exc}",
+ }
+
+ if vies_result.get("found"):
+ return jsonify(vies_result)
+
+ if country == "PL":
+ try:
+ polish_result = check_polish_wl(vat_number)
+ if "warning" in vies_result:
+ polish_result["warning"] = vies_result["warning"]
+ return jsonify(polish_result)
+ except Exception as exc:
+ return jsonify(
+ {
+ "source": "polish_wl",
+ "found": False,
+ "country": country,
+ "vat_number": vat_number,
+ "name": "",
+ "street": "",
+ "zip": "",
+ "city": "",
+ "raw_address": "",
+ "warning": vies_result.get("warning"),
+ "fallback_error": f"Polish WL lookup failed: {exc}",
+ }
+ )
+
+ return jsonify(vies_result)
+
+
+if __name__ == "__main__":
+ app.run(debug=True)
\ No newline at end of file
diff --git a/flask_invoicing.py b/flask_invoicing.py
index d05e6cb..fa7a87d 100644
--- a/flask_invoicing.py
+++ b/flask_invoicing.py
@@ -206,6 +206,7 @@ def write_repaired_csv(rows):
writer.writerows(rows)
def write_broken_csv(header, rows):
+ print("write broken")
with open(BUYERS_BROKEN, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(header)
diff --git a/get_company_data_be.py b/get_company_data_be.py
new file mode 100644
index 0000000..eee4110
--- /dev/null
+++ b/get_company_data_be.py
@@ -0,0 +1,484 @@
+from flask import Flask, request, jsonify, render_template_string
+from datetime import date
+import re
+import requests
+from requests import Session
+from zeep import Client
+from zeep.transports import Transport
+
+app = Flask(__name__)
+
+VIES_WSDL_URL = "https://ec.europa.eu/taxation_customs/vies/services/checkVatService.wsdl"
+POLISH_WL_API = "https://wl-api.mf.gov.pl/api/search/nip/{nip}?date={query_date}"
+
+
+def get_vies_client():
+ session = Session()
+ transport = Transport(session=session, timeout=20)
+ return Client(wsdl=VIES_WSDL_URL, transport=transport)
+
+
+def normalize_vat(country_code: str, vat_number: str):
+ country_code = (country_code or "").strip().upper()
+ vat_number = (vat_number or "").strip().replace(" ", "")
+ if vat_number.upper().startswith(country_code):
+ vat_number = vat_number[len(country_code):]
+ return country_code, vat_number
+
+
+def split_zip_city(text: str):
+ if not text:
+ return "", ""
+ text = " ".join(text.split())
+ m = re.match(r"^(\d{2}-\d{3})\s+(.+)$", text)
+ if m:
+ return m.group(1), m.group(2)
+ return "", text
+
+
+def parse_polish_working_address(address: str):
+ if not address:
+ return "", "", ""
+ address = " ".join(address.split())
+ m = re.match(r"^(.*?)(\d{2}-\d{3})\s+(.+)$", address)
+ if m:
+ street = m.group(1).strip(" ,")
+ zip_code = m.group(2).strip()
+ city = m.group(3).strip()
+ return street, zip_code, city
+ return address, "", ""
+
+
+def parse_vies_address(address: str):
+ if not address:
+ return "", "", ""
+
+ parts = [p.strip() for p in address.split(",") if p.strip()]
+ if not parts:
+ return "", "", ""
+
+ street = parts[0]
+ last_part = parts[-1]
+
+ # Common cases:
+ # "1000 Brussels"
+ # "00-838 Warszawa"
+ # "HR-10000 Zagreb"
+ m = re.match(r"^([A-Z]{0,2}-?\d[\dA-Z -]*)\s+(.+)$", last_part)
+ if m:
+ zip_code = m.group(1).strip()
+ city = m.group(2).strip()
+ return street, zip_code, city
+
+ return street, "", last_part
+
+
+def check_vies(country_code: str, vat_number: str):
+ client = get_vies_client()
+ result = client.service.checkVat(countryCode=country_code, vatNumber=vat_number)
+
+ name = (result.name or "").strip()
+ address = (result.address or "").strip()
+ street, zip_code, city = parse_vies_address(address)
+
+ return {
+ "source": "vies",
+ "found": bool(result.valid),
+ "country": country_code,
+ "vat_number": vat_number,
+ "name": name if result.valid else "",
+ "street": street if result.valid else "",
+ "zip": zip_code if result.valid else "",
+ "city": city if result.valid else "",
+ "raw_address": address if result.valid else "",
+ }
+
+
+def check_polish_wl(nip: str):
+ today = date.today().isoformat()
+ url = POLISH_WL_API.format(nip=nip, query_date=today)
+
+ response = requests.get(url, headers={"Accept": "application/json"}, timeout=20)
+ response.raise_for_status()
+
+ data = response.json()
+ result = data.get("result", {})
+ subject = result.get("subject")
+
+ if not subject:
+ subjects = result.get("subjects") or []
+ subject = subjects[0] if subjects else None
+
+ if not subject:
+ return {
+ "source": "polish_wl",
+ "found": False,
+ "country": "PL",
+ "vat_number": nip,
+ "name": "",
+ "street": "",
+ "zip": "",
+ "city": "",
+ "raw_address": "",
+ }
+
+ name = (subject.get("name") or "").strip()
+ working_address = (subject.get("workingAddress") or "").strip()
+ street, zip_code, city = parse_polish_working_address(working_address)
+
+ return {
+ "source": "polish_wl",
+ "found": True,
+ "country": "PL",
+ "vat_number": nip,
+ "name": name,
+ "street": street,
+ "zip": zip_code,
+ "city": city,
+ "raw_address": working_address,
+ }
+
+
+@app.get("/api/company_lookup")
+def company_lookup():
+ country = request.args.get("country", "")
+ vat_number = request.args.get("vat_number", "")
+
+ country, vat_number = normalize_vat(country, vat_number)
+
+ if not country or not vat_number:
+ return jsonify({"error": "country and vat_number are required"}), 400
+
+ try:
+ vies_result = check_vies(country, vat_number)
+ except Exception as e:
+ vies_result = {
+ "source": "vies",
+ "found": False,
+ "country": country,
+ "vat_number": vat_number,
+ "name": "",
+ "street": "",
+ "zip": "",
+ "city": "",
+ "raw_address": "",
+ "warning": f"VIES lookup failed: {e}",
+ }
+
+ if vies_result.get("found"):
+ return jsonify(vies_result)
+
+ if country == "PL":
+ try:
+ polish_result = check_polish_wl(vat_number)
+ if "warning" in vies_result:
+ polish_result["warning"] = vies_result["warning"]
+ return jsonify(polish_result)
+ except Exception as e:
+ response = {
+ "source": "polish_wl",
+ "found": False,
+ "country": country,
+ "vat_number": vat_number,
+ "name": "",
+ "street": "",
+ "zip": "",
+ "city": "",
+ "raw_address": "",
+ "warning": vies_result.get("warning"),
+ "fallback_error": f"Polish WL lookup failed: {e}",
+ }
+ return jsonify(response)
+
+ return jsonify(vies_result)
+
+
+HTML = """
+
+
+
+
+
+ VAT Company Lookup
+
+
+
+
+
VAT Company Lookup
+
+
+
+
+
+
+
+
Ready.
+
+
+
+
+
+"""
+
+
+@app.get("/")
+def index():
+ return render_template_string(HTML)
+
+
+if __name__ == "__main__":
+ app.run(debug=True)
\ No newline at end of file
diff --git a/ids.txt b/ids.txt
new file mode 100644
index 0000000..24984b8
--- /dev/null
+++ b/ids.txt
@@ -0,0 +1,322 @@
+PL 5252312380
+PL 5223326818
+PL 8371691451
+PL 5211443926
+PL 7122479987
+PL 8351517715
+PL 5272175093
+PL 7010249294
+PL 6812064950
+PL 6282094698
+PL 8641689330
+PL 5862342505
+PL 9510057883
+PL 1133120152
+PL 5223184063
+PL 9581642778
+PL 8411738355
+PL 6441555115
+PL 9372177136
+PL 7123099979
+PL 8621392710
+PL 5651038584
+PL 7831864882
+PL 7882035701
+PL 8871814655
+PL 8992374497
+PL 5130177811
+PL 6751770109
+PL 6282097188
+PL 7262441743
+PL 8511216828
+PL 8541114564
+PL 9551436838
+PL 1251621119
+PL 5242928346
+PL 5240104641
+PL 5252094977
+PL 5662003112
+PL 8212542282
+PL 9511645369
+PL 5342647606
+PL 5110239532
+PL 5221834918
+PL 5223340020
+PL 7792426792
+PL 5223151715
+PL 7010343395
+PL 1181403221
+PL 5213197319
+PL 5961650279
+PL 8961588694
+PL 9131583969
+PL 8943170228
+PL 8992475577
+PL 5882226788
+PL 9581352827
+PL 5891987110
+PL 5892044936
+PL 8410009967
+PL 7861723608
+PL 8733292391
+PL 2220899222
+PL 8871164128
+PL 6040251786
+PL 8272088792
+PL 6772012242
+BE 0405746743
+BE 0437809005
+BE 0444799636
+BE 0445378567
+BE 0449272128
+BE 0458131196
+BE 0458131196
+BE 0460534224
+BE 0460549862
+BE 0460549862
+BE 0460549862
+BE 0468813074
+BE 0471642506
+BE 0473641102
+BE 0475548042
+BE 0476123411
+BE 0477477352
+BE 0479650845
+BE 0499827053
+BE 0525863031
+BE 0533876122
+BE 0536446127
+BE 0542742120
+BE 0550572493
+BE 0553907117
+BE 0553907117
+BE 0633976459
+BE 0644635868
+BE 0653792371
+BE 0659760445
+BE 0663823557
+BE 0664458512
+BE 0670639489
+BE 0671768847
+BE 0673369743
+BE 0689926455
+BE 0695819701
+BE 0700748388
+BE 0718628458
+BE 0724901487
+BE 0729882240
+BE 0735796468
+BE 0740903618
+BE 0742676738
+BE 0742676738
+BE 0744509444
+BE 0746634041
+BE 0748850193
+BE 0749451494
+BE 0753533711
+BE 0754867658
+BE 0756959591
+BE 0757920188
+BE 0758855546
+BE 0759391719
+BE 0764330009
+BE 0768682141
+BE 0769935223
+BE 0771370922
+BE 0776737297
+BE 0778435688
+BE 0778435688
+BE 0780604926
+BE 0783770589
+BE 0784408712
+BE 0786255868
+BE 0787736010
+BE 0788033443
+BE 0788179141
+BE 0790494075
+BE 0793289556
+BE 0793416943
+BE 0795338731
+BE 0797647529
+BE 0802150408
+BE 0802342032
+BE 0806156607
+BE 0806463245
+BE 0807677428
+BE 0809707795
+BE 0810583072
+BE 0811250392
+BE 0811250392
+BE 0819258040
+BE 0834659561
+BE 0839865491
+BE 0842831812
+BE 0843094306
+BE 0843720747
+BE 0849197980
+BE 0889235226
+BE 0889235226
+BE 0889235226
+BE 0890993005
+BE 0894104329
+BE 0894448183
+BE 0897169826
+BE 0899546920
+BE 0899736168
+BE 1000196395
+BE 1000967150
+BE 1001431067
+BE 1002587545
+BE 1002587545
+BE 1002587545
+BE 1003658703
+BE 1004502207
+BE 1004652358
+BE 1010102570
+BE 1012166492
+BE 1014616238
+BE 1014616238
+BE 1014729866
+BE 1015634045
+BE 1016426673
+BE 1016600679
+BE 1016600679
+BE 1017649368
+BE 1018214146
+BE 1018699146
+BE 1019933719
+BE 1021425737
+BE 1021957851
+BE 1025453217
+BE 1027367481
+BE 1029067060
+BE 1030172464
+HR 01554625423
+HR 02040448967
+HR 08129951577
+HR 09710116594
+HR 09710116594
+HR 10663663549
+HR 10663663549
+HR 10757059293
+HR 11377032066
+HR 11642311869
+HR 11642311869
+HR 14069513398
+HR 14299551051
+HR 14299551051
+HR 18629637231
+HR 24343131061
+HR 29249157622
+HR 33789366096
+HR 33789366096
+HR 34734835141
+HR 35328808373
+HR 39336270727
+HR 39336270727
+HR 40547768143
+HR 42003905178
+HR 42003905178
+HR 43644971818
+HR 43644971818
+HR 43644971818
+HR 43966367762
+HR 45561404814
+HR 46193476875
+HR 52452475872
+HR 53674250410
+HR 53674250410
+HR 53674250410
+HR 53674250410
+HR 53674250410
+HR 53674250410
+HR 53747311795
+HR 56211904821
+HR 56338510000
+HR 56338510000
+HR 58921039473
+HR 63112900674
+HR 63991444504
+HR 64150104602
+HR 64278511473
+HR 64797210995
+HR 66170601459
+HR 72383284772
+HR 76945714039
+HR 79492122719
+HR 79492122719
+HR 85476136549
+HR 87143125889
+HR 87856102906
+HR 88283563670
+HR 89273653827
+HR 89668863546
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 90656275879
+HR 93823575846
+HR 93823575846
+HR 97003245557
+HR 99087306845
+
diff --git a/idst.txt b/idst.txt
new file mode 100644
index 0000000..f94a9ab
--- /dev/null
+++ b/idst.txt
@@ -0,0 +1,3 @@
+PL 5252312380
+BE 0718628458
+HR 56211904821
\ No newline at end of file
diff --git a/lib_poslog.py b/lib_poslog.py
index fe21ff3..d834aaf 100644
--- a/lib_poslog.py
+++ b/lib_poslog.py
@@ -80,8 +80,10 @@ def get_elm_value(tree, name, default=''):
return default
def dump_buyer_info(buyer, invoice_ref, receipt_ref, p_country):
+ print("dump buyers")
if buyer:
message = f"{p_country},{invoice_ref},{receipt_ref},{buyer.orgid},{buyer.taxID},{buyer.name},{buyer.city},{buyer.street1},{buyer.zip},{buyer.country}\n"
+ print(message)
with open(BUYERS_BROKEN, "a", encoding="utf-8") as lfile:
lfile.write(message)
diff --git a/log.txt b/log.txt
index 0f332bf..ecfcb51 100644
--- a/log.txt
+++ b/log.txt
@@ -981,3 +981,93 @@
2026-02-24 11:28:50.270849 - INFO Receipt AN1920260120103075: Avoid rounding of -0.14 by updating max 88 articles.
2026-02-24 11:28:50.289230 - INFO Receipt AN1920260120103074: Avoid rounding of -0.08 by updating max 85 articles.
2026-02-24 11:28:50.308309 - INFO Receipt AN1920260201102083: Avoid rounding of 0.02 by updating max 8 articles.
+2026-03-12 11:43:16.877496 - WARNING Invoice AN02_T000_260203_1: Buyer country XX does not match company country BE
+2026-03-12 11:43:16.939878 - ERROR No linked receipt file found for A100_T101_260121_30.xml
+2026-03-12 11:45:35.070905 - WARNING Invoice AN02_T000_260203_1: Buyer country XX does not match company country BE
+2026-03-12 11:45:35.108581 - ERROR No linked receipt file found for A100_T101_260121_30.xml
+2026-03-12 11:45:35.280810 - INFO Receipt A10020260103101188: Avoid rounding of -0.01 by updating max 3 articles.
+2026-03-12 11:45:35.290898 - INFO Receipt A10020260115101212: Avoid rounding of -0.01 by updating max 4 articles.
+2026-03-12 11:45:35.332701 - ERROR receipt AN1920260104105115: buyer has insufficient details - repair before export
+2026-03-12 11:45:35.345416 - ERROR No linked receipt file found for AN19_T102_260111_101.xml
+2026-03-12 11:45:35.350302 - ERROR No linked receipt file found for AN19_T102_260111_122.xml
+2026-03-12 11:45:35.354657 - ERROR No linked receipt file found for AN19_T102_260111_117.xml
+2026-03-12 11:45:35.358474 - ERROR No linked receipt file found for AN19_T103_260111_98.xml
+2026-03-12 11:45:35.362851 - ERROR No linked receipt file found for AN19_T103_260111_123.xml
+2026-03-12 11:45:35.367026 - ERROR No linked receipt file found for AN19_T102_260113_87.xml
+2026-03-12 11:45:35.371114 - ERROR No linked receipt file found for AN19_T101_260115_80.xml
+2026-03-12 11:45:35.377378 - ERROR receipt AN1920260104105115: buyer has insufficient details - repair before export
+2026-03-12 11:45:35.383681 - ERROR No linked receipt file found for AN19_T101_260104_148.xml
+2026-03-12 11:45:35.388161 - ERROR No linked receipt file found for AN19_T104_260103_188.xml
+2026-03-12 11:45:35.392386 - ERROR No linked receipt file found for AN19_T101_260103_105.xml
+2026-03-12 11:45:35.396871 - ERROR No linked receipt file found for AN19_T103_260102_255.xml
+2026-03-12 11:45:35.402013 - ERROR No linked receipt file found for AN19_T102_260108_110.xml
+2026-03-12 11:45:35.411347 - ERROR No linked receipt file found for AN19_T104_260110_78.xml
+2026-03-12 11:45:35.493328 - INFO Receipt AN1920260120103075: Avoid rounding of -0.14 by updating max 88 articles.
+2026-03-12 11:45:35.514824 - INFO Receipt AN1920260120103074: Avoid rounding of -0.08 by updating max 85 articles.
+2026-03-12 11:45:35.541657 - INFO Receipt AN1920260201102083: Avoid rounding of 0.02 by updating max 8 articles.
+2026-03-12 11:46:33.848100 - WARNING Invoice AN02_T000_260203_1: Buyer country XX does not match company country BE
+2026-03-12 11:46:33.880421 - ERROR No linked receipt file found for A100_T101_260121_30.xml
+2026-03-12 11:46:33.972187 - INFO Receipt A10020260103101188: Avoid rounding of -0.01 by updating max 3 articles.
+2026-03-12 11:46:33.981501 - INFO Receipt A10020260115101212: Avoid rounding of -0.01 by updating max 4 articles.
+2026-03-12 11:46:34.014710 - ERROR receipt AN1920260104105115: buyer has insufficient details - repair before export
+2026-03-12 11:46:34.023193 - ERROR No linked receipt file found for AN19_T102_260111_101.xml
+2026-03-12 11:46:34.026756 - ERROR No linked receipt file found for AN19_T102_260111_122.xml
+2026-03-12 11:46:34.030094 - ERROR No linked receipt file found for AN19_T102_260111_117.xml
+2026-03-12 11:46:34.032757 - ERROR No linked receipt file found for AN19_T103_260111_98.xml
+2026-03-12 11:46:34.035798 - ERROR No linked receipt file found for AN19_T103_260111_123.xml
+2026-03-12 11:46:34.038968 - ERROR No linked receipt file found for AN19_T102_260113_87.xml
+2026-03-12 11:46:34.041433 - ERROR No linked receipt file found for AN19_T101_260115_80.xml
+2026-03-12 11:46:34.047107 - ERROR receipt AN1920260104105115: buyer has insufficient details - repair before export
+2026-03-12 11:46:34.052946 - ERROR No linked receipt file found for AN19_T101_260104_148.xml
+2026-03-12 11:46:34.055801 - ERROR No linked receipt file found for AN19_T104_260103_188.xml
+2026-03-12 11:46:34.058460 - ERROR No linked receipt file found for AN19_T101_260103_105.xml
+2026-03-12 11:46:34.061759 - ERROR No linked receipt file found for AN19_T103_260102_255.xml
+2026-03-12 11:46:34.065481 - ERROR No linked receipt file found for AN19_T102_260108_110.xml
+2026-03-12 11:46:34.070597 - ERROR No linked receipt file found for AN19_T104_260110_78.xml
+2026-03-12 11:46:34.131931 - INFO Receipt AN1920260120103075: Avoid rounding of -0.14 by updating max 88 articles.
+2026-03-12 11:46:34.151978 - INFO Receipt AN1920260120103074: Avoid rounding of -0.08 by updating max 85 articles.
+2026-03-12 11:46:34.173302 - INFO Receipt AN1920260201102083: Avoid rounding of 0.02 by updating max 8 articles.
+2026-03-12 12:23:15.765003 - WARNING Invoice AN02_T000_260203_1: Buyer country XX does not match company country BE
+2026-03-12 12:23:15.822124 - ERROR No linked receipt file found for A100_T101_260121_30.xml
+2026-03-12 12:23:15.978224 - INFO Receipt A10020260103101188: Avoid rounding of -0.01 by updating max 3 articles.
+2026-03-12 12:23:15.992007 - INFO Receipt A10020260115101212: Avoid rounding of -0.01 by updating max 4 articles.
+2026-03-12 12:23:16.046791 - ERROR receipt AN1920260104105115: buyer has insufficient details - repair before export
+2026-03-12 12:23:16.060196 - ERROR No linked receipt file found for AN19_T102_260111_101.xml
+2026-03-12 12:23:16.072034 - ERROR No linked receipt file found for AN19_T102_260111_122.xml
+2026-03-12 12:23:16.084611 - ERROR No linked receipt file found for AN19_T102_260111_117.xml
+2026-03-12 12:23:16.092492 - ERROR No linked receipt file found for AN19_T103_260111_98.xml
+2026-03-12 12:23:16.097356 - ERROR No linked receipt file found for AN19_T103_260111_123.xml
+2026-03-12 12:23:16.103395 - ERROR No linked receipt file found for AN19_T102_260113_87.xml
+2026-03-12 12:23:16.109512 - ERROR No linked receipt file found for AN19_T101_260115_80.xml
+2026-03-12 12:23:16.117200 - ERROR receipt AN1920260104105115: buyer has insufficient details - repair before export
+2026-03-12 12:23:16.123065 - ERROR No linked receipt file found for AN19_T101_260104_148.xml
+2026-03-12 12:23:16.128018 - ERROR No linked receipt file found for AN19_T104_260103_188.xml
+2026-03-12 12:23:16.132909 - ERROR No linked receipt file found for AN19_T101_260103_105.xml
+2026-03-12 12:23:16.137598 - ERROR No linked receipt file found for AN19_T103_260102_255.xml
+2026-03-12 12:23:16.142263 - ERROR No linked receipt file found for AN19_T102_260108_110.xml
+2026-03-12 12:23:16.152216 - ERROR No linked receipt file found for AN19_T104_260110_78.xml
+2026-03-12 12:23:16.244792 - INFO Receipt AN1920260120103075: Avoid rounding of -0.14 by updating max 88 articles.
+2026-03-12 12:23:16.269411 - INFO Receipt AN1920260120103074: Avoid rounding of -0.08 by updating max 85 articles.
+2026-03-12 12:23:16.294935 - INFO Receipt AN1920260201102083: Avoid rounding of 0.02 by updating max 8 articles.
+2026-03-12 12:24:10.319982 - WARNING Invoice AN02_T000_260203_1: Buyer country XX does not match company country BE
+2026-03-12 12:24:10.357773 - ERROR No linked receipt file found for A100_T101_260121_30.xml
+2026-03-12 12:24:10.455483 - INFO Receipt A10020260103101188: Avoid rounding of -0.01 by updating max 3 articles.
+2026-03-12 12:24:10.466522 - INFO Receipt A10020260115101212: Avoid rounding of -0.01 by updating max 4 articles.
+2026-03-12 12:24:10.499786 - ERROR receipt AN1920260104105115: buyer has insufficient details - repair before export
+2026-03-12 12:24:10.508145 - ERROR No linked receipt file found for AN19_T102_260111_101.xml
+2026-03-12 12:24:10.511826 - ERROR No linked receipt file found for AN19_T102_260111_122.xml
+2026-03-12 12:24:10.514475 - ERROR No linked receipt file found for AN19_T102_260111_117.xml
+2026-03-12 12:24:10.517296 - ERROR No linked receipt file found for AN19_T103_260111_98.xml
+2026-03-12 12:24:10.520011 - ERROR No linked receipt file found for AN19_T103_260111_123.xml
+2026-03-12 12:24:10.522573 - ERROR No linked receipt file found for AN19_T102_260113_87.xml
+2026-03-12 12:24:10.525808 - ERROR No linked receipt file found for AN19_T101_260115_80.xml
+2026-03-12 12:24:10.530370 - ERROR receipt AN1920260104105115: buyer has insufficient details - repair before export
+2026-03-12 12:24:10.534451 - ERROR No linked receipt file found for AN19_T101_260104_148.xml
+2026-03-12 12:24:10.537367 - ERROR No linked receipt file found for AN19_T104_260103_188.xml
+2026-03-12 12:24:10.540454 - ERROR No linked receipt file found for AN19_T101_260103_105.xml
+2026-03-12 12:24:10.543686 - ERROR No linked receipt file found for AN19_T103_260102_255.xml
+2026-03-12 12:24:10.546001 - ERROR No linked receipt file found for AN19_T102_260108_110.xml
+2026-03-12 12:24:10.550573 - ERROR No linked receipt file found for AN19_T104_260110_78.xml
+2026-03-12 12:24:10.609768 - INFO Receipt AN1920260120103075: Avoid rounding of -0.14 by updating max 88 articles.
+2026-03-12 12:24:10.629560 - INFO Receipt AN1920260120103074: Avoid rounding of -0.08 by updating max 85 articles.
+2026-03-12 12:24:10.649139 - INFO Receipt AN1920260201102083: Avoid rounding of 0.02 by updating max 8 articles.
diff --git a/static/comany_data.js b/static/comany_data.js
new file mode 100644
index 0000000..f82f1ef
--- /dev/null
+++ b/static/comany_data.js
@@ -0,0 +1,59 @@
+async function searchCompany() {
+ const country = document.getElementById("country").value;
+ const vatNumber = document.getElementById("vat_number").value.trim();
+ const status = document.getElementById("status");
+
+ if (!country || !vatNumber) {
+ status.textContent = "Please enter a country and VAT number.";
+ return;
+ }
+
+ status.textContent = "Searching...";
+
+ try {
+ const response = await fetch(
+ `/api/company_lookup?country=${encodeURIComponent(country)}&vat_number=${encodeURIComponent(vatNumber)}`
+ );
+
+ const data = await response.json();
+
+ if (!response.ok) {
+ status.textContent = data.error || "Lookup failed.";
+ return;
+ }
+
+ document.getElementById("company_name").value = data.name || "";
+ document.getElementById("street").value = data.street || "";
+ document.getElementById("zip").value = data.zip || "";
+ document.getElementById("city").value = data.city || "";
+
+ let message = data.found ? `Found via ${data.source}.` : "No company found.";
+
+ if (data.warning) {
+ message += `\nWarning: ${data.warning}`;
+ }
+
+ if (data.fallback_error) {
+ message += `\nFallback error: ${data.fallback_error}`;
+ }
+
+ status.textContent = message;
+ } catch (error) {
+ status.textContent = "Request failed: " + error;
+ }
+}
+
+function saveData() {
+ const payload = {
+ country: document.getElementById("country").value,
+ vat_number: document.getElementById("vat_number").value.trim(),
+ company_name: document.getElementById("company_name").value.trim(),
+ street: document.getElementById("street").value.trim(),
+ zip: document.getElementById("zip").value.trim(),
+ city: document.getElementById("city").value.trim(),
+ email: document.getElementById("email").value.trim(),
+ phone: document.getElementById("phone").value.trim()
+ };
+
+ alert(JSON.stringify(payload, null, 2));
+}
diff --git a/static/company_data.css b/static/company_data.css
new file mode 100644
index 0000000..4c6ed83
--- /dev/null
+++ b/static/company_data.css
@@ -0,0 +1,171 @@
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
+
+:root {
+ --bg1: #eef6ff;
+ --bg2: #dcecff;
+ --panel: rgba(255, 255, 255, 0.9);
+ --blue: #1f5fae;
+ --blue-dark: #123d73;
+ --blue-soft: #6fa6e8;
+ --line: #4d8fdb;
+ --text: #1a2f4d;
+ --muted: #4f6f92;
+ --shadow: 0 18px 50px rgba(20, 71, 132, 0.16);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ font-family: 'Inter', sans-serif;
+ font-size: 24px;
+ color: var(--text);
+ background:
+ radial-gradient(circle at top left, #f7fbff 0%, transparent 32%),
+ linear-gradient(135deg, var(--bg1), var(--bg2));
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+ padding: 48px 24px;
+}
+
+.container {
+ width: 100%;
+ max-width: 1100px;
+ background: var(--panel);
+ backdrop-filter: blur(8px);
+ border-radius: 28px;
+ box-shadow: var(--shadow);
+ padding: 40px 48px;
+}
+
+h1 {
+ margin: 0 0 36px 0;
+ color: var(--blue-dark);
+ font-size: 34px;
+ font-weight: 700;
+ letter-spacing: -0.5px;
+}
+
+.form-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 28px;
+}
+
+.row {
+ display: grid;
+ grid-template-columns: 220px 1fr 1fr auto;
+ gap: 20px;
+ align-items: end;
+}
+
+label {
+ font-weight: 600;
+ color: var(--blue);
+ padding-bottom: 8px;
+}
+
+input,
+select {
+ width: 100%;
+ font-family: inherit;
+ font-size: 24px;
+ color: var(--text);
+ padding: 10px 4px;
+ border: none;
+ border-bottom: 2px solid var(--blue-soft);
+ background: transparent;
+ outline: none;
+ transition: border-color 0.2s ease;
+ border-radius: 0;
+}
+
+input:focus,
+select:focus {
+ border-bottom-color: var(--blue);
+ box-shadow: none;
+}
+
+input::placeholder {
+ color: #7f9fc4;
+}
+
+button {
+ font-family: inherit;
+ font-size: 24px;
+ font-weight: 700;
+ padding: 14px 24px;
+ border: none;
+ border-radius: 16px;
+ cursor: pointer;
+ background: linear-gradient(135deg, #3b82d6, #1f5fae);
+ color: white;
+ box-shadow: 0 10px 24px rgba(31, 95, 174, 0.24);
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
+ white-space: nowrap;
+}
+
+button:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 14px 28px rgba(31, 95, 174, 0.28);
+}
+
+button:active {
+ transform: translateY(0);
+}
+
+.button-row {
+ margin-top: 36px;
+}
+
+.status {
+ margin-top: 28px;
+ padding: 20px 24px;
+ border-radius: 18px;
+ background: linear-gradient(135deg, rgba(204, 227, 255, 0.7), rgba(232, 243, 255, 0.95));
+ color: var(--muted);
+ white-space: pre-wrap;
+ font-size: 22px;
+ border: 1px solid rgba(77, 143, 219, 0.18);
+}
+
+.span-3 {
+ grid-column: span 3;
+}
+
+@media (max-width: 900px) {
+ body {
+ padding: 20px;
+ }
+
+ .container {
+ padding: 28px 24px;
+ }
+
+ .row {
+ grid-template-columns: 1fr;
+ gap: 14px;
+ }
+
+ h1 {
+ font-size: 30px;
+ }
+
+ input,
+ select,
+ button {
+ font-size: 20px;
+ }
+
+ .status {
+ font-size: 18px;
+ }
+
+ .span-3 {
+ grid-column: span 1;
+ }
+}
diff --git a/templates/company_data.html b/templates/company_data.html
new file mode 100644
index 0000000..7fd0c0c
--- /dev/null
+++ b/templates/company_data.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+ VAT Company Lookup
+
+
+
+
+
VAT Company Lookup
+
+
+
+
+
+
+
+
Ready.
+
+
+
+
+
\ No newline at end of file