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