def test_create_pad_invoice_multiple_transactions(session):
    """Assert PAD invoices are created."""
    # Create an account and an invoice for the account
    account = factory_create_pad_account(auth_account_id='1',
                                         status=CfsAccountStatus.ACTIVE.value)
    previous_day = datetime.now() - timedelta(days=1)
    # Create an invoice for this account
    invoice = factory_invoice(payment_account=account,
                              created_on=previous_day,
                              total=10,
                              payment_method_code=None)
    fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type(
        'CP', 'OTANN')
    line = factory_payment_line_item(
        invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id)
    line.save()

    # Create another invoice for this account
    invoice2 = factory_invoice(payment_account=account,
                               created_on=previous_day,
                               total=10,
                               payment_method_code=None)
    fee_schedule2 = FeeScheduleModel.find_by_filing_type_and_corp_type(
        'CP', 'OTADD')
    line2 = factory_payment_line_item(
        invoice2.id, fee_schedule_id=fee_schedule2.fee_schedule_id)
    line2.save()

    CreateInvoiceTask.create_invoices()
    invoice2 = InvoiceModel.find_by_id(invoice2.id)
    invoice = InvoiceModel.find_by_id(invoice.id)
    assert invoice2.invoice_status_code == invoice.invoice_status_code == InvoiceStatus.SETTLEMENT_SCHEDULED.value
예제 #2
0
def test_update_distribution(session, public_user_mock, stan_server,
                             monkeypatch):
    """Assert that the invoice status is updated when the distribution is updated."""
    # 1. Create a distribution code
    # 2. Attach a fee schedule to the distribution
    # 3. Create and complete payment
    # 4. Update the distribution and assert the invoice status is changed.
    distribution_code_svc = services.DistributionCode()
    fee_schedule = FeeSchedule.find_by_filing_type_and_corp_type('CP', 'OTANN')

    # Create a direct pay
    current_app.config['DIRECT_PAY_ENABLED'] = True
    payment_account = factory_payment_account(
        payment_method_code=PaymentMethod.DIRECT_PAY.value)
    payment_account.save()

    invoice = factory_invoice(payment_account, total=30)
    invoice.save()
    invoice_reference = factory_invoice_reference(invoice.id).save()
    line = factory_payment_line_item(
        invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id)
    line.save()

    factory_payment(invoice_number=invoice_reference.invoice_number,
                    payment_method_code=PaymentMethod.DIRECT_PAY.value,
                    invoice_amount=30).save()

    distribution_id = line.fee_distribution_id

    distribution_code = distribution_code_svc.find_by_id(distribution_id)

    transaction = PaymentTransactionService.create_transaction_for_invoice(
        invoice.id, get_paybc_transaction_request())

    def get_receipt(cls, payment_account, pay_response_url: str,
                    invoice_reference):  # pylint: disable=unused-argument; mocks of library methods
        return '1234567890', datetime.now(), 30.00

    monkeypatch.setattr(
        'pay_api.services.direct_pay_service.DirectPayService.get_receipt',
        get_receipt)

    # Update transaction without response url, which should update the receipt
    PaymentTransactionService.update_transaction(transaction.id,
                                                 pay_response_url=None)

    invoice = InvoiceModel.find_by_id(invoice.id)
    assert invoice.invoice_status_code == InvoiceStatus.PAID.value

    # Update distribution code
    distribution_code_svc.save_or_update(distribution_code, distribution_id)
    invoice = InvoiceModel.find_by_id(invoice.id)
    assert invoice.invoice_status_code == InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value
예제 #3
0
def test_internal_rs_back_active(session, public_user_mock):
    """12033 - Scenario 2.

    Routing slip is complete and a transaction is cancelled
    the balance is restored - Should move back to Active
    """
    payment_response = PaymentService.create_invoice(
        get_routing_slip_payment_request(), get_auth_staff())
    account_model = PaymentAccount.find_by_auth_account_id(get_auth_staff().get('account').get('id'))
    account_id = account_model.id
    assert account_id is not None
    assert payment_response.get('id') is not None

    rs_number = '123456789'
    rs = factory_routing_slip(number=rs_number, payment_account_id=account_id, remaining_amount=50.00)
    rs.save()

    # Create another invoice with a routing slip.
    invoice = PaymentService.create_invoice(get_routing_slip_payment_request(), get_auth_staff())
    account_model = PaymentAccount.find_by_auth_account_id(get_auth_staff().get('account').get('id'))

    assert account_id == account_model.id

    rs = RoutingSlipModel.find_by_number(rs_number)
    assert rs.remaining_amount == 0.0
    assert rs.status == RoutingSlipStatus.COMPLETE.name

    invoice = Invoice.find_by_id(invoice['id'])
    InternalPayService().process_cfs_refund(invoice)

    assert rs.status == RoutingSlipStatus.ACTIVE.name
예제 #4
0
    def create_refund(cls, invoice_id: int, request: Dict[str, str],
                      **kwargs) -> None:
        """Create refund."""
        current_app.logger.debug('<create refund')
        # Do validation by looking up the invoice
        invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id)
        # Allow refund only for direct pay payments, and only if the status of invoice is PAID/UPDATE_REVENUE_ACCOUNT
        paid_statuses = (InvoiceStatus.PAID.value,
                         InvoiceStatus.APPROVED.value,
                         InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value)

        if invoice.invoice_status_code not in paid_statuses:
            raise BusinessException(Error.INVALID_REQUEST)

        refund: RefundService = RefundService()
        refund.invoice_id = invoice_id
        refund.reason = get_str_by_path(request, 'reason')
        refund.requested_by = kwargs['user'].user_name
        refund.requested_date = datetime.now()
        refund.flush()

        cls._process_cfs_refund(invoice)

        # set invoice status
        invoice.invoice_status_code = InvoiceStatus.REFUND_REQUESTED.value
        invoice.refund = invoice.total  # no partial refund
        invoice.save()
예제 #5
0
def test_create_duplicate_refund_for_paid_invoice(session, monkeypatch):
    """Assert that the create duplicate refund fails for paid invoices."""
    payment_account = factory_payment_account()
    payment_account.save()

    i = factory_invoice(payment_account=payment_account)
    i.save()
    inv_ref = factory_invoice_reference(i.id)
    inv_ref.status_code = InvoiceReferenceStatus.COMPLETED.value
    inv_ref.save()

    payment = factory_payment(invoice_number=inv_ref.invoice_number).save()

    factory_payment_transaction(payment_id=payment.id, status_code=TransactionStatus.COMPLETED.value).save()

    i.invoice_status_code = InvoiceStatus.PAID.value
    i.save()

    factory_receipt(invoice_id=i.id).save()
    monkeypatch.setattr('pay_api.services.payment_transaction.publish_response', lambda *args, **kwargs: None)

    RefundService.create_refund(invoice_id=i.id, request={'reason': 'Test'})
    i = InvoiceModel.find_by_id(i.id)
    payment: PaymentModel = PaymentModel.find_by_id(payment.id)

    assert i.invoice_status_code == InvoiceStatus.REFUND_REQUESTED.value
    assert payment.payment_status_code == PaymentStatus.REFUNDED.value

    with pytest.raises(Exception) as excinfo:
        RefundService.create_refund(invoice_id=i.id, request={'reason': 'Test'})
    assert excinfo.type == BusinessException
예제 #6
0
def test_create_refund_for_paid_invoice(session, monkeypatch, payment_method, invoice_status, pay_status,
                                        has_reference, expected_inv_status):
    """Assert that the create refund succeeds for paid invoices."""
    expected = REFUND_SUCCESS_MESSAGES[f'{payment_method}.{invoice_status}']
    payment_account = factory_payment_account()
    payment_account.save()

    i = factory_invoice(payment_account=payment_account, payment_method_code=payment_method)
    i.save()
    if has_reference:
        inv_ref = factory_invoice_reference(i.id)
        inv_ref.status_code = InvoiceReferenceStatus.COMPLETED.value
        inv_ref.save()

        payment = factory_payment(invoice_number=inv_ref.invoice_number, payment_status_code=pay_status).save()

        factory_payment_transaction(payment_id=payment.id, status_code=TransactionStatus.COMPLETED.value).save()

    i.invoice_status_code = invoice_status
    i.save()

    factory_receipt(invoice_id=i.id).save()

    monkeypatch.setattr('pay_api.services.payment_transaction.publish_response', lambda *args, **kwargs: None)

    message = RefundService.create_refund(invoice_id=i.id, request={'reason': 'Test'})
    i = InvoiceModel.find_by_id(i.id)

    assert i.invoice_status_code == expected_inv_status
    assert message['message'] == expected
예제 #7
0
def _process_partial_paid_invoices(inv_ref: InvoiceReferenceModel, row):
    """Process partial payments.

    Update Payment as COMPLETED.
    Update Transaction is COMPLETED.
    Update Invoice as PARTIAL.
    """
    receipt_date: datetime = datetime.strptime(
        _get_row_value(row, Column.APP_DATE), '%d-%b-%y')
    receipt_number: str = _get_row_value(row, Column.APP_ID)

    inv: InvoiceModel = InvoiceModel.find_by_id(inv_ref.invoice_id)
    _validate_account(inv, row)
    logger.debug('Partial Invoice. Invoice Reference ID : %s, invoice ID : %s',
                 inv_ref.id, inv_ref.invoice_id)
    inv.invoice_status_code = InvoiceStatus.PARTIAL.value
    inv.paid = inv.total - float(
        _get_row_value(row, Column.TARGET_TXN_OUTSTANDING))
    # Create Receipt records
    receipt: ReceiptModel = ReceiptModel()
    receipt.receipt_date = receipt_date
    receipt.receipt_amount = float(_get_row_value(row, Column.APP_AMOUNT))
    receipt.invoice_id = inv.id
    receipt.receipt_number = receipt_number
    db.session.add(receipt)
예제 #8
0
async def _process_paid_invoices(inv_references, row):
    """Process PAID invoices.

    Update invoices as PAID
    Update payment as COMPLETED
    Update invoice_reference as COMPLETED
    Update payment_transaction as COMPLETED.
    """
    receipt_date: datetime = datetime.strptime(
        _get_row_value(row, Column.APP_DATE), '%d-%b-%y')
    receipt_number: str = _get_row_value(row, Column.SOURCE_TXN_NO)
    for inv_ref in inv_references:
        inv_ref.status_code = InvoiceReferenceStatus.COMPLETED.value
        # Find invoice, update status
        inv: InvoiceModel = InvoiceModel.find_by_id(inv_ref.invoice_id)
        _validate_account(inv, row)
        logger.debug(
            'PAID Invoice. Invoice Reference ID : %s, invoice ID : %s',
            inv_ref.id, inv_ref.invoice_id)

        inv.invoice_status_code = InvoiceStatus.PAID.value
        inv.paid = inv.total
        # Create Receipt records
        receipt: ReceiptModel = ReceiptModel()
        receipt.receipt_date = receipt_date
        receipt.receipt_amount = inv.total
        receipt.invoice_id = inv.id
        receipt.receipt_number = receipt_number
        db.session.add(receipt)
        # Publish to the queue if it's an Online Banking payment
        if inv.payment_method_code == PaymentMethod.ONLINE_BANKING.value:
            logger.debug('Publishing payment event for OB. Invoice : %s',
                         inv.id)
            await _publish_payment_event(inv)
def test_create_pad_invoice_for_frozen_accounts(session):
    """Assert PAD invoices are created."""
    # Create an account and an invoice for the account
    account = factory_create_pad_account(auth_account_id='1',
                                         status=CfsAccountStatus.FREEZE.value)
    previous_day = datetime.now() - timedelta(days=1)
    # Create an invoice for this account
    invoice = factory_invoice(payment_account=account,
                              created_on=previous_day,
                              total=10,
                              payment_method_code=None)

    fee_schedule = FeeScheduleModel.find_by_filing_type_and_corp_type(
        'CP', 'OTANN')
    line = factory_payment_line_item(
        invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id)
    line.save()

    assert invoice.invoice_status_code == InvoiceStatus.CREATED.value

    CreateInvoiceTask.create_invoices()

    updated_invoice: InvoiceModel = InvoiceModel.find_by_id(invoice.id)
    inv_ref: InvoiceReferenceModel = InvoiceReferenceModel. \
        find_reference_by_invoice_id_and_status(invoice.id, InvoiceReferenceStatus.ACTIVE.value)

    assert inv_ref is None
    assert updated_invoice.invoice_status_code == InvoiceStatus.CREATED.value
예제 #10
0
def test_create_refund_with_existing_routing_slip(session, client, jwt, app,
                                                  stan_server):
    """Assert that the endpoint  returns 202."""
    claims = get_claims(roles=[
        Role.FAS_CREATE.value, Role.FAS_SEARCH.value, Role.FAS_REFUND.value,
        Role.STAFF.value, 'make_payment'
    ])
    token = jwt.create_jwt(claims, token_header)
    headers = {
        'Authorization': f'Bearer {token}',
        'content-type': 'application/json'
    }
    payload = get_routing_slip_request()
    routingslip_amount = payload.get('payments')[0].get('paidAmount')
    rv = client.post('/api/v1/fas/routing-slips',
                     data=json.dumps(payload),
                     headers=headers)
    rs_number = rv.json.get('number')

    headers = {
        'Authorization': f'Bearer {token}',
        'content-type': 'application/json'
    }
    data = get_payment_request()
    data['accountInfo'] = {'routingSlip': rs_number}

    rv = client.post('/api/v1/payment-requests',
                     data=json.dumps(data),
                     headers=headers)
    inv_id = rv.json.get('id')
    total = rv.json.get('total')
    rv = client.post('/api/v1/fas/routing-slips/queries',
                     data=json.dumps({'routingSlipNumber': rs_number}),
                     headers=headers)
    items = rv.json.get('items')

    inv: InvoiceModel = InvoiceModel.find_by_id(inv_id)
    inv.invoice_status_code = InvoiceStatus.PAID.value
    inv.save()

    assert items[0].get('remainingAmount') == payload.get('payments')[0].get(
        'paidAmount') - total

    rv = client.post(f'/api/v1/payment-requests/{inv_id}/refunds',
                     data=json.dumps({'reason': 'Test'}),
                     headers=headers)
    assert rv.status_code == 202
    assert rv.json.get(
        'message') == REFUND_SUCCESS_MESSAGES['INTERNAL.REFUND_REQUESTED']

    rv = client.post('/api/v1/fas/routing-slips/queries',
                     data=json.dumps({'routingSlipNumber': rs_number}),
                     headers=headers)
    # asssert refund amount goes to routing slip back
    assert rv.json.get('items')[0].get('remainingAmount') == routingslip_amount
예제 #11
0
def _process_failed_payments(row):
    """Handle failed payments."""
    # 1. Set the cfs_account status as FREEZE.
    # 2. Call cfs api to Stop further PAD on this account.
    # 3. Reverse the invoice_reference status to ACTIVE, invoice status to SETTLEMENT_SCHED, and delete receipt.
    # 4. Create an NSF invoice for this account.
    # 5. Create invoice reference for the newly created NSF invoice.
    # 6. Adjust invoice in CFS to include NSF fees.
    inv_number = _get_row_value(row, Column.TARGET_TXN_NO)
    # If there is a FAILED payment record for this; it means it's a duplicate event. Ignore it.
    payment: PaymentModel = PaymentModel.find_payment_by_invoice_number_and_status(
        inv_number, PaymentStatus.FAILED.value)
    if payment:
        logger.info('Ignoring duplicate NSF message for invoice : %s ',
                    inv_number)
        return

    # Set CFS Account Status.
    payment_account: PaymentAccountModel = _get_payment_account(row)
    cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(
        payment_account.id)
    logger.info('setting payment account id : %s status as FREEZE',
                payment_account.id)
    cfs_account.status = CfsAccountStatus.FREEZE.value
    # Call CFS to stop any further PAD transactions on this account.
    CFSService.suspend_cfs_account(cfs_account)
    # Find the invoice_reference for this invoice and mark it as ACTIVE.
    inv_references: List[InvoiceReferenceModel] = db.session.query(InvoiceReferenceModel). \
        filter(InvoiceReferenceModel.status_code == InvoiceReferenceStatus.COMPLETED.value). \
        filter(InvoiceReferenceModel.invoice_number == inv_number). \
        all()

    # Update status to ACTIVE, if it was marked COMPLETED
    for inv_reference in inv_references:
        inv_reference.status_code = InvoiceReferenceStatus.ACTIVE.value
        # Find receipt and delete it.
        receipt: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(
            invoice_id=inv_reference.invoice_id)
        if receipt:
            db.session.delete(receipt)
        # Find invoice and update the status to SETTLEMENT_SCHED
        invoice: InvoiceModel = InvoiceModel.find_by_id(
            identifier=inv_reference.invoice_id)
        invoice.invoice_status_code = InvoiceStatus.SETTLEMENT_SCHEDULED.value
        invoice.paid = 0

    # Create an invoice for NSF for this account
    invoice = _create_nsf_invoice(cfs_account, inv_number, payment_account)
    # Adjust CFS invoice
    CFSService.add_nsf_adjustment(cfs_account=cfs_account,
                                  inv_number=inv_number,
                                  amount=invoice.total)
예제 #12
0
def test_patch_transaction_for_nsf_payment(session, monkeypatch):
    """Assert that the payment is saved to the table."""
    # Create a FAILED payment (NSF), then clone the payment to create another one for CC payment
    # Create a transaction and assert it's success.
    # Patch transaction and check the status of records
    inv_number_1 = 'REG00001'
    payment_account = factory_payment_account(
        cfs_account_status=CfsAccountStatus.FREEZE.value,
        payment_method_code='PAD').save()
    invoice_1 = factory_invoice(payment_account, total=100)
    invoice_1.save()
    factory_payment_line_item(invoice_id=invoice_1.id,
                              fee_schedule_id=1).save()
    factory_invoice_reference(invoice_1.id, invoice_number=inv_number_1).save()
    payment_1 = factory_payment(payment_status_code='FAILED',
                                payment_account_id=payment_account.id,
                                invoice_number=inv_number_1,
                                invoice_amount=100,
                                payment_method_code=PaymentMethod.PAD.value)
    payment_1.save()

    # Create payment for NSF payment.
    payment_2 = factory_payment(payment_status_code='CREATED',
                                payment_account_id=payment_account.id,
                                invoice_number=inv_number_1,
                                invoice_amount=100,
                                payment_method_code=PaymentMethod.CC.value)
    payment_2.save()

    def get_receipt(cls, payment_account, pay_response_url: str,
                    invoice_reference):  # pylint: disable=unused-argument; mocks of library methods
        return '1234567890', datetime.now(), 100.00

    monkeypatch.setattr(
        'pay_api.services.paybc_service.PaybcService.get_receipt', get_receipt)

    txn = PaymentTransactionService.create_transaction_for_payment(
        payment_2.id, get_paybc_transaction_request())
    txn = PaymentTransactionService.update_transaction(
        txn.id, pay_response_url='receipt_number=123451')

    assert txn.status_code == 'COMPLETED'
    payment_2 = Payment.find_by_id(payment_2.id)
    assert payment_2.payment_status_code == 'COMPLETED'

    invoice_1: Invoice = Invoice.find_by_id(invoice_1.id)
    assert invoice_1.invoice_status_code == 'PAID'
    cfs_account = CfsAccount.find_effective_by_account_id(payment_account.id)
    assert cfs_account.status == 'ACTIVE'
예제 #13
0
    def find_by_id(identifier: int, pay_id: int = None, skip_auth_check: bool = False):
        """Find invoice by id."""
        invoice_dao = InvoiceModel.find_by_id(identifier) if not pay_id else InvoiceModel.find_by_id_and_payment_id(
            identifier, pay_id)
        if not invoice_dao:
            raise BusinessException(Error.INVALID_INVOICE_ID)

        if not skip_auth_check:
            Invoice._check_for_auth(invoice_dao)

        invoice = Invoice()
        invoice._dao = invoice_dao  # pylint: disable=protected-access

        current_app.logger.debug('>find_by_id')
        return invoice
예제 #14
0
async def test_eft_wire_reconciliations(session, app, stan_server, event_loop,
                                        client_id, events_stan, future,
                                        mock_publish):
    """Test Reconciliations worker."""
    # Call back for the subscription
    from reconciliations.worker import cb_subscription_handler

    # Create a Credit Card Payment
    # register the handler to test it
    await subscribe_to_queue(
        events_stan,
        current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'),
        current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'),
        current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'),
        cb_subscription_handler)

    # 1. Create payment account
    # 2. Create invoice and related records
    # 3. Create CFS Invoice records
    # 4. Create a CFS settlement file, and verify the records
    cfs_account_number = '1234'
    pay_account: PaymentAccountModel = factory_create_online_banking_account(
        status=CfsAccountStatus.ACTIVE.value, cfs_account=cfs_account_number)

    invoice: InvoiceModel = factory_invoice(
        payment_account=pay_account,
        total=100,
        service_fees=10.0,
        payment_method_code=PaymentMethod.ONLINE_BANKING.value)
    factory_payment_line_item(invoice_id=invoice.id,
                              filing_fees=90.0,
                              service_fees=10.0,
                              total=90.0)
    invoice_number = '1234567890'
    factory_invoice_reference(invoice_id=invoice.id,
                              invoice_number=invoice_number)
    invoice.invoice_status_code = InvoiceStatus.SETTLEMENT_SCHEDULED.value
    invoice = invoice.save()
    invoice_id = invoice.id
    total = invoice.total

    # Create a payment for EFT Wire
    eft_wire_receipt = 'RCPT0012345'
    paid_amount = 100
    PaymentModel(payment_method_code=PaymentMethod.EFT.value,
                 payment_status_code=PaymentStatus.CREATED.value,
                 payment_system_code='PAYBC',
                 payment_account_id=pay_account.id,
                 completed_on=datetime.now(),
                 paid_amount=paid_amount,
                 receipt_number=eft_wire_receipt).save()

    # Create a settlement file and publish.
    file_name: str = 'cas_settlement_file.csv'

    # Settlement row
    date = datetime.now().strftime('%d-%b-%y')

    row = [
        RecordType.EFTP.value, SourceTransaction.EFT_WIRE.value,
        eft_wire_receipt, 100001, date, total, cfs_account_number,
        TargetTransaction.INV.value, invoice_number, total, 0,
        Status.PAID.value
    ]
    create_and_upload_settlement_file(file_name, [row])
    await helper_add_event_to_queue(events_stan, file_name=file_name)

    # The invoice should be in PAID status and Payment should be completed
    updated_invoice = InvoiceModel.find_by_id(invoice_id)
    assert updated_invoice.invoice_status_code == InvoiceStatus.PAID.value

    payment: PaymentModel = PaymentModel.find_payment_by_receipt_number(
        eft_wire_receipt)
    assert payment.payment_status_code == PaymentStatus.COMPLETED.value
    assert payment.paid_amount == paid_amount
    assert payment.receipt_number == eft_wire_receipt
예제 #15
0
async def test_pad_reversal_reconciliations(session, app, stan_server,
                                            event_loop, client_id, events_stan,
                                            future, mock_publish):
    """Test Reconciliations worker for NSF."""
    # Call back for the subscription
    from reconciliations.worker import cb_subscription_handler

    # register the handler to test it
    await subscribe_to_queue(
        events_stan,
        current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'),
        current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'),
        current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'),
        cb_subscription_handler)

    # 1. Create payment account
    # 2. Create invoices and related records for a completed payment
    # 3. Create CFS Invoice records
    # 4. Create a CFS settlement file, and verify the records
    cfs_account_number = '1234'
    pay_account: PaymentAccountModel = factory_create_pad_account(
        status=CfsAccountStatus.ACTIVE.value,
        account_number=cfs_account_number)
    invoice1: InvoiceModel = factory_invoice(
        payment_account=pay_account,
        total=100,
        service_fees=10.0,
        payment_method_code=PaymentMethod.PAD.value,
        status_code=InvoiceStatus.PAID.value)
    factory_payment_line_item(invoice_id=invoice1.id,
                              filing_fees=90.0,
                              service_fees=10.0,
                              total=90.0)

    invoice2: InvoiceModel = factory_invoice(
        payment_account=pay_account,
        total=200,
        service_fees=10.0,
        payment_method_code=PaymentMethod.PAD.value,
        status_code=InvoiceStatus.PAID.value)
    factory_payment_line_item(invoice_id=invoice2.id,
                              filing_fees=190.0,
                              service_fees=10.0,
                              total=190.0)

    invoice_number = '1234567890'
    receipt_number = '9999999999'

    factory_invoice_reference(
        invoice_id=invoice1.id,
        invoice_number=invoice_number,
        status_code=InvoiceReferenceStatus.COMPLETED.value)
    factory_invoice_reference(
        invoice_id=invoice2.id,
        invoice_number=invoice_number,
        status_code=InvoiceReferenceStatus.COMPLETED.value)

    receipt_id1 = factory_receipt(invoice_id=invoice1.id,
                                  receipt_number=receipt_number).save().id
    receipt_id2 = factory_receipt(invoice_id=invoice2.id,
                                  receipt_number=receipt_number).save().id

    invoice1_id = invoice1.id
    invoice2_id = invoice2.id
    pay_account_id = pay_account.id

    total = invoice1.total + invoice2.total

    payment = factory_payment(pay_account=pay_account,
                              paid_amount=total,
                              invoice_amount=total,
                              invoice_number=invoice_number,
                              receipt_number=receipt_number,
                              status=PaymentStatus.COMPLETED.value)
    pay_id = payment.id

    # Now publish message saying payment has been reversed.
    # Create a settlement file and publish.
    file_name: str = 'cas_settlement_file.csv'
    # Settlement row
    date = datetime.now().strftime('%d-%b-%y')
    row = [
        RecordType.PADR.value, SourceTransaction.PAD.value, receipt_number,
        100001, date, 0, cfs_account_number, 'INV', invoice_number, total,
        total, Status.NOT_PAID.value
    ]
    create_and_upload_settlement_file(file_name, [row])
    await helper_add_event_to_queue(events_stan, file_name=file_name)

    # The invoice should be in SETTLEMENT_SCHEDULED status and Payment should be FAILED
    updated_invoice1 = InvoiceModel.find_by_id(invoice1_id)
    assert updated_invoice1.invoice_status_code == InvoiceStatus.SETTLEMENT_SCHEDULED.value
    updated_invoice2 = InvoiceModel.find_by_id(invoice2_id)
    assert updated_invoice2.invoice_status_code == InvoiceStatus.SETTLEMENT_SCHEDULED.value

    payment: PaymentModel = PaymentModel.find_by_id(pay_id)
    assert payment.payment_status_code == PaymentStatus.FAILED.value
    assert payment.paid_amount == 0
    assert payment.receipt_number == receipt_number
    assert payment.payment_method_code == PaymentMethod.PAD.value
    assert payment.invoice_number == invoice_number

    cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(
        pay_account_id)
    assert cfs_account.status == CfsAccountStatus.FREEZE.value

    # Receipt should be deleted
    assert ReceiptModel.find_by_id(receipt_id1) is None
    assert ReceiptModel.find_by_id(receipt_id2) is None
예제 #16
0
async def test_pad_reconciliations_with_credit_memo(session, app, stan_server,
                                                    event_loop, client_id,
                                                    events_stan, future,
                                                    mock_publish):
    """Test Reconciliations worker."""
    # Call back for the subscription
    from reconciliations.worker import cb_subscription_handler

    # Create a Credit Card Payment
    # register the handler to test it
    await subscribe_to_queue(
        events_stan,
        current_app.config.get('SUBSCRIPTION_OPTIONS').get('subject'),
        current_app.config.get('SUBSCRIPTION_OPTIONS').get('queue'),
        current_app.config.get('SUBSCRIPTION_OPTIONS').get('durable_name'),
        cb_subscription_handler)

    # 1. Create payment account
    # 2. Create invoices and related records
    # 3. Create CFS Invoice records
    # 4. Mimic some credits on the account
    # 4. Create a CFS settlement file, and verify the records
    cfs_account_number = '1234'
    pay_account: PaymentAccountModel = factory_create_pad_account(
        status=CfsAccountStatus.ACTIVE.value,
        account_number=cfs_account_number)
    invoice1: InvoiceModel = factory_invoice(
        payment_account=pay_account,
        total=100,
        service_fees=10.0,
        payment_method_code=PaymentMethod.PAD.value)
    factory_payment_line_item(invoice_id=invoice1.id,
                              filing_fees=90.0,
                              service_fees=10.0,
                              total=90.0)

    invoice2: InvoiceModel = factory_invoice(
        payment_account=pay_account,
        total=200,
        service_fees=10.0,
        payment_method_code=PaymentMethod.PAD.value)
    factory_payment_line_item(invoice_id=invoice2.id,
                              filing_fees=190.0,
                              service_fees=10.0,
                              total=190.0)

    invoice_number = '1234567890'

    factory_invoice_reference(invoice_id=invoice1.id,
                              invoice_number=invoice_number)
    factory_invoice_reference(invoice_id=invoice2.id,
                              invoice_number=invoice_number)

    invoice1.invoice_status_code = InvoiceStatus.SETTLEMENT_SCHEDULED.value
    invoice2.invoice_status_code = InvoiceStatus.SETTLEMENT_SCHEDULED.value
    invoice1.save()
    invoice2.save()
    invoice1_id = invoice1.id
    invoice2_id = invoice2.id
    total = invoice1.total + invoice2.total

    # Create a settlement file and publish.
    file_name: str = 'cas_settlement_file.csv'
    # Settlement row
    receipt_number = '1234567890'
    credit_memo_number = 'CM123'
    date = datetime.now().strftime('%d-%b-%y')
    credit_amount = 25

    credit_row = [
        RecordType.CMAP.value, SourceTransaction.CREDIT_MEMO.value,
        credit_memo_number, 100002, date, credit_amount, cfs_account_number,
        'INV', invoice_number, total, 0, Status.PAID.value
    ]
    pad_row = [
        RecordType.PAD.value, SourceTransaction.PAD.value, receipt_number,
        100001, date, total - credit_amount, cfs_account_number, 'INV',
        invoice_number, total, 0, Status.PAID.value
    ]
    create_and_upload_settlement_file(file_name, [credit_row, pad_row])
    await helper_add_event_to_queue(events_stan, file_name=file_name)

    # The invoice should be in PAID status and Payment should be completed
    updated_invoice1 = InvoiceModel.find_by_id(invoice1_id)
    assert updated_invoice1.invoice_status_code == InvoiceStatus.PAID.value
    updated_invoice2 = InvoiceModel.find_by_id(invoice2_id)
    assert updated_invoice2.invoice_status_code == InvoiceStatus.PAID.value

    payment: PaymentModel = PaymentModel.find_payment_by_receipt_number(
        receipt_number)
    assert payment.payment_status_code == PaymentStatus.COMPLETED.value
    assert payment.paid_amount == total - credit_amount
    assert payment.receipt_number == receipt_number
    assert payment.payment_method_code == PaymentMethod.PAD.value
    assert payment.invoice_number == invoice_number

    rcpt1: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(
        invoice1_id, receipt_number)
    rcpt2: ReceiptModel = ReceiptModel.find_by_invoice_id_and_receipt_number(
        invoice2_id, receipt_number)
    assert rcpt1
    assert rcpt2
    assert rcpt1.receipt_date == rcpt2.receipt_date