def _parse_statement_properties(self, tree): stmt = tree.find('./s:BkToCstmrStmt/s:Stmt', self.xmlns) bnk = stmt.find('./s:Acct/s:Svcr/s:FinInstnId/s:BIC', self.xmlns) if bnk is None: bnk = stmt.find('./s:Acct/s:Svcr/s:FinInstnId/s:Nm', self.xmlns) iban = stmt.find('./s:Acct/s:Id/s:IBAN', self.xmlns) ccy = stmt.find('./s:Acct/s:Ccy', self.xmlns) bals = stmt.findall('./s:Bal', self.xmlns) acctCurrency = ccy.text if ccy is not None else None if acctCurrency: self.statement.currency = acctCurrency else: if self.statement.currency is None: raise exceptions.ParseError( 0, "No account currency provided in statement. Please " "specify one in configuration file (e.g. currency=EUR)") bal_amts = {} bal_dates = {} for bal in bals: cd = bal.find('./s:Tp/s:CdOrPrtry/s:Cd', self.xmlns) amt = bal.find('./s:Amt', self.xmlns) dt = bal.find('./s:Dt', self.xmlns) amt_ccy = amt.get('Ccy') # Amount currency should match with statement currency if amt_ccy != self.statement.currency: continue bal_amts[cd.text] = self._parse_amount(amt) bal_dates[cd.text] = self._parse_date(dt) if not bal_amts: raise exceptions.ParseError( 0, "No statement balance found for currency '%s'. Check " "currency of statement file." % self.statement.currency) self.statement.bank_id = bnk.text if bnk is not None else None self.statement.account_id = iban.text if 'OPBD' in bal_amts: self.statement.start_balance = bal_amts['OPBD'] self.statement.start_date = bal_dates['OPBD'] elif 'PRCD' in bal_amts: self.statement.start_balance = bal_amts['PRCD'] self.statement.start_date = bal_dates['PRCD'] else: raise exceptions.ParseError( 0, "No statement opening balance found for currency '%s'. Check " "currency of statement file." % self.statement.currency) if 'CLBD' in bal_amts: self.statement.end_balance = bal_amts['CLBD'] self.statement.end_date = bal_dates['CLBD'] else: raise exceptions.ParseError( 0, "No statement closing balance found for currency '%s'. Check " "currency of statement file." % self.statement.currency)
def parse(self): """Main entry point for parsers """ self.statements = [] tree = ET.parse(self.filename) # Find out XML namespace and make sure we can parse it ns = self._get_namespace(tree.getroot()) if not ns.startswith(ISO20022_NAMESPACE_ROOT): raise exceptions.ParseError(0, "Cannot recognize ISO20022 XML") self.xmlns = { "s": ns } for stmt in tree.findall('./s:BkToCstmrStmt/s:Stmt', self.xmlns): self.statements.append(self._parse_statement(stmt)) return self.statements
def parse(self): """Main entry point for parsers """ self.statement = Statement() self.statement.currency = self.currency tree = ET.parse(self.filename) # Find out XML namespace and make sure we can parse it ns = self._get_namespace(tree.getroot()) if not ns.startswith(ISO20022_NAMESPACE_ROOT): raise exceptions.ParseError(0, "Cannot recognize ISO20022 XML") self.xmlns = { "s": ns } self._parse_statement_properties(tree) self._parse_lines(tree) return self.statement
def parse(self): raise exceptions.ParseError(23, "Catastrophic error")
def _parse_statement_properties(self, tree: ET.ElementTree) -> None: stmt = self._get_statement_el(tree) bnk = stmt.find("./s:Acct/s:Svcr/s:FinInstnId/s:BIC", self.xmlns) if bnk is None: bnk = stmt.find("./s:Acct/s:Svcr/s:FinInstnId/s:Nm", self.xmlns) iban = stmt.find("./s:Acct/s:Id/s:IBAN", self.xmlns) assert iban is not None ccy = stmt.find("./s:Acct/s:Ccy", self.xmlns) bals = stmt.findall("./s:Bal", self.xmlns) acctCurrency = ccy.text if ccy is not None else None if acctCurrency: self.statement.currency = acctCurrency else: if self.statement.currency is None: raise exceptions.ParseError( 0, "No account currency provided in statement. Please " "specify one in configuration file (e.g. currency=EUR)", ) bal_amts = {} bal_dates = {} for bal in bals: cd = bal.find("./s:Tp/s:CdOrPrtry/s:Cd", self.xmlns) amt = bal.find("./s:Amt", self.xmlns) dt = bal.find("./s:Dt", self.xmlns) assert cd is not None assert amt is not None amt_ccy = amt.get("Ccy") # Amount currency should match with statement currency if amt_ccy != self.statement.currency: continue bal_amts[cd.text] = self._parse_amount(amt) bal_dates[cd.text] = self._parse_date(dt) if not bal_amts: raise exceptions.ParseError( 0, "No statement balance found for currency '%s'. Check " "currency of statement file." % self.statement.currency, ) self.statement.bank_id = bnk.text if bnk is not None else None self.statement.account_id = iban.text # From ISO 20022 Account Statement Guide: # # The following balance types are mandatory in the Bank-To-Customer # statement: # # OPBD – Book balance of the account at the beginning of the account # reporting period. - PRCD - Closing book balance of the previous day; # to be supplied in the material with the OPBD with similar data (see # the camt.053 example message), except for the PRCD type balance, # which has the same date as the previous day‟s CLBD type balance. The # date of OPBD type balance is the date of the reported account # statement. # # CLBD - Closing book balance of the account at the end of the account # reporting period. self.statement.start_balance = bal_amts.get("OPBD", bal_amts.get("PRCD")) self.statement.start_date = bal_dates.get("OPBD", bal_dates.get("PRCD")) self.statement.end_balance = bal_amts["CLBD"] self.statement.end_date = bal_dates["CLBD"]
def _recognize_version(self, ns: str) -> CamtVersion: for ver in CamtVersion: if ns.startswith(ver.value): return ver raise exceptions.ParseError(0, "Cannot recognize ISO20022 XML")
def _findstrict(self, tree: ET.Element, spath: str) -> ET.Element: found = self._find(tree, spath) if found is None: raise exceptions.ParseError(0, f"{spath} is not found in {tree}") return found