Пример #1
0
def test_payment_usd_payment(session):
    """Assert a payment with usd payment is stored.

    Start with a blank database.
    """
    payment = Payment(payment_system_code='PAYBC',
                      payment_method_code='CC',
                      payment_status_code='CREATED',
                      paid_usd_amount=100)
    payment.save()
    assert payment.id is not None
    assert payment.paid_usd_amount == 100
Пример #2
0
    def _process_cfs_refund(cls, invoice: InvoiceModel):
        """Process refund in CFS."""
        if invoice.payment_method_code in ([
                PaymentMethod.DIRECT_PAY.value, PaymentMethod.DRAWDOWN.value
        ]):
            cls._publish_to_mailer(invoice)
            payment: PaymentModel = PaymentModel.find_payment_for_invoice(
                invoice.id)
            payment.payment_status_code = PaymentStatus.REFUNDED.value
            payment.flush()
        else:
            # Create credit memo in CFS if the invoice status is PAID.
            # Don't do anything is the status is APPROVED.
            if invoice.invoice_status_code == InvoiceStatus.APPROVED.value:
                return
            cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(
                invoice.payment_account_id)
            line_items: List[PaymentLineItemModel] = []
            for line_item in invoice.payment_line_items:
                line_items.append(PaymentLineItemModel.find_by_id(
                    line_item.id))

            cms_response = CFSService.create_cms(line_items=line_items,
                                                 cfs_account=cfs_account)
            # TODO Create a payment record for this to show up on transactions, when the ticket comes.
            # Create a credit with CM identifier as CMs are not reported in payment interface file
            # until invoice is applied.
            CreditModel(cfs_identifier=cms_response.get('credit_memo_number'),
                        is_credit_memo=True,
                        amount=invoice.total,
                        remaining_amount=invoice.total,
                        account_id=invoice.payment_account_id).save()
Пример #3
0
    def _process_cfs_refund(cls, invoice: InvoiceModel):
        """Process refund in CFS."""
        if invoice.payment_method_code == PaymentMethod.DIRECT_PAY.value:
            cls._publish_to_mailer(invoice)
            payment: PaymentModel = PaymentModel.find_payment_for_invoice(
                invoice.id)
            payment.payment_status_code = PaymentStatus.REFUNDED.value
            payment.flush()
        else:
            # Create credit memo in CFS.
            # TODO Refactor this when actual task is done. This is just a quick fix for CFS UAT - Dec 2020
            cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(
                invoice.payment_account_id)
            line_items: List[PaymentLineItemModel] = []
            for line_item in invoice.payment_line_items:
                line_items.append(PaymentLineItemModel.find_by_id(
                    line_item.id))

            cms_response = CFSService.create_cms(line_items=line_items,
                                                 cfs_account=cfs_account)
            # TODO Create a payment record for this to show up on transactions, when the ticket comes.
            # Create a credit with CM identifier as CMs are not reported in payment interface file
            # until invoice is applied.
            CreditModel(cfs_identifier=cms_response.get('credit_memo_number'),
                        is_credit_memo=True,
                        amount=invoice.total,
                        remaining_amount=invoice.total,
                        account_id=invoice.payment_account_id).save()
Пример #4
0
    def create_receipt(payment_identifier: str, invoice_identifier: str, filing_data: Dict[str, Any],
                       skip_auth_check: bool = False, **kwargs):
        """Create receipt."""
        current_app.logger.debug('<create receipt initiated')
        receipt_dict = {
            'templateName': 'payment_receipt',
            'reportName': filing_data.pop('fileName', 'payment_receipt')
        }

        template_vars = {}
        template_vars.update(filing_data)

        # invoice number not mandatory ;since only one invoice exist for a payment now
        if not invoice_identifier:
            invoice_data = Invoice.find_by_payment_identifier(payment_identifier, skip_auth_check=skip_auth_check)
        else:
            invoice_data = Invoice.find_by_id(invoice_identifier, payment_identifier, skip_auth_check=skip_auth_check)

        payment_account = PaymentAccount.find_by_pay_system_id(
            credit_account_id=invoice_data.credit_account_id,
            internal_account_id=invoice_data.internal_account_id,
            bcol_account_id=invoice_data.bcol_account_id)

        invoice_reference = InvoiceReference.find_completed_reference_by_invoice_id(invoice_data.id)

        # template_vars['incorporationNumber'] = payment_account.corp_number
        template_vars['invoiceNumber'] = invoice_reference.invoice_number

        if payment_account.payment_system_code == PaymentSystem.INTERNAL.value and invoice_data.routing_slip:
            template_vars['routingSlipNumber'] = invoice_data.routing_slip

        if not invoice_data.receipts:
            raise BusinessException(Error.INVALID_REQUEST)

        template_vars['receiptNo'] = invoice_data.receipts[0].receipt_number
        template_vars['filingIdentifier'] = filing_data.get('filingIdentifier', invoice_data.filing_id)

        if invoice_data.bcol_account_id:
            bcol_account: BcolPaymentAccountModel = BcolPaymentAccountModel.find_by_id(invoice_data.bcol_account_id)
            template_vars['bcOnlineAccountNumber'] = bcol_account.bcol_account_id

        payment_method = PaymentModel.find_payment_method_by_payment_id(payment_identifier)

        # TODO fix properly later
        if not invoice_data.internal_account_id:
            template_vars['paymentMethod'] = payment_method.description

        template_vars['invoice'] = camelcase_dict(invoice_data.asdict(), {})

        receipt_dict['templateVars'] = template_vars

        current_app.logger.debug(
            '<OAuthService invoked from receipt.py {}'.format(current_app.config.get('REPORT_API_BASE_URL')))

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

        return pdf_response
Пример #5
0
    def search_purchase_history(
            cls,
            auth_account_id: str,  # pylint: disable=too-many-locals, too-many-arguments
            search_filter: Dict,
            page: int,
            limit: int,
            return_all: bool = False):
        """Search purchase history for the account."""
        current_app.logger.debug(f'<search_purchase_history {auth_account_id}')
        # If the request filter is empty, return N number of records
        # Adding offset degrades performance, so just override total records by default value if no filter is provided
        max_no_records: int = 0
        if not bool(search_filter) or not any(search_filter.values()):
            max_no_records = current_app.config.get(
                'TRANSACTION_REPORT_DEFAULT_TOTAL')

        purchases, total = PaymentModel.search_purchase_history(
            auth_account_id, search_filter, page, limit, return_all,
            max_no_records)
        data = {'total': total, 'page': page, 'limit': limit, 'items': []}

        data = cls.create_payment_report_details(purchases, data)

        current_app.logger.debug('>search_purchase_history')
        return data
Пример #6
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
Пример #7
0
def factory_payment(payment_system_code: str = 'PAYBC',
                    payment_method_code='CC',
                    payment_status_code='CREATED'):
    """Return Factory."""
    return Payment(payment_system_code=payment_system_code,
                   payment_method_code=payment_method_code,
                   payment_status_code=payment_status_code)
Пример #8
0
 def process_cfs_refund(self, invoice: InvoiceModel):
     """Process refund in CFS."""
     self._publish_refund_to_mailer(invoice)
     payment: PaymentModel = PaymentModel.find_payment_for_invoice(
         invoice.id)
     payment.payment_status_code = PaymentStatus.REFUNDED.value
     payment.flush()
Пример #9
0
def test_delete_payment(session, auth_mock, public_user_mock):
    """Assert that the payment records are soft deleted."""
    payment_account = factory_payment_account()
    # payment = factory_payment()
    payment_account.save()
    # payment.save()
    invoice = factory_invoice(payment_account, total=10)
    invoice.save()
    invoice_reference = factory_invoice_reference(invoice.id).save()

    # Create a payment for this reference
    payment = factory_payment(invoice_number=invoice_reference.invoice_number,
                              invoice_amount=10).save()

    fee_schedule = FeeSchedule.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()
    transaction = factory_payment_transaction(payment.id)
    transaction.save()

    PaymentService.delete_invoice(invoice.id)
    invoice = Invoice.find_by_id(invoice.id)

    payment = Payment.find_by_id(payment.id)

    assert invoice.invoice_status_code == InvoiceStatus.DELETED.value
    assert payment.payment_status_code == PaymentStatus.DELETED.value
Пример #10
0
 def process_cfs_refund(self, invoice: InvoiceModel):
     """Process refund in CFS."""
     current_app.logger.debug(f'Processing refund for {invoice.id}')
     super()._publish_refund_to_mailer(invoice)
     payment: PaymentModel = PaymentModel.find_payment_for_invoice(
         invoice.id)
     payment.payment_status_code = PaymentStatus.REFUNDED.value
     payment.flush()
Пример #11
0
def _save_payment(
        payment_date,
        inv_number,
        invoice_amount,  # pylint: disable=too-many-arguments
        paid_amount,
        row,
        status,
        payment_method,
        receipt_number):
    # pylint: disable=import-outside-toplevel
    from pay_api.factory.payment_system_factory import PaymentSystemFactory

    payment_account = _get_payment_account(row)
    pay_service = PaymentSystemFactory.create_from_payment_method(
        payment_method)
    # If status is failed, which means NSF. We already have a COMPLETED payment record, find and update iit.
    payment: PaymentModel = None
    if status == PaymentStatus.FAILED.value:
        payment = _get_payment_by_inv_number_and_status(
            inv_number, PaymentStatus.COMPLETED.value)
        # Just to handle duplicate rows in settlement file,
        # pull out failed payment record if it exists and no COMPLETED payments are present.
        if not payment:
            payment = _get_payment_by_inv_number_and_status(
                inv_number, PaymentStatus.FAILED.value)
    elif status == PaymentStatus.COMPLETED.value:
        # if the payment status is COMPLETED, then make sure there are
        # no other COMPLETED payment for same invoice_number.If found, return. This is to avoid duplicate entries.
        payment = _get_payment_by_inv_number_and_status(
            inv_number, PaymentStatus.COMPLETED.value)
        if payment:
            return

    if not payment:
        payment = PaymentModel()
    payment.payment_method_code = pay_service.get_payment_method_code()
    payment.payment_status_code = status
    payment.payment_system_code = pay_service.get_payment_system_code()
    payment.invoice_number = inv_number
    payment.invoice_amount = invoice_amount
    payment.payment_account_id = payment_account.id
    payment.payment_date = payment_date
    payment.paid_amount = paid_amount
    payment.receipt_number = receipt_number
    db.session.add(payment)
Пример #12
0
    def find_by_id(identifier: int):
        """Find payment by id."""
        payment_dao = PaymentModel.find_by_id(identifier)

        payment = Payment()
        payment._dao = payment_dao  # pylint: disable=protected-access

        current_app.logger.debug('>find_by_id')
        return payment
Пример #13
0
def factory_payment(payment_system_code: str = 'PAYBC',
                    payment_method_code='CC',
                    payment_status_code='DRAFT'):
    """Factory."""
    return Payment(payment_system_code=payment_system_code,
                   payment_method_code=payment_method_code,
                   payment_status_code=payment_status_code,
                   created_by='test',
                   created_on=datetime.now())
Пример #14
0
    def find_payment_for_invoice(invoice_id: int):
        """Find payment for by invoice."""
        payment_dao = PaymentModel.find_payment_for_invoice(invoice_id)
        payment: Payment = None
        if payment_dao:
            payment = Payment._populate(payment_dao)

        current_app.logger.debug('>find_payment_for_invoice')
        return payment
Пример #15
0
    def create_account_payment(auth_account_id: str,
                               is_retry_payment: bool) -> Payment:
        """Create a payment record for the account."""
        payment: Payment = None
        if is_retry_payment:
            # If there are multiple failed payments, consolidate them
            # Else clone failed payment
            # Find all failed payments.
            payments = Payment.get_failed_payments(auth_account_id)
            can_consolidate_invoice: bool = True
            if len(payments) == 1:
                can_consolidate_invoice = False
                failed_payment = payments[0]
            else:
                # Here iterate the payments and see if there is a failed PARTIAL payment.
                for payment in payments:
                    paid_amount = payment.paid_amount or 0
                    if payment.payment_status_code == PaymentStatus.FAILED.value and paid_amount > 0:
                        failed_payment = payment
                        can_consolidate_invoice = False
                        break

            if not can_consolidate_invoice:
                # Find if there is a payment for the same invoice number, with status CREATED.
                # If yes, use that record
                # Else create new one.
                stale_payments = PaymentModel.find_payment_by_invoice_number_and_status(
                    inv_number=failed_payment.invoice_number,
                    payment_status=PaymentStatus.CREATED.value)
                # pick the first one. Ideally only one will be there, but a race condition can cause multiple.
                if len(stale_payments) > 0:
                    payment = Payment._populate(stale_payments[0])

                # For consolidated payment status will be CREATED, if so don't create another payment record.
                elif failed_payment.payment_status_code == PaymentStatus.FAILED.value:
                    invoice_total = 0
                    for inv in InvoiceModel.find_invoices_for_payment(
                            payment_id=failed_payment.id):
                        invoice_total += inv.total

                    payment = Payment.create(
                        payment_method=PaymentMethod.CC.value,
                        payment_system=PaymentSystem.PAYBC.value,
                        invoice_number=failed_payment.invoice_number,
                        invoice_amount=invoice_total -
                        float(failed_payment.paid_amount or 0),
                        payment_account_id=failed_payment.payment_account_id)
                else:
                    payment = Payment._populate(failed_payment)

            else:  # Consolidate invoices into a single payment.
                payment = Payment._consolidate_payments(
                    auth_account_id, payments)

        current_app.logger.debug('>create_account_payment')
        return payment
Пример #16
0
def factory_payment(payment_system_code: str = 'PAYBC',
                    payment_method_code: str = 'CC',
                    payment_status_code: str = PaymentStatus.CREATED.value,
                    payment_date: datetime = datetime.now(),
                    invoice_number: str = None):
    """Return Factory."""
    return Payment(payment_system_code=payment_system_code,
                   payment_method_code=payment_method_code,
                   payment_status_code=payment_status_code,
                   payment_date=payment_date,
                   invoice_number=invoice_number).save()
Пример #17
0
def test_payments_marked_for_delete(session):
    """Assert a payment is stored.

    Start with a blank database.
    """
    payment = factory_payment()
    payment.payment_status_code = PaymentStatus.DELETE_ACCEPTED.value
    payment.save()
    assert payment.id is not None
    payments = Payment.find_payments_marked_for_delete()
    assert len(payments) == 1
Пример #18
0
def factory_payment(payment_system_code: str = 'PAYBC',
                    payment_method_code: str = 'CC',
                    payment_status_code: str = PaymentStatus.CREATED.value,
                    created_on: datetime = datetime.now()):
    """Return Factory."""
    return Payment(
        payment_system_code=payment_system_code,
        payment_method_code=payment_method_code,
        payment_status_code=payment_status_code,
        created_by='test',
        created_on=created_on,
    )
Пример #19
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)
Пример #20
0
def factory_payment(payment_system_code: str = 'PAYBC',
                    payment_method_code: str = 'CC',
                    payment_status_code: str = PaymentStatus.CREATED.value,
                    invoice_number: str = None,
                    payment_account_id: str = None,
                    invoice_amount=0):
    """Return Factory."""
    payment: Payment = Payment(payment_system_code=payment_system_code,
                               payment_method_code=payment_method_code,
                               payment_status_code=payment_status_code,
                               invoice_number=invoice_number,
                               payment_account_id=payment_account_id,
                               invoice_amount=invoice_amount)
    return payment
Пример #21
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'
Пример #22
0
    def find_by_id(identifier: int, jwt: JwtManager = None, skip_auth_check: bool = False,
                   one_of_roles: Tuple = ALL_ALLOWED_ROLES):
        """Find payment by id."""
        payment_dao = PaymentModel.find_by_id(identifier)

        # Check if user is authorized to view the payment
        if not skip_auth_check and payment_dao:
            for invoice in payment_dao.invoices:
                check_auth(invoice.account.corp_number, jwt, one_of_roles=one_of_roles)

        payment = Payment()
        payment._dao = payment_dao  # pylint: disable=protected-access

        current_app.logger.debug('>find_by_id')
        return payment
Пример #23
0
def factory_payment(pay_account: PaymentAccount,
                    invoice_number: str = '10021',
                    status=PaymentStatus.CREATED.value,
                    payment_method_code=PaymentMethod.ONLINE_BANKING.value,
                    invoice_amount: float = 100,
                    paid_amount: float = 0,
                    receipt_number: str = ''):
    """Return Factory."""
    return Payment(payment_status_code=status,
                   payment_system_code=PaymentSystem.PAYBC.value,
                   payment_method_code=payment_method_code,
                   payment_account_id=pay_account.id,
                   invoice_amount=invoice_amount,
                   invoice_number=invoice_number,
                   paid_amount=paid_amount,
                   receipt_number=receipt_number).save()
Пример #24
0
    def get_receipt_details(filing_data, invoice_identifier,
                            payment_identifier, skip_auth_check):
        """Return receipt details."""
        receipt_details: dict = {}
        # invoice number not mandatory ;since only one invoice exist for a payment now
        if not invoice_identifier:
            invoice_data = Invoice.find_by_payment_identifier(
                payment_identifier, skip_auth_check=skip_auth_check)
        else:
            invoice_data = Invoice.find_by_id(invoice_identifier,
                                              payment_identifier,
                                              skip_auth_check=skip_auth_check)
        payment_account = PaymentAccount.find_by_pay_system_id(
            credit_account_id=invoice_data.credit_account_id,
            internal_account_id=invoice_data.internal_account_id,
            bcol_account_id=invoice_data.bcol_account_id)
        invoice_reference = InvoiceReference.find_completed_reference_by_invoice_id(
            invoice_data.id)

        receipt_details['invoiceNumber'] = invoice_reference.invoice_number
        if payment_account.payment_system_code == PaymentSystem.INTERNAL.value and invoice_data.routing_slip:
            receipt_details['routingSlipNumber'] = invoice_data.routing_slip
        if not invoice_data.receipts:
            raise BusinessException(Error.INVALID_REQUEST)
        receipt_details['receiptNumber'] = invoice_data.receipts[
            0].receipt_number
        receipt_details['filingIdentifier'] = filing_data.get(
            'filingIdentifier', invoice_data.filing_id)
        if invoice_data.bcol_account_id:
            bcol_account: BcolPaymentAccountModel = BcolPaymentAccountModel.find_by_id(
                invoice_data.bcol_account_id)
            receipt_details[
                'bcOnlineAccountNumber'] = bcol_account.bcol_account_id
        payment_method = PaymentModel.find_payment_method_by_payment_id(
            payment_identifier)
        # TODO fix properly later
        if not invoice_data.internal_account_id:
            receipt_details['paymentMethod'] = payment_method.description
        receipt_details['invoice'] = camelcase_dict(invoice_data.asdict(), {})
        return receipt_details
Пример #25
0
def delete_marked_payments(app):
    """Update stale payment records. 
    
    This is to handle edge cases where the user has completed payment and some error occured and payment status is not up-to-date.
    """
    payments_to_delete = PaymentModel.find_payments_marked_for_delete()
    if len(payments_to_delete) == 0:
        app.logger.info(
            f'Delete Payment Job Ran at {datetime.datetime.now()}.But No records found!'
        )
    for payment in payments_to_delete:
        try:
            app.logger.info(
                'Delete Payment Job found records.Payment Id: {}'.format(
                    payment.id))
            PaymentService.delete_payment(payment.id)
            app.logger.info(
                'Delete Payment Job Updated records.Payment Id: {}'.format(
                    payment.id))
        except BusinessException as err:  # just catch and continue .Don't stop
            app.logger.error('Error on delete_payment')
            app.logger.error(err)
Пример #26
0
    def search_account_payments(auth_account_id: str, status: str, page: int,
                                limit: int):
        """Search account payments."""
        current_app.logger.debug('<search_account_payments')
        results, total = PaymentModel.search_account_payments(
            auth_account_id=auth_account_id,
            payment_status=status,
            page=page,
            limit=limit)

        data = {'total': total, 'page': page, 'limit': limit, 'items': []}

        # Result is tuple of payment and invoice records.
        # Iterate the results and group all invoices for the same payment by keeping the last payment object to compare.
        last_payment_iter = None
        payment_dict = None

        for result in results:
            payment = result[0]
            invoice = result[1]
            if last_payment_iter is None or payment.id != last_payment_iter.id:  # Payment doesn't exist in array yet
                payment_schema = PaymentSchema()
                payment_dict = payment_schema.dump(payment)
                payment_dict['invoices'] = []
                inv_schema = InvoiceSchema(exclude=('receipts', 'references',
                                                    '_links'))
                payment_dict['invoices'] = [inv_schema.dump(invoice)]
                data['items'].append(payment_dict)
            else:
                inv_schema = InvoiceSchema(exclude=('receipts', 'references',
                                                    '_links'))
                payment_dict['invoices'].append(inv_schema.dump(invoice))

            last_payment_iter = payment

        current_app.logger.debug('>search_account_payments')
        return data
Пример #27
0
def factory_routing_slip_account(
        number: str = '1234',
        status: str = CfsAccountStatus.PENDING.value,
        total: int = 0,
        remaining_amount: int = 0,
        routing_slip_date=datetime.now(),
        payment_method=PaymentMethod.CASH.value,
        auth_account_id='1234',
        routing_slip_status=RoutingSlipStatus.ACTIVE.value,
        refund_amount=0):
    """Create routing slip and return payment account with it."""
    payment_account = PaymentAccount(payment_method=payment_method,
                                     name=f'Test {auth_account_id}')
    payment_account.save()

    rs = RoutingSlip(number=number,
                     payment_account_id=payment_account.id,
                     status=routing_slip_status,
                     total=total,
                     remaining_amount=remaining_amount,
                     created_by='test',
                     routing_slip_date=routing_slip_date,
                     refund_amount=refund_amount).save()

    Payment(payment_system_code=PaymentSystem.FAS.value,
            payment_account_id=payment_account.id,
            payment_method_code=PaymentMethod.CASH.value,
            payment_status_code=PaymentStatus.COMPLETED.value,
            receipt_number=number,
            is_routing_slip=True,
            paid_amount=rs.total,
            created_by='TEST')

    CfsAccount(status=status, account_id=payment_account.id).save()

    return payment_account
Пример #28
0
    def search_purchase_history(
            auth_account_id: str,
            search_filter: Dict,
            page: int,  # pylint: disable=too-many-locals
            limit: int,
            return_all: bool = False):
        """Search purchase history for the account."""
        current_app.logger.debug(f'<search_purchase_history {auth_account_id}')
        # If the request filter is empty, return N number of records
        # Adding offset degrades performance, so just override total records by default value if no filter is provided
        max_no_records: int = 0
        if not bool(search_filter) or not any(search_filter.values()):
            max_no_records = current_app.config.get(
                'TRANSACTION_REPORT_DEFAULT_TOTAL')

        purchases, total = PaymentModel.search_purchase_history(
            auth_account_id, search_filter, page, limit, return_all,
            max_no_records)
        data = {'total': total, 'page': page, 'limit': limit, 'items': []}

        invoice_ids = []
        payment_status_codes = CodeService.find_code_values_by_type(
            Code.PAYMENT_STATUS.value)
        for purchase in purchases:
            payment_dao = purchase[0]
            invoice_dao = purchase[1]
            payment_schema = PaymentSchema(exclude=('invoices', 'transactions',
                                                    '_links', 'created_by',
                                                    'updated_by'))
            purchase_history = payment_schema.dump(payment_dao)

            filtered_codes = [
                cd for cd in payment_status_codes['codes']
                if cd['code'] == purchase_history['status_code']
            ]
            if filtered_codes:
                purchase_history['status_code'] = filtered_codes[0][
                    'description']

            invoice_schema = InvoiceSchema(
                exclude=('receipts', 'payment_line_items', 'references',
                         '_links', 'created_by', 'created_name', 'created_on',
                         'updated_by', 'updated_name', 'updated_on',
                         'invoice_status_code'))
            invoice = invoice_schema.dump(invoice_dao)
            invoice['line_items'] = []
            purchase_history['invoice'] = invoice
            data['items'].append(purchase_history)

            invoice_ids.append(invoice_dao.id)

        # Query the payment line item to retrieve more details
        payment_line_items = PaymentLineItem.find_by_invoice_ids(invoice_ids)
        for payment_line_item in payment_line_items:
            for purchase_history in data['items']:
                if purchase_history.get('invoice').get(
                        'id') == payment_line_item.invoice_id:
                    line_item_schema = PaymentLineItemSchema(
                        many=False, exclude=('id', 'line_item_status_code'))
                    line_item_dict = line_item_schema.dump(payment_line_item)
                    line_item_dict[
                        'filing_type_code'] = payment_line_item.fee_schedule.filing_type_code
                    purchase_history.get('invoice').get('line_items').append(
                        line_item_dict)

        current_app.logger.debug('>search_purchase_history')
        return data
Пример #29
0
        # find if its a routing slip refund
        # if yes -> check existing one or legacy
        # if yes , add money back to rs after refunding

        if (routing_slip_number := invoice.routing_slip) is None:
            raise BusinessException(Error.INVALID_REQUEST)
        if invoice.total == 0:
            raise BusinessException(Error.NO_FEE_REFUND)
        if not (routing_slip :=
                RoutingSlipModel.find_by_number(routing_slip_number)):
            raise BusinessException(Error.ROUTING_SLIP_REFUND)
        current_app.logger.info(
            f'Processing refund for {invoice.id}, on routing slip {routing_slip.number}'
        )
        payment: PaymentModel = PaymentModel.find_payment_for_invoice(
            invoice.id)
        if payment:
            payment.payment_status_code = PaymentStatus.REFUNDED.value
            payment.flush()
        routing_slip.remaining_amount += get_quantized(invoice.total)
        # Move routing slip back to active on refund.
        if routing_slip.status == RoutingSlipStatus.COMPLETE.value:
            routing_slip.status = RoutingSlipStatus.ACTIVE.value
        routing_slip.flush()
        invoice.invoice_status_code = InvoiceStatus.REFUND_REQUESTED.value
        invoice.flush()

    @staticmethod
    def _validate_routing_slip(routing_slip: RoutingSlipModel,
                               invoice: Invoice):
        """Validate different conditions of a routing slip payment."""
Пример #30
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