def parse(self, rules_dir: Optional[Path]) -> BankStatement:
        m = re.search(r'DATE DE PAIEMENT *(\d\d \S* \d{4})', self.summary_text)
        if m is None:
            raise PayfitPdfParserError('Could not find payment date.')
        payment_date = parse_verbose_date(m.group(1))
        transaction = MultiTransaction('Salaire', payment_date)

        salary_postings, total_gross_salary = self._parse_salary()

        social_security_postings, social_security_total = \
                self._parse_social_security_payments()
        transportation_postings, transportation_reimbursed \
                = self._parse_travel_reimbursement()
        meal_vouchers = self._parse_meal_vouchers()

        m = self.net_before_taxes_pattern.search(self.summary_text)
        if m is None:
            raise PayfitPdfParserError('Could not find net before taxes.')
        net_before_taxes = parse_amount(m.group(1))
        income_tax = self._parse_tax_deducted_at_source()
        payment = self._parse_payment()

        assert (-total_gross_salary - transportation_reimbursed +
                meal_vouchers.amount + social_security_total +
                income_tax.amount + payment.amount == 0)
        transaction.add_posting(payment)
        for p in salary_postings:
            transaction.add_posting(p)
        transaction.add_posting(income_tax)
        for p in social_security_postings:
            transaction.add_posting(p)
        transaction.add_posting(meal_vouchers)
        for p in transportation_postings:
            transaction.add_posting(p)
        return BankStatement(None, [transaction])
Exemple #2
0
 def parse_raw(self) -> BankStatement:
     with self.csv_file.open(encoding='LATIN1', newline='') as f:
         f.seek(self.csv_start)
         reader = csv.DictReader(f, fieldnames=self.csv_fieldnames,
                                 dialect=IngCsvDialect)
         transactions: list[BaseTransaction] = []
         for row in reader:
             transaction_type = row['Buchungstext']
             destination = row['Auftraggeber/Empfänger']
             description = destination + ' | ' + row['Verwendungszweck']
             currency = row['Währung']
             if currency == 'EUR':
                 currency = '€'
             transaction = Transaction(
                     account=self.account,
                     description=description,
                     operation_date=parse_date(row['Buchung']),
                     value_date=parse_date(row['Valuta']),
                     amount=parse_amount(row['Betrag']),
                     currency=currency,
                     metadata={
                         'type': transaction_type,
                         },
                     )
             transactions.append(transaction)
         transactions = list(reversed(transactions))
         return BankStatement(self.account, transactions)
 def parse(self, rules_dir: Optional[Path]) -> BankStatement:
     metadata = self.parse_metadata()
     transaction = MultiTransaction(metadata.description,
                                    metadata.payment_date)
     net_total = self._parse_main_table(transaction)
     payment_total = self._parse_payment_table(transaction)
     assert net_total == payment_total
     assert transaction.is_balanced()
     return BankStatement(None, [transaction])
 def parse_raw(self) -> BankStatement:
     self.transactions_text = self.extract_transactions_table()
     self.parse_balances()
     transactions = [
         t for t in self.generate_transactions(self.transactions_start,
                                               self.transactions_end)
     ]
     return BankStatement(self.account, transactions, self.old_balance,
                          self.new_balance)
 def parse_raw(self) -> BankStatement:
     if not self.qif_file.exists():
         raise IOError(f'Unknown file: {self.qif_file}')
     with open(self.qif_file) as f:
         header = f.readline()
         if not header == '!Type:Bank\n':
             raise RuntimeError(f'Unknown QIF account type: {header}')
         transactions = self._parse_transactions(f)
     return BankStatement(account=self.account,
                   transactions=transactions)
    def parse(self, rules_dir: Optional[Path]) -> BankStatement:
        m = re.search(r'Date de paiement\s*Période de paie\n'
                      r'\s*BULLETIN DE PAIE'
                      r'\s*(\d\d/\d\d/\d{4})\s*'
                      r's*DU (\d\d/\d\d/\d{4}) AU (\d\d/\d\d/\d{4})',
                      self.pdf_pages[0])
        if m is None:
            raise BouyguesPdfParserError('Could not find payment date.')
        payment_date = parse_date(m.group(1))
        payment_period = (parse_date(m.group(2)), parse_date(m.group(3)))
        description = f'Salaire du {payment_period[0]} au {payment_period[1]}'
        transaction = MultiTransaction(description, payment_date)

        lines = self.iter_main_table()
        header = next(lines)
        if header.is_section_header() and header.description == 'ELEMENTS DE REVENU BRUT':
            salary_postings, total_gross_salary \
                    = self._parse_gross_income(lines)
        else:
            assert header.description == "AUTRES CONTRIBUTIONS DUES PAR L'EMPLOYEUR"
            salary_postings = []
            total_gross_salary = Decimal('0.00')
        social_security_postings, social_security_total = \
                self._parse_social_security_payments(lines)
        misc_postings, misc_total = self._parse_misc(lines)
        net_before_taxes, income_tax = self._parse_net_income(lines)
        payment = self._parse_payment(lines)

        assert(total_gross_salary
               + social_security_total
               + misc_total
               - income_tax.amount - payment.amount == 0)
        transaction.add_posting(payment)
        for p in salary_postings:
            transaction.add_posting(p)
        transaction.add_posting(income_tax)
        for p in social_security_postings:
            transaction.add_posting(p)
        for p in misc_postings:
            transaction.add_posting(p)
        assert transaction.is_balanced()
        return BankStatement(None, [transaction])
def travel_history_to_bank_statement(transactions: list[OvTransaction],
                                     recharge_account: str) -> BankStatement:
    def convert_transaction(transaction: OvTransaction) -> Transaction:
        # TODO: That looks like a nice case for structural pattern matching
        #       once we depend on Python 3.10.
        if transaction.type == 'recharge':
            account = recharge_account
        elif transaction.mode_of_transportation is None:
            raise RuntimeError(
                'Expected mode_of_transportation for transaction'
                f' {transaction}.')
        elif transaction.mode_of_transportation.startswith('Trein'):
            account = 'expenses:transportation:train'
        elif transaction.mode_of_transportation.startswith('Bus'):
            account = 'expenses:transportation:bus'
        else:
            raise RuntimeError('Unknown mode of transportation'
                               f' "{transaction.mode_of_transportation}" in'
                               f' transaction {transaction}.')
        description = 'OV-Chipkaart |'
        if transaction.mode_of_transportation is None:
            description += ' ' + transaction.type
        else:
            description += ' ' + transaction.mode_of_transportation
        if transaction.place:
            description += ' | ' + transaction.place
        return Transaction(
            account='assets:OV-Chipkaart',
            description=description,
            operation_date=transaction.date,
            value_date=None,
            amount=transaction.amount,
            currency='€',
            external_account=account,
        )

    return BankStatement(
        account='assets:OV-Chipkaart',
        transactions=[convert_transaction(t) for t in transactions],
    )
Exemple #8
0
    def _add_interest_details(self, bank_statement: BankStatement) -> None:
        interests = self.parse_interest_postings()
        interest_transaction = cast(Transaction,
                                    bank_statement.transactions[-1])
        assert interest_transaction.type == 'Zinsertrag'
        interests_sum = sum(i.amount for i in interests)
        assert interest_transaction.amount + interests_sum == 0
        interest_transaction.to_multi_transaction()

        mt = MultiTransaction(
                description=interest_transaction.description,
                transaction_date=interest_transaction.operation_date,
                metadata=interest_transaction.metadata)
        mt.add_posting(Posting(
                        interest_transaction.account,
                        interest_transaction.amount,
                        interest_transaction.currency,
                        interest_transaction.value_date))
        for posting in interests:
            mt.add_posting(posting)

        bank_statement.transactions[-1] = mt
 def parse_raw(self) -> BankStatement:
     transactions = list(reversed(list(self._iter_main_table())))
     return BankStatement(self.account, transactions, self.old_balance,
                          self.new_balance)
 def parse_raw(self) -> BankStatement:
     #self.check_transactions_consistency(self.transactions)
     return BankStatement(self.account, self.transactions)