first release of comapny-data app
This commit is contained in:
@@ -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
|
||||
|
||||
|
@@ -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,
|
||||
|
||||
|
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>VAT Company Lookup</title>
|
||||
<style>
|
||||
@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);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>VAT Company Lookup</h1>
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="row">
|
||||
<label for="country">Country / VAT</label>
|
||||
<select id="country">
|
||||
<option value="BE">Belgium</option>
|
||||
<option value="HR">Croatia</option>
|
||||
<option value="PL">Poland</option>
|
||||
</select>
|
||||
<input id="vat_number" type="text" placeholder="VAT number">
|
||||
<button type="button" onclick="searchCompany()">Search</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="company_name">Company</label>
|
||||
<input id="company_name" type="text" style="grid-column: span 3;">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="street">Street</label>
|
||||
<input id="street" type="text" style="grid-column: span 3;">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="zip">ZIP / City</label>
|
||||
<input id="zip" type="text" placeholder="ZIP">
|
||||
<input id="city" type="text" placeholder="City">
|
||||
<div></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="email">Email</label>
|
||||
<input id="email" type="email" placeholder="example@company.com" style="grid-column: span 3;">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="phone">Phone</label>
|
||||
<input id="phone" type="text" placeholder="+32 123 45 67 89" style="grid-column: span 3;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-row">
|
||||
<button type="button" onclick="saveData()">Save</button>
|
||||
</div>
|
||||
|
||||
<div id="status" class="status">Ready.</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
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 msg = data.found ? `Found via ${data.source}.` : "No company found.";
|
||||
|
||||
if (data.warning) {
|
||||
msg += `\\nWarning: ${data.warning}`;
|
||||
}
|
||||
if (data.fallback_error) {
|
||||
msg += `\\nFallback error: ${data.fallback_error}`;
|
||||
}
|
||||
|
||||
status.textContent = msg;
|
||||
} catch (err) {
|
||||
status.textContent = "Request failed: " + err;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def index():
|
||||
return render_template_string(HTML)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>VAT Company Lookup</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>VAT Company Lookup</h1>
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="row">
|
||||
<label for="country">Country / VAT</label>
|
||||
<select id="country">
|
||||
<option value="BE">Belgium</option>
|
||||
<option value="HR">Croatia</option>
|
||||
<option value="PL">Poland</option>
|
||||
</select>
|
||||
<input id="vat_number" type="text" placeholder="VAT number">
|
||||
<button type="button" onclick="searchCompany()">Search</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="company_name">Company</label>
|
||||
<input id="company_name" type="text" class="span-3">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="street">Street</label>
|
||||
<input id="street" type="text" class="span-3">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="zip">ZIP / City</label>
|
||||
<input id="zip" type="text" placeholder="ZIP">
|
||||
<input id="city" type="text" placeholder="City">
|
||||
<div></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="email">Email</label>
|
||||
<input id="email" type="email" placeholder="example@company.com" class="span-3">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label for="phone">Phone</label>
|
||||
<input id="phone" type="text" placeholder="+32 123 45 67 89" class="span-3">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-row">
|
||||
<button type="button" onclick="saveData()">Save</button>
|
||||
</div>
|
||||
|
||||
<div id="status" class="status">Ready.</div>
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user