Beispiel #1
0
def parse_account_transaction(bank_config, account_config, transaction):
    amount = transaction['amount']['amount']
    transaction_code = get_nested_item(transaction, 'scheme.subCategory.id')

    if transaction_code == '0054':  # "Otros ..."
        transaction_code = get_nested_item(transaction, 'concept.id')

    transation_direction = TransactionDirection.CHARGE if amount < 0 else TransactionDirection.INCOME
    transaction_type = get_type(transaction_code, transation_direction)

    details = get_account_transaction_details(transaction, transaction_type)
    details['account'] = Account.from_config(account_config)
    details['bank'] = Bank.from_config(bank_config)

    card_number = details.pop('card_number', None)
    used_card = get_card(account_config, card_number)

    keywords = extract_keywords(
        chain(extract_literals(transaction, KEYWORD_FIELDS),
              filter(lambda value: isinstance(value, str), details.values())))

    comment = get_comment(details, transaction_type)

    source = get_source(details, transaction_type)
    destination = get_destination(details, transaction_type)
    del details['bank']

    return ParsedBankAccountTransaction(
        transaction_id=transaction['id'],
        type=transaction_type,
        currency=transaction['amount']['currency']['code'],
        amount=amount,
        balance=transaction['balance']['availableBalance']['amount'],
        value_date=decode_date(transaction['valueDate']),
        transaction_date=decode_date(transaction['transactionDate']),
        source=source,
        destination=destination,
        account=details.pop('account'),
        card=used_card,
        details=details,
        keywords=keywords,
        comment=comment if comment is not None else '',
    )
Beispiel #2
0
    def set_detail(fieldname, xpath_string_or_list, fmt=None, default=None):
        nonlocal details

        xpath_list = xpath_string_or_list if isinstance(xpath_string_or_list, list) else [xpath_string_or_list]

        for xpath in xpath_list:
            value = get_nested_item(transaction, xpath)
            formatted_value = None if value is None else (value if fmt is None else fmt(value))
            if formatted_value is not None:
                details[fieldname] = formatted_value
                return

        details[fieldname] = default
Beispiel #3
0
def _check_MatchCondition(condition, transaction):
    field = get_nested_item(transaction, condition.fieldname)
    if isinstance(field, datatypes.TransactionSubject):
        field_value = field.name
    elif isinstance(field, datatypes.UnknownSubject):
        return False
    else:
        field_value = field

    def included(ok, value):
        return condition.operator(
            ok,
            value in field_value
        )

    def equals(ok, value):
        return condition.operator(
            ok,
            value == field_value
        )

    def match(ok, value):
        return condition.operator(
            ok,
            bool(re.match(value, field_value, re.IGNORECASE))
        )

    def search(ok, value):
        return condition.operator(
            ok,
            bool(re.search(value, field_value, re.IGNORECASE))
        )

    if isinstance(field_value, list):
        value_checker = included
    elif not condition.regex:
        value_checker = equals
    # Cannot do a regex on a None
    elif field_value is None:
        return False
    if condition.regex == 'search':
        value_checker = search
    elif condition.regex == 'match':
        value_checker = match

    return reduce(
        value_checker,
        condition.values,
        initial_value(condition.operator)
    )
Beispiel #4
0
    def capture_value(source, regex, transaction):
        field = get_nested_item(transaction, source)
        if isinstance(field, datatypes.TransactionSubject):
            source_value = field.name
        elif isinstance(field, datatypes.UnknownSubject):
            return False
        else:
            source_value = field

        match = re.search(regex, source_value, re.IGNORECASE)
        if match:
            try:
                return match.groups()[capture_group]
            except IndexError:
                return source_value
        else:
            return source_value
Beispiel #5
0
def get_last_update_time(metadata, bank, account_type, identifier):
    return get_nested_item(
        metadata, '{}.{}.{}.updated'.format(bank, account_type, identifier))
Beispiel #6
0
def _check_MatchNumericCondition(condition, transaction):
    field_value = get_nested_item(transaction, condition.fieldname)
    field_value = abs(field_value) if condition.absolute else field_value
    return condition.operator(field_value, condition.value)
Beispiel #7
0
def parse_credit_card_transaction(bank_config, account_config, card_config,
                                  transaction):
    amount = transaction['amount']['amount']
    transaction_code = get_nested_item(transaction, 'concept.id')
    if transaction_code == '0000':
        # code 0000 seems like an error, as it's really a regular purcharse,
        # so we fake the code
        transaction_code = '0005'

    transation_direction = TransactionDirection.CHARGE if amount < 0 else TransactionDirection.INCOME
    transaction_type = get_type(transaction_code, transation_direction)

    # Transactions have a _PT or _TT termination, that changes once over time and makes id unusable
    # this is an attempt to fix this and still being able to use the id as an unique id
    transaction_id = re.sub(r'_[PT]T$', '', transaction['id'])

    details = get_card_transaction_details(transaction, transaction_type)
    details['account'] = Account.from_config(account_config)
    details['bank'] = Bank.from_config(bank_config)

    # As we are processing a concrete card, and transaction doesn't have this
    # information, we set it to be able to process all transactions equally
    card_used = Card.from_config(card_config)

    keywords = extract_keywords(
        chain(extract_literals(transaction, KEYWORD_FIELDS),
              filter(lambda value: isinstance(value, str), details.values())))

    comment = get_comment(details, transaction_type)

    source = get_source(details, transaction_type)
    destination = get_destination(details, transaction_type)
    del details['bank']
    del details['account']

    is_debit_operation = transaction.get('operationTypeIndicator') == 'D'
    is_consolidated = transaction.get('status', {}).get('id') == '7'

    notify_not_added = False
    status_flags = datatypes.StatusFlags()

    if is_debit_operation:
        if notify_not_added:
            from common.notifications import get_notifier
            import bank
            banking_configuration = bank.load_config(
                bank.env()['main_config_file'])
            notifier = get_notifier(banking_configuration.notifications)
            notifier(
                'Debit transaction found, not adding {bank.name} card transaction: {date} {amount}, {source}->{destination}'
                .format(bank=bank_config,
                        amount=amount,
                        date=transaction['valueDate'],
                        source=str(source),
                        destination=str(destination)))
        status_flags.invalid = True

    if not is_consolidated and notify_not_added:
        if notify_not_added:
            from common.notifications import get_notifier
            import bank
            banking_configuration = bank.load_config(
                bank.env()['main_config_file'])
            notifier = get_notifier(banking_configuration.notifications)
            notifier(
                'Non consolidated transaction found, not adding {bank.name} card transaction: {date} {amount}, {source}->{destination}'
                .format(bank=bank_config,
                        amount=amount,
                        date=transaction['valueDate'],
                        source=str(source),
                        destination=str(destination)))
        return None

    return ParsedCreditCardTransaction(
        transaction_id=transaction_id,
        type=transaction_type,
        currency=transaction['amount']['currency']['code'],
        amount=amount,
        value_date=decode_date(transaction['valueDate']),
        transaction_date=decode_date(transaction['transactionDate']),
        source=source,
        destination=destination,
        card=card_used,
        details=details,
        keywords=keywords,
        comment=comment if comment is not None else '',
        status_flags=status_flags)
Beispiel #8
0
 def transaction_string(transaction):
     return ' '.join(
         map(lambda field: get_nested_item(transaction, field).__repr__(),
             transaction_key_fields))
Beispiel #9
0
def test_nested_item_with_dict(obj, path, value):
    assert get_nested_item(obj, path) == value