diff --git a/src/VATValidation.fbp b/src/VATValidation.fbp index c2084b3..43d59a7 100644 --- a/src/VATValidation.fbp +++ b/src/VATValidation.fbp @@ -128,7 +128,7 @@ Single 0 - + 1 1 1 @@ -180,16 +180,16 @@ wxTAB_TRAVERSAL - + bSizer3 wxVERTICAL none - + 5 wxEXPAND 1 - + 3 wxBOTH 1 @@ -530,11 +530,11 @@ - + 5 wxEXPAND 1 - + 0 protected 0 @@ -667,11 +667,11 @@ - + 5 wxEXPAND 1 - + 0 protected 0 @@ -804,11 +804,11 @@ - + 5 wxEXPAND 1 - + 0 protected 0 @@ -941,11 +941,11 @@ - + 5 wxEXPAND 1 - + 0 protected 0 @@ -1078,21 +1078,21 @@ - + 5 wxEXPAND 1 - + 0 protected 0 - + 5 wxEXPAND 1 - + 0 protected 0 @@ -1175,11 +1175,11 @@ - + 5 wxEXPAND 1 - + 2 wxBOTH 1 @@ -1652,7 +1652,7 @@ Batch - 0 + 1 1 1 @@ -1958,7 +1958,7 @@ Resizable 1 - wxFLP_DEFAULT_STYLE + wxFLP_SAVE|wxFLP_USE_TEXTCTRL ; ; forward_declare 0 @@ -2064,7 +2064,7 @@ Configuration - 1 + 0 1 1 diff --git a/src/batch.py b/src/batch.py index 04d6643..63e3a51 100644 --- a/src/batch.py +++ b/src/batch.py @@ -18,12 +18,16 @@ import settings # import common libraries +import logging +import json import pandas as pd +logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s') + columns = ["key1", "key2", "ownvat", "foreignvat", "company", "street", "zip", "town"] -def validatebatch(inputfile, outputfile="", type="vies", lang="en", iscli=False): +def validatebatch(inputfile, outputfile="", type="vies", lang="en"): """ Validate the batch file and write the results to the output file. """ @@ -31,123 +35,162 @@ def validatebatch(inputfile, outputfile="", type="vies", lang="en", iscli=False) ext = inputfile.split(".")[-1].lower() # if the output file is not set, use the input file with a different extension + # using the UI, this error can't happen, because the user has to select the output file if not outputfile: outputfile = inputfile.replace(ext, f"log.{ext}") match ext: case "csv": resultcode = processcsv(inputfile, outputfile, type, lang) - if iscli: - return resultcode + return resultcode case "xlsx": resultcode = processxlsx(inputfile, outputfile, type, lang) - if iscli: - return resultcode + return resultcode case "json": resultcode = processjson(inputfile, outputfile, type, lang) - if iscli: - return resultcode + return resultcode case _: print("Unsupported file format") return 127 def processcsv(inputfile, outputfile, type, lang): - # read csv with columns - data = pd.read_csv( - inputfile, - names=columns, - delimiter=settings.load_value_from_json_file("delimiter"), - ) - # create a list to store the results - results = [] - # iterate over the rows - for index, row in data.iterrows(): - # skip first line, because it contains the column names - if index == 0: - continue - # validate the row - message = single.validatesingle( - key1=row["key1"], - key2=row["key2"], - ownvat=row["ownvat"], - foreignvat=row["foreignvat"], - company=row["company"], - street=row["street"], - zip=row["zip"], - town=row["town"], - type=type, - lang=lang, - iscli=True + try: + # read csv with columns + data = pd.read_csv( + inputfile, + names=columns, + delimiter=settings.load_value_from_json_file("delimiter"), ) - # append the result to the results list - results.append(message) - - # load the results into a DataFrame - dataframe = pd.DataFrame(results) - - # save the dateframe to a csv file - dataframe.to_csv(outputfile, index=False, header=False) - - return True + # create a list to store the results + results = [] + # iterate over the rows + for index, row in data.iterrows(): + # skip first line, because it contains the column names + if index == 0: + continue + # validate the row + message = single.validatesingle( + key1=row["key1"], + key2=row["key2"], + ownvat=row["ownvat"], + foreignvat=row["foreignvat"], + company=row["company"], + street=row["street"], + zip=row["zip"], + town=row["town"], + type=type, + lang=lang + ) + + # parse everything as string to easily replace newlines + tempstring = json.dumps(message) + tempstring = tempstring.replace('\\n', ' ').replace('\\r', ' ') + tempstring = tempstring.replace(' ', ' ') + message = json.loads(tempstring) + + # append the result to the results list + results.append(message) + + # load the results into a DataFrame + dataframe = pd.DataFrame(results) + + # save the dateframe to a csv file + dataframe.to_csv(outputfile, index=False, header=False) + + return 0 + + except FileNotFoundError as e: + logging.error(f"File not found: {e}") + return 1 + except pd.errors.EmptyDataError as e: + logging.error(f"Empty data error: {e}") + return 2 + except Exception as e: + logging.error(f"An unexpected error occurred: {e}") + return 99 def processxlsx(inputfile, outputfile, type, lang): - # read the input file - data = pd.read_excel(inputfile, usecols=columns) - # create a list to store the results - results = [] - # iterate over the rows - for index, row in data.iterrows(): - # validate the row - _, message = single.validatesingle( - key1=row["key1"], - key2=row["key2"], - ownvat=row["ownvat"], - foreignvat=row["foreignvat"], - company=row["company"], - street=row["street"], - zip=row["zip"], - town=row["town"], - type=type, - lang=lang, - iscli=True, - ) - # append the result to the results list - results.append(message) - - # load the results into a DataFrame - dataframe = pd.DataFrame(results) - - # save the dateframe to a csv file - dataframe.to_excel(outputfile, index=False, header=False) + try: + # read the input file + data = pd.read_excel(inputfile, usecols=columns) + # create a list to store the results + results = [] + # iterate over the rows + for index, row in data.iterrows(): + # validate the row + message = single.validatesingle( + key1=row["key1"], + key2=row["key2"], + ownvat=row["ownvat"], + foreignvat=row["foreignvat"], + company=row["company"], + street=row["street"], + zip=row["zip"], + town=row["town"], + type=type, + lang=lang + ) + # append the result to the results list + results.append(message) + + # load the results into a DataFrame + dataframe = pd.DataFrame(results) + + # save the dateframe to a csv file + dataframe.to_excel(outputfile, index=False, header=False) + + return 0 + + except FileNotFoundError as e: + logging.error(f"File not found: {e}") + return 1 + except pd.errors.EmptyDataError as e: + logging.error(f"Empty data error: {e}") + return 2 + except Exception as e: + logging.error(f"An unexpected error occurred: {e}") + return 99 def processjson(inputfile, outputfile, type, lang): - data = pd.read_json(inputfile) - # create a list to store the results - results = [] - # iterate over the rows - for index, row in data.iterrows(): - # validate the row - _, message = single.validatesingle( - key1=row["key1"], - key2=row["key2"], - ownvat=row["ownvat"], - foreignvat=row["foreignvat"], - company=row["company"], - street=row["street"], - zip=row["zip"], - town=row["town"], - type=type, - lang=lang, - iscli=True, - ) - # append the result to the results list - results.append(message) - - # load the results into a DataFrame - dataframe = pd.DataFrame(results) - - # save the dateframe to a json file - dataframe.to_json(outputfile, orient="records", lines=False, indent=2) + try: + data = pd.read_json(inputfile) + # create a list to store the results + results = [] + # iterate over the rows + for index, row in data.iterrows(): + # validate the row + message = single.validatesingle( + key1=row["key1"], + key2=row["key2"], + ownvat=row["ownvat"], + foreignvat=row["foreignvat"], + company=row["company"], + street=row["street"], + zip=row["zip"], + town=row["town"], + type=type, + lang=lang + ) + # append the result to the results list + results.append(message) + + # load the results into a DataFrame + dataframe = pd.DataFrame(results) + + # save the dateframe to a json file + dataframe.to_json(outputfile, orient="records", lines=False, indent=2) + + return 0 + + except FileNotFoundError as e: + logging.error(f"File not found: {e}") + return 1 + except pd.errors.EmptyDataError as e: + logging.error(f"Empty data error: {e}") + return 2 + except Exception as e: + logging.error(f"An unexpected error occurred: {e}") + return 99 diff --git a/src/gui.py b/src/gui.py index e96c356..e7f22ec 100644 --- a/src/gui.py +++ b/src/gui.py @@ -183,7 +183,7 @@ def __init__( self, parent ): fgSizer3.Add( self.staticText_Outputfile, 0, wx.ALL, 5 ) - self.filepickerOutput = wx.FilePickerCtrl( self.panelBatch, wx.ID_ANY, wx.EmptyString, _(u"Select a file"), _(u"*.*"), wx.DefaultPosition, wx.DefaultSize, wx.FLP_DEFAULT_STYLE ) + self.filepickerOutput = wx.FilePickerCtrl( self.panelBatch, wx.ID_ANY, wx.EmptyString, _(u"Select a file"), _(u"*.*"), wx.DefaultPosition, wx.DefaultSize, wx.FLP_SAVE|wx.FLP_USE_TEXTCTRL ) fgSizer3.Add( self.filepickerOutput, 1, wx.ALL|wx.EXPAND, 5 ) diff --git a/src/helper.py b/src/helper.py index b6cb1ce..a6465c0 100644 --- a/src/helper.py +++ b/src/helper.py @@ -18,7 +18,7 @@ import logging -VERSION = "v2024-07-28" +VERSION = "v2024-08-17" UPDATEURL = 'https://api.github.com/repos/dseichter/VATValidation/releases/latest' RELEASES = 'https://github.com/dseichter/VATValidation/releases' NAME = 'VAT-Validation' diff --git a/src/single.py b/src/single.py index 9193233..e988faf 100644 --- a/src/single.py +++ b/src/single.py @@ -26,8 +26,7 @@ def validatesingle( zip="", town="", type="vies", - lang="en", - iscli=True + lang="en" ): data = { "key1": key1, @@ -41,6 +40,6 @@ def validatesingle( "type": type, "lang": lang, } - r = validate_workflow.start_validation(data, iscli) + r = validate_workflow.start_workflow(data) return r diff --git a/src/validate_bzst.py b/src/validate_bzst.py index 66ee703..77e5183 100644 --- a/src/validate_bzst.py +++ b/src/validate_bzst.py @@ -74,9 +74,8 @@ def load_codes(lang, errorcode): return None -def start_validation(payload, iscli=True): +def start_validation(payload): logger.debug(payload) - logger.debug(iscli) # map requested fields to bzst request bzstmap = { diff --git a/src/validate_hmrc.py b/src/validate_hmrc.py index 688d9c0..c57e8d8 100644 --- a/src/validate_hmrc.py +++ b/src/validate_hmrc.py @@ -84,9 +84,8 @@ def load_codes(lang, message): return message -def start_validation(payload, iscli=True): +def start_validation(payload): logger.debug(payload) - logger.debug(iscli) try: resp = http.request("GET", URL + payload["foreignvat"][2:]) @@ -113,9 +112,7 @@ def start_validation(payload, iscli=True): if "target" in result: validationresult["company"] = result["target"]["name"] validationresult["address"] = ( - result["target"]["address"]["line1"].strip() + "" - if iscli - else chr(13) + result["target"]["address"]["line2"].strip() + result["target"]["address"]["line1"].strip() + " " + result["target"]["address"]["line2"].strip() ) validationresult["town"] = "" validationresult["zip"] = result["target"]["address"]["postcode"] diff --git a/src/validate_vies.py b/src/validate_vies.py index 3305358..7d35a6b 100644 --- a/src/validate_vies.py +++ b/src/validate_vies.py @@ -87,9 +87,8 @@ def load_codes(lang, errorcode): return None -def start_validation(payload, iscli=True): +def start_validation(payload): logger.debug(payload) - logger.debug(iscli) foreign_vat = payload["foreignvat"] own_vat = payload["ownvat"] @@ -109,7 +108,6 @@ def start_validation(payload, iscli=True): """ - try: resp = http.request("POST", URL, headers=HEADERS, body=requestpayload) @@ -163,9 +161,11 @@ def start_validation(payload, iscli=True): result["requestDate"] = None # in case of faultcode try: - result["errorcode"] = ( - node.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue - ) + result["errorcode"] = "" + if node.getElementsByTagName("faultstring"): + result["errorcode"] = ( + node.getElementsByTagName("faultstring")[0].childNodes[0].nodeValue + ) except Exception as e: result["errorcode"] = "INVALID_INPUT" diff --git a/src/validate_workflow.py b/src/validate_workflow.py index 26e70a8..fa86d3e 100644 --- a/src/validate_workflow.py +++ b/src/validate_workflow.py @@ -66,7 +66,7 @@ def return_fielderror(fieldname): } -def start_validation(payload, iscli=True): +def start_workflow(payload): logger.debug(payload) required_fields = ["key1", "key2", "ownvat", "foreignvat", "company", "town", "zip", "street"] @@ -86,13 +86,12 @@ def start_validation(payload, iscli=True): payload["lang"] = "en" # start the validation - if payload["ownvat"].upper().startswith("DE") and not payload[ - "foreignvat" - ].upper().startswith("GB"): - response = validate_bzst.start_validation(payload, iscli) - elif payload["foreignvat"].upper().startswith("GB"): - response = validate_hmrc.start_validation(payload, iscli) + # Use hmrc for GB VAT numbers, otherwise use given type + if payload["foreignvat"].upper().startswith("GB"): + response = validate_hmrc.start_validation(payload) + elif payload["type"] == "bzst": + response = validate_bzst.start_validation(payload) else: - response = validate_vies.start_validation(payload, iscli) + response = validate_vies.start_validation(payload) return response diff --git a/src/vatvalidation.py b/src/vatvalidation.py index 6580727..4232055 100644 --- a/src/vatvalidation.py +++ b/src/vatvalidation.py @@ -158,14 +158,13 @@ def validateSingle(self, event): zip=self.textZip.GetValue(), town=self.textTown.GetValue(), type=settings.load_value_from_json_file("interface"), - lang=settings.load_value_from_json_file("language"), - iscli=False, + lang=settings.load_value_from_json_file("language") ) self.textResultIsValid.SetValue("Yes" if message["valid"] else "No") self.textResultCode.SetValue(message["errorcode"]) self.textResultDetails.SetValue(message.get("errorcode_description", "")) - # In case of empty errorcode_description, load the company, address twon, zip and street into textResultDetails + # In case of empty errorcode_description, load the company, address town, zip and street into textResultDetails if message.get("errorcode_description", "") == "": self.textResultDetails.SetValue( f"Company: {message['company']}\nAddress: {message['address']}\nTown: {message['town']}\nZip: {message['zip']}\nStreet: {message['street']}" @@ -188,13 +187,26 @@ def validateBatch(self, event): ) return - batch.validatebatch( + # check if given file exists + if not self.filepickerInput.GetPath(): + wx.MessageBox( + "Please select an input file.", "No input file", wx.OK | wx.ICON_ERROR + ) + return + + resultcode = batch.validatebatch( inputfile=self.filepickerInput.GetPath(), outputfile=self.filepickerOutput.GetPath(), type=settings.load_value_from_json_file("interface"), - lang=settings.load_value_from_json_file("language"), + lang=settings.load_value_from_json_file("language") ) + if resultcode == 127: + wx.MessageBox( + "Unsupported file format.", "Unsupported file format", wx.OK | wx.ICON_ERROR + ) + return + # if done, show a message box wx.MessageBox( "Batch validation done.", "Batch Validation", wx.OK | wx.ICON_INFORMATION diff --git a/src/vatvalidation_cli.py b/src/vatvalidation_cli.py index a1d1170..357b36f 100644 --- a/src/vatvalidation_cli.py +++ b/src/vatvalidation_cli.py @@ -53,7 +53,7 @@ + " and output file: " + args.output ) -response = batch.validatebatch(inputfile=args.input, outputfile=args.output, iscli=True) +response = batch.validatebatch(inputfile=args.input, outputfile=args.output) match response: case 0: @@ -62,9 +62,6 @@ case 127: print("Unsupported file format") exit(127) - case True: - print("Validation successful") - exit(0) case _: print("Validation failed: " + str(response)) exit(1)