def test_update_pad_account(session):
    """Test update account."""
    # Create a pending account first, then call the job
    account = factory_create_pad_account(auth_account_id='2')
    CreateAccountTask.create_accounts()

    account = PaymentAccount.find_by_id(account.id)
    cfs_account = CfsAccount.find_effective_by_account_id(account.id)

    assert cfs_account.payment_instrument_number

    # Now update the account.
    new_payment_details = {
        'bankInstitutionNumber': '111',
        'bankTransitNumber': '222',
        'bankAccountNumber': '3333333333'
    }
    PadService().update_account(name='Test', cfs_account=cfs_account, payment_info=new_payment_details)
    cfs_account = CfsAccount.find_by_id(cfs_account.id)

    # Run the job again
    CreateAccountTask.create_accounts()

    updated_cfs_account: CfsAccount = CfsAccount.find_effective_by_account_id(account.id)
    assert updated_cfs_account.id != cfs_account.id
    assert updated_cfs_account.bank_account_number == new_payment_details.get('bankAccountNumber')
    assert updated_cfs_account.bank_branch_number == new_payment_details.get('bankTransitNumber')
    assert updated_cfs_account.bank_number == new_payment_details.get('bankInstitutionNumber')

    assert cfs_account.status == CfsAccountStatus.INACTIVE.value
    assert updated_cfs_account.status == CfsAccountStatus.PENDING_PAD_ACTIVATION.value
    assert updated_cfs_account.payment_instrument_number
Beispiel #2
0
def _validate_account(inv: InvoiceModel, row: Dict[str, str]):
    """Validate any mismatch in account number."""
    # This should never happen, just in case
    cfs_account: CfsAccountModel = CfsAccountModel.find_by_id(
        inv.cfs_account_id)
    if (account_number := _get_row_value(
            row, Column.CUSTOMER_ACC)) != cfs_account.cfs_account:
        logger.error('Customer Account received as %s, but expected %s.',
                     account_number, cfs_account.cfs_account)
        capture_message(
            f'Customer Account received as {account_number}, but expected {cfs_account.cfs_account}.',
            level='error')

        raise Exception('Invalid Account Number')
Beispiel #3
0
def test_link_rs(session):
    """Test link routing slip."""
    child_rs_number = '1234'
    parent_rs_number = '89799'
    factory_routing_slip_account(number=child_rs_number,
                                 status=CfsAccountStatus.ACTIVE.value)
    factory_routing_slip_account(number=parent_rs_number,
                                 status=CfsAccountStatus.ACTIVE.value)
    child_rs = RoutingSlipModel.find_by_number(child_rs_number)
    parent_rs = RoutingSlipModel.find_by_number(parent_rs_number)
    # Do Link
    child_rs.status = RoutingSlipStatus.LINKED.value
    child_rs.parent_number = parent_rs.number
    child_rs.save()
    payment_account: PaymentAccountModel = PaymentAccountModel.find_by_id(
        child_rs.payment_account_id)

    cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(
        payment_account.id)

    with patch('pay_api.services.CFSService.reverse_rs_receipt_in_cfs'
               ) as mock_cfs_reverse:
        with patch('pay_api.services.CFSService.create_cfs_receipt'
                   ) as mock_create_cfs:
            RoutingSlipTask.link_routing_slips()
            mock_cfs_reverse.assert_called()
            mock_cfs_reverse.assert_called_with(cfs_account, child_rs.number)
            mock_create_cfs.assert_called()

    # child_rs = RoutingSlipModel.find_by_number(child_rs_number)
    # parent_rs = RoutingSlipModel.find_by_number(parent_rs_number)
    # PS This has changed, no longer updating child rs payment account with parent.
    # assert child_rs.payment_account_id == parent_rs.payment_account_id
    cfs_account: CfsAccountModel = CfsAccountModel.find_by_id(cfs_account.id)
    assert cfs_account.status == CfsAccountStatus.INACTIVE.value

    # make sure next invocation doesnt fetch any records
    with patch('pay_api.services.CFSService.reverse_rs_receipt_in_cfs'
               ) as mock_cfs_reverse:
        with patch('pay_api.services.CFSService.create_cfs_receipt'
                   ) as mock_create_cfs:
            RoutingSlipTask.link_routing_slips()
            mock_cfs_reverse.assert_not_called()
            mock_create_cfs.assert_not_called()
Beispiel #4
0
def test_create_pad_account_system_error(session):
    """Test create account."""
    # Create a pending account first, then call the job
    account = factory_create_pad_account(auth_account_id='1')
    cfs_account: CfsAccount = CfsAccount.find_effective_by_account_id(
        account.id)
    assert cfs_account.status == CfsAccountStatus.PENDING.value
    mock_response = requests.models.Response()
    mock_response.headers['CAS-Returned-Messages'] = '[CFS Down]'
    mock_response.status_code = 404

    side_effect = HTTPError(response=mock_response)
    with patch.object(mailer, 'publish_mailer_events') as mock_mailer:
        with patch('pay_api.services.CFSService.create_cfs_account',
                   side_effect=side_effect):
            CreateAccountTask.create_accounts()
            mock_mailer.assert_not_called()

    account = PaymentAccount.find_by_id(account.id)
    cfs_account: CfsAccount = CfsAccount.find_by_id(cfs_account.id)
    assert cfs_account.status == CfsAccountStatus.PENDING.value
Beispiel #5
0
    def _create_pad_invoices(cls):  # pylint: disable=too-many-locals
        """Create PAD invoices in to CFS system."""
        # Find all accounts which have done a transaction with PAD transactions

        inv_subquery = db.session.query(InvoiceModel.payment_account_id) \
            .filter(InvoiceModel.payment_method_code == PaymentMethod.PAD.value) \
            .filter(InvoiceModel.invoice_status_code == PaymentStatus.CREATED.value).subquery()

        # Exclude the accounts which are in FREEZE state.
        pad_accounts: List[PaymentAccountModel] = db.session.query(PaymentAccountModel) \
            .join(CfsAccountModel, CfsAccountModel.account_id == PaymentAccountModel.id) \
            .filter(CfsAccountModel.status != CfsAccountStatus.FREEZE.value) \
            .filter(PaymentAccountModel.id.in_(inv_subquery)).all()

        current_app.logger.info(
            f'Found {len(pad_accounts)} with PAD transactions.')

        for account in pad_accounts:
            # Find all PAD invoices for this account
            account_invoices = db.session.query(InvoiceModel) \
                .filter(InvoiceModel.payment_account_id == account.id) \
                .filter(InvoiceModel.payment_method_code == PaymentMethod.PAD.value) \
                .filter(InvoiceModel.invoice_status_code == InvoiceStatus.CREATED.value) \
                .order_by(InvoiceModel.created_on.desc()).all()

            # Get cfs account
            payment_account: PaymentAccountService = PaymentAccountService.find_by_id(
                account.id)

            current_app.logger.debug(
                f'Found {len(account_invoices)} invoices for account {payment_account.auth_account_id}'
            )
            if len(account_invoices) == 0:
                continue

            cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(
                payment_account.id)
            if cfs_account is None:
                # Get the last invoice and look up cfs_account for it, as the account might have got upgraded.
                cfs_account = CfsAccountModel.find_by_id(
                    account_invoices[0].cfs_account_id)

            # If the CFS Account status is not ACTIVE, raise error and continue
            if cfs_account.status not in (CfsAccountStatus.ACTIVE.value,
                                          CfsAccountStatus.INACTIVE.value):
                capture_message(
                    f'CFS Account status is not ACTIVE. for account {payment_account.auth_account_id} '
                    f'is {payment_account.cfs_account_status}',
                    level='error')
                current_app.logger.error(
                    f'CFS status for account {payment_account.auth_account_id} '
                    f'is {payment_account.cfs_account_status}')
                continue

            # Add all lines together
            lines = []
            invoice_total: float = 0
            for invoice in account_invoices:
                lines.append(*invoice.payment_line_items)
                invoice_total += invoice.total

            try:
                # Get the first invoice id as the trx number for CFS
                invoice_response = CFSService.create_account_invoice(
                    transaction_number=account_invoices[0].id,
                    line_items=lines,
                    payment_account=cfs_account)
            except Exception as e:  # pylint: disable=broad-except
                capture_message(
                    f'Error on creating PAD invoice: account id={payment_account.id}, '
                    f'auth account : {payment_account.auth_account_id}, ERROR : {str(e)}',
                    level='error')
                current_app.logger.error(e)
                continue
            # emit account mailer event
            mailer.publish_mailer_events('pad.invoiceCreated', payment_account,
                                         {'invoice_total': invoice_total})
            # Iterate invoice and create invoice reference records
            for invoice in account_invoices:
                # Create invoice reference, payment record and a payment transaction
                InvoiceReference.create(
                    invoice_id=invoice.id,
                    invoice_number=invoice_response.json().get(
                        'invoice_number'),
                    reference_number=invoice_response.json().get(
                        'pbc_ref_number', None))

                # Misc
                invoice.cfs_account_id = payment_account.cfs_account_id
                invoice.invoice_status_code = InvoiceStatus.SETTLEMENT_SCHEDULED.value
                invoice.save()
Beispiel #6
0
    def create_invoice_pdf(identifier: int, **kwargs) -> Tuple:
        """Find invoice by id."""
        invoice_dao: InvoiceModel = InvoiceModel.find_by_id(identifier)

        if not invoice_dao:
            raise BusinessException(Error.INVALID_INVOICE_ID)

        payment_account: PaymentAccountModel = PaymentAccountModel.find_by_id(
            invoice_dao.payment_account_id)
        cfs_account: CfsAccountModel = CfsAccountModel.find_by_id(
            invoice_dao.cfs_account_id)
        org_response = OAuthService.get(
            current_app.config.get('AUTH_API_ENDPOINT') +
            f'orgs/{payment_account.auth_account_id}',
            kwargs['user'].bearer_token, AuthHeaderType.BEARER,
            ContentType.JSON).json()
        org_contact_response = OAuthService.get(
            current_app.config.get('AUTH_API_ENDPOINT') +
            f'orgs/{payment_account.auth_account_id}/contacts',
            kwargs['user'].bearer_token, AuthHeaderType.BEARER,
            ContentType.JSON).json()

        org_contact = org_contact_response.get(
            'contacts')[0] if org_contact_response.get('contacts',
                                                       None) else {}

        invoice_number: str = invoice_dao.references[0].invoice_number if invoice_dao.references \
            else generate_transaction_number(invoice_dao.id)

        filing_types: List[Dict[str, str]] = []
        for line_item in invoice_dao.payment_line_items:
            business_identifier = invoice_dao.business_identifier \
                if not invoice_dao.business_identifier.startswith('T') \
                else ''
            filing_types.append({
                'folioNumber':
                invoice_dao.folio_number,
                'description':
                line_item.description,
                'businessIdentifier':
                business_identifier,
                'createdOn':
                get_local_formatted_date(invoice_dao.created_on),
                'filingTypeCode':
                line_item.fee_schedule.filing_type_code,
                'fee':
                line_item.total,
                'gst':
                line_item.gst,
                'serviceFees':
                line_item.service_fees,
                'total':
                line_item.total + line_item.service_fees
            })

        template_vars: Dict[str, any] = {
            'invoiceNumber': invoice_number,
            'createdOn': get_local_formatted_date(invoice_dao.created_on),
            'accountNumber': cfs_account.cfs_account if cfs_account else None,
            'total': invoice_dao.total,
            'gst': 0,
            'serviceFees': invoice_dao.service_fees,
            'fees': invoice_dao.total - invoice_dao.service_fees,
            'filingTypes': filing_types,
            'accountContact': {
                'name': org_response.get('name'),
                'city': org_contact.get('city', None),
                'country': org_contact.get('country', None),
                'postalCode': org_contact.get('postalCode', None),
                'region': org_contact.get('region', None),
                'street': org_contact.get('street', None),
                'streetAdditional': org_contact.get('streetAdditional', None)
            }
        }

        invoice_pdf_dict = {
            'templateName': 'invoice',
            'reportName': invoice_number,
            'templateVars': template_vars
        }
        current_app.logger.info('Invoice PDF Dict %s', invoice_pdf_dict)

        pdf_response = OAuthService.post(
            current_app.config.get('REPORT_API_BASE_URL'),
            kwargs['user'].bearer_token, AuthHeaderType.BEARER,
            ContentType.JSON, invoice_pdf_dict)
        current_app.logger.debug('<OAuthService responded to receipt.py')

        return pdf_response, invoice_pdf_dict.get('reportName')