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)
Пример #2
0
    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
Пример #3
0
    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
Пример #4
0
 def parse(self):
     raise exceptions.ParseError(23, "Catastrophic error")
Пример #5
0
    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"]
Пример #6
0
    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")
Пример #7
0
 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