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
Esempio n. 2
0
def factory_invoice(payment_account: PaymentAccount,
                    status_code: str = InvoiceStatus.CREATED.value,
                    corp_type_code='CP',
                    business_identifier: str = 'CP0001234',
                    service_fees: float = 0.0,
                    total=0,
                    payment_method_code: str = PaymentMethod.DIRECT_PAY.value,
                    created_on: datetime = datetime.now(),
                    cfs_account_id: int = 0,
                    routing_slip=None):
    """Return Factory."""
    status_code = InvoiceStatus.APPROVED.value if payment_method_code == PaymentMethod.PAD.value else status_code
    invoice = Invoice(invoice_status_code=status_code,
                      payment_account_id=payment_account.id,
                      total=total,
                      created_by='test',
                      created_on=created_on,
                      business_identifier=business_identifier,
                      corp_type_code=corp_type_code,
                      folio_number='1234567890',
                      service_fees=service_fees,
                      bcol_account=payment_account.bcol_account,
                      payment_method_code=payment_method_code
                      or payment_account.payment_method,
                      routing_slip=routing_slip)
    if cfs_account_id != 0:
        invoice.cfs_account_id = cfs_account_id

    invoice.save()
    return invoice
Esempio n. 3
0
    def save_or_update(distribution_details: Dict, dist_id: int = None):
        """Save distribution."""
        current_app.logger.debug('<save_or_update')

        dist_code_svc = DistributionCode()
        if dist_id is not None:
            dist_code_dao = DistributionCodeModel.find_by_id(dist_id)
            dist_code_svc._dao = dist_code_dao  # pylint: disable=protected-access

        if distribution_details.get('endDate', None):
            dist_code_svc.end_date = parser.parse(
                distribution_details.get('endDate'))

        if distribution_details.get('startDate', None):
            dist_code_svc.start_date = parser.parse(
                distribution_details.get('startDate'))
        else:
            dist_code_svc.start_date = date.today()

        _has_code_changes: bool = dist_code_svc.client != distribution_details.get('client', None) \
            or dist_code_svc.responsibility_centre != distribution_details.get('responsibilityCentre', None) \
            or dist_code_svc.service_line != distribution_details.get('serviceLine', None) \
            or dist_code_svc.project_code != distribution_details.get('projectCode', None) \
            or dist_code_svc.service_fee_distribution_code_id != \
            distribution_details.get('serviceFeeDistributionCodeId', None)

        dist_code_svc.client = distribution_details.get('client', None)
        dist_code_svc.name = distribution_details.get('name', None)
        dist_code_svc.responsibility_centre = distribution_details.get(
            'responsibilityCentre', None)
        dist_code_svc.service_line = distribution_details.get(
            'serviceLine', None)
        dist_code_svc.stob = distribution_details.get('stob', None)
        dist_code_svc.project_code = distribution_details.get(
            'projectCode', None)
        dist_code_svc.service_fee_distribution_code_id = distribution_details.get(
            'serviceFeeDistributionCodeId', None)
        dist_code_svc.disbursement_distribution_code_id = distribution_details.get(
            'disbursementDistributionCodeId', None)
        dist_code_svc.account_id = distribution_details.get('accountId', None)

        if _has_code_changes and dist_id is not None:
            # Update all invoices which used this distribution for updating revenue account details
            # If this is a service fee distribution, then find all distribution which uses this and update them.
            InvoiceModel.update_invoices_for_revenue_updates(dist_id)
            for dist in DistributionCodeModel.find_by_service_fee_distribution_id(
                    dist_id):
                InvoiceModel.update_invoices_for_revenue_updates(
                    dist.distribution_code_id)

        # Reset stop jv for every dave.
        dist_code_svc.stop_ejv = False
        dist_code_dao = dist_code_svc.save()

        distribution_code_schema = DistributionCodeSchema()
        current_app.logger.debug('>save_or_update')
        return distribution_code_schema.dump(dist_code_dao, many=False)
Esempio n. 4
0
    def _create_nsf_invoice(cls, cfs_account: CfsAccountModel, rs_number: str,
                            payment_account: PaymentAccountModel) -> InvoiceModel:
        """Create Invoice, line item and invoice reference records."""
        fee_schedule: FeeScheduleModel = FeeScheduleModel.find_by_filing_type_and_corp_type(corp_type_code='BCR',
                                                                                            filing_type_code='NSF')
        invoice = InvoiceModel(
            bcol_account=payment_account.bcol_account,
            payment_account_id=payment_account.id,
            cfs_account_id=cfs_account.id,
            invoice_status_code=InvoiceStatus.CREATED.value,
            total=fee_schedule.fee.amount,
            service_fees=0,
            paid=0,
            payment_method_code=PaymentMethod.INTERNAL.value,
            corp_type_code='BCR',
            created_on=datetime.now(),
            created_by='SYSTEM',
            routing_slip=rs_number
        )
        invoice = invoice.save()
        distribution: DistributionCodeModel = DistributionCodeModel.find_by_active_for_fee_schedule(
            fee_schedule.fee_schedule_id)

        line_item = PaymentLineItemModel(
            invoice_id=invoice.id,
            total=invoice.total,
            fee_schedule_id=fee_schedule.fee_schedule_id,
            description=fee_schedule.filing_type.description,
            filing_fees=invoice.total,
            gst=0,
            priority_fees=0,
            pst=0,
            future_effective_fees=0,
            line_item_status_code=LineItemStatus.ACTIVE.value,
            service_fees=0,
            fee_distribution_id=distribution.distribution_code_id)
        line_item.save()

        invoice_response = CFSService.create_account_invoice(transaction_number=invoice.id,
                                                             line_items=invoice.payment_line_items,
                                                             cfs_account=cfs_account)

        invoice_number = invoice_response.json().get('invoice_number', None)
        current_app.logger.info(f'invoice_number  {invoice_number}  created in CFS for NSF.')

        InvoiceReferenceModel(
            invoice_id=invoice.id,
            invoice_number=invoice_number,
            reference_number=invoice_response.json().get('pbc_ref_number', None),
            status_code=InvoiceReferenceStatus.ACTIVE.value
        ).save()

        return invoice
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
Esempio n. 6
0
    def find_by_id(identifier: int, pay_id: int = None):
        """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.PAY012)

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

        current_app.logger.debug('>find_by_id')
        return invoice
Esempio n. 7
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
Esempio n. 8
0
    def save_or_update(distribution_details: Dict, dist_id: int = None):
        """Save distribution."""
        current_app.logger.debug('<save_or_update')

        dist_code_svc = DistributionCode()
        if dist_id is not None:
            dist_code_dao = DistributionCodeModel.find_by_id(dist_id)
            dist_code_svc._dao = dist_code_dao  # pylint: disable=protected-access

        if distribution_details.get('endDate', None):
            dist_code_svc.end_date = parser.parse(
                distribution_details.get('endDate'))

        if distribution_details.get('startDate', None):
            dist_code_svc.start_date = parser.parse(
                distribution_details.get('startDate'))
        else:
            dist_code_svc.start_date = date.today()

        dist_code_svc.client = distribution_details.get('client', None)
        dist_code_svc.responsibility_centre = distribution_details.get(
            'responsibilityCentre', None)
        dist_code_svc.service_line = distribution_details.get(
            'serviceLine', None)
        dist_code_svc.stob = distribution_details.get('stob', None)
        dist_code_svc.project_code = distribution_details.get(
            'projectCode', None)

        dist_code_svc.service_fee_client = distribution_details.get(
            'serviceFeeClient', None)
        dist_code_svc.service_fee_responsibility_centre = distribution_details.get(
            'serviceFeeResponsibilityCentre', None)
        dist_code_svc.service_fee_line = distribution_details.get(
            'serviceFeeLine', None)
        dist_code_svc.service_fee_stob = distribution_details.get(
            'serviceFeeStob', None)
        dist_code_svc.service_fee_project_code = distribution_details.get(
            'serviceFeeProjectCode', None)

        if dist_id is not None:
            # Update all invoices which used this distribution for updating revenue account details
            InvoiceModel.update_invoices_for_revenue_updates(dist_id)

        dist_code_dao = dist_code_svc.save()

        distribution_code_schema = DistributionCodeSchema()
        current_app.logger.debug('>save_or_update')
        return distribution_code_schema.dump(dist_code_dao, many=False)
Esempio n. 9
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
Esempio n. 10
0
def factory_invoice(payment: Payment,
                    payment_account: str,
                    status_code: str = InvoiceStatus.CREATED.value,
                    corp_type_code='CP',
                    business_identifier: str = 'CP0001234',
                    service_fees: float = 0.0):
    """Return Factory."""
    bcol_account_id = None
    credit_account_id = None
    internal_account_id = None
    if isinstance(payment_account, BcolPaymentAccount):
        bcol_account_id = payment_account.id
    elif isinstance(payment_account, InternalPaymentAccount):
        internal_account_id = payment_account.id
    if isinstance(payment_account, CreditPaymentAccount):
        credit_account_id = payment_account.id

    return Invoice(payment_id=payment.id,
                   invoice_status_code=status_code,
                   bcol_account_id=bcol_account_id,
                   credit_account_id=credit_account_id,
                   internal_account_id=internal_account_id,
                   total=0,
                   created_by='test',
                   created_on=datetime.now(),
                   business_identifier=business_identifier,
                   corp_type_code=corp_type_code,
                   folio_number='1234567890',
                   service_fees=service_fees)
Esempio n. 11
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)
Esempio n. 12
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)
Esempio n. 13
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
Esempio n. 14
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()
def test_line_saved_from_new(session):
    """Assert that the payment is saved to the table."""
    payment_account = factory_payment_account()
    payment = factory_payment()
    payment_account.save()
    payment.save()
    invoice = factory_invoice(payment.id, payment_account.id)
    invoice.save()
    factory_invoice_reference(invoice.id).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()
    line = factory_payment_line_item(invoice.id, fee_schedule_id=fee_schedule.fee_schedule_id, status='DELETED')
    line.save()

    p = PaymentLineService.find_by_id(line.id)

    assert p is not None
    assert p.id is not None
    assert p.invoice_id is not None
    assert p.filing_fees is not None
    assert p.fee_schedule_id is not None
    assert p.gst is None
    assert p.pst is None
    assert p.line_item_status_code is not None
    assert p.priority_fees is None
    assert p.future_effective_fees is None
    invoice = Invoice.find_by_id(invoice.id)
    schema = InvoiceSchema()
    d = schema.dump(invoice)
    assert d.get('id') == invoice.id
    assert len(d.get('line_items')) == 1
def test_create_pad_invoice_single_transaction_run_again(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,
                              status_code=InvoiceStatus.APPROVED.value, 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()
    invoice_response = {'invoice_number': '10021', 'pbc_ref_number': '10005', 'party_number': '11111',
                        'party_name': 'invoice'}
    assert invoice.invoice_status_code == InvoiceStatus.APPROVED.value
    the_response = Response()
    the_response._content = json.dumps(invoice_response).encode('utf-8')

    with patch.object(CFSService, 'create_account_invoice', return_value=the_response) as mock_cfs:
        CreateInvoiceTask.create_invoices()
        mock_cfs.assert_called()

    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
    assert updated_invoice.invoice_status_code == InvoiceStatus.APPROVED.value

    with patch.object(CFSService, 'create_account_invoice', return_value=the_response) as mock_cfs:
        CreateInvoiceTask.create_invoices()
        mock_cfs.assert_not_called()
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
Esempio n. 18
0
    def delete_account(cls, auth_account_id: str) -> PaymentAccount:
        """Delete the payment account."""
        current_app.logger.debug('<delete_account')
        pay_account: PaymentAccountModel = PaymentAccountModel.find_by_auth_account_id(
            auth_account_id)
        cfs_account: CfsAccountModel = CfsAccountModel.find_effective_by_account_id(
            pay_account.id)
        # 1 - Check if account have any credits
        # 2 - Check if account have any PAD transactions done in last N (10) days.
        if pay_account.credit and pay_account.credit > 0:
            raise BusinessException(Error.OUTSTANDING_CREDIT)
        # Check if account is frozen.
        cfs_status: str = cfs_account.status if cfs_account else None
        if cfs_status == CfsAccountStatus.FREEZE.value:
            raise BusinessException(Error.FROZEN_ACCOUNT)
        if InvoiceModel.find_outstanding_invoices_for_account(
                pay_account.id, get_outstanding_txns_from_date()):
            # Check if there is any recent PAD transactions in N days.
            raise BusinessException(Error.TRANSACTIONS_IN_PROGRESS)

        # If CFS Account present, mark it as INACTIVE.
        if cfs_status and cfs_status != CfsAccountStatus.INACTIVE.value:
            cfs_account.status = CfsAccountStatus.INACTIVE.value
            # If account is active or pending pad activation stop PAD payments.
            if pay_account.payment_method == PaymentMethod.PAD.value \
                    and cfs_status in [CfsAccountStatus.ACTIVE.value, CfsAccountStatus.PENDING_PAD_ACTIVATION.value]:
                CFSService.suspend_cfs_account(cfs_account)
            cfs_account.save()

        if pay_account.statement_notification_enabled:
            pay_account.statement_notification_enabled = False
            pay_account.save()
Esempio n. 19
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
Esempio n. 20
0
    def create_refund(cls, invoice_id: int, request: Dict[str, str],
                      **kwargs) -> Dict[str, str]:
        """Create refund."""
        current_app.logger.debug(f'Starting refund : {invoice_id}')
        # Do validation by looking up the invoice
        invoice: InvoiceModel = InvoiceModel.find_by_id(invoice_id)

        paid_statuses = (InvoiceStatus.PAID.value,
                         InvoiceStatus.APPROVED.value,
                         InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value)

        if invoice.invoice_status_code not in paid_statuses:
            current_app.logger.info(
                f'Cannot process refund as status of {invoice_id} is {invoice.invoice_status_code}'
            )
            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()
        pay_system_service: PaymentSystemService = PaymentSystemFactory.create_from_payment_method(
            payment_method=invoice.payment_method_code)
        invoice_status = pay_system_service.process_cfs_refund(invoice)
        message = REFUND_SUCCESS_MESSAGES.get(
            f'{invoice.payment_method_code}.{invoice.invoice_status_code}')
        # set invoice status
        invoice.invoice_status_code = invoice_status or InvoiceStatus.REFUND_REQUESTED.value
        invoice.refund = invoice.total  # no partial refund
        invoice.save()
        current_app.logger.debug(f'Completed refund : {invoice_id}')
        return {'message': message}
Esempio n. 21
0
def factory_invoice(payment: Payment,
                    payment_account: str,
                    status_code: str = Status.DRAFT.value,
                    corp_type_code='CP',
                    business_identifier: str = 'CP0001234'):
    """Factory."""
    bcol_account_id = None
    credit_account_id = None
    internal_account_id = None
    if isinstance(payment_account, BcolPaymentAccount):
        bcol_account_id = payment_account.id
    elif isinstance(payment_account, InternalPaymentAccount):
        internal_account_id = payment_account.id
    if isinstance(payment_account, CreditPaymentAccount):
        credit_account_id = payment_account.id

    return Invoice(payment_id=payment.id,
                   invoice_status_code=status_code,
                   bcol_account_id=bcol_account_id,
                   credit_account_id=credit_account_id,
                   internal_account_id=internal_account_id,
                   total=0,
                   created_by='test',
                   created_on=datetime.now(),
                   business_identifier=business_identifier,
                   corp_type_code=corp_type_code)
Esempio n. 22
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
Esempio n. 23
0
def factory_invoice(payment_id: str, account_id: str):
    """Factory."""
    return Invoice(payment_id=payment_id,
                   invoice_status_code='DRAFT',
                   account_id=account_id,
                   total=0,
                   created_by='test',
                   created_on=datetime.now())
Esempio n. 24
0
def _create_nsf_invoice(cfs_account: CfsAccountModel, inv_number: str,
                        payment_account: PaymentAccountModel) -> InvoiceModel:
    """Create Invoice, line item and invoice referwnce records."""
    fee_schedule: FeeScheduleModel = FeeScheduleModel.find_by_filing_type_and_corp_type(
        corp_type_code='BCR', filing_type_code='NSF')
    invoice = InvoiceModel(bcol_account=payment_account.bcol_account,
                           payment_account_id=payment_account.id,
                           cfs_account_id=cfs_account.id,
                           invoice_status_code=InvoiceStatus.CREATED.value,
                           total=fee_schedule.fee.amount,
                           service_fees=0,
                           paid=0,
                           payment_method_code=PaymentMethod.CC.value,
                           corp_type_code='BCR',
                           created_on=datetime.now(),
                           created_by='SYSTEM')
    invoice = invoice.save()
    distribution: DistributionCodeModel = DistributionCodeModel.find_by_active_for_fee_schedule(
        fee_schedule.fee_schedule_id)

    line_item = PaymentLineItemModel(
        invoice_id=invoice.id,
        total=invoice.total,
        fee_schedule_id=fee_schedule.fee_schedule_id,
        description=fee_schedule.filing_type.description,
        filing_fees=invoice.total,
        gst=0,
        priority_fees=0,
        pst=0,
        future_effective_fees=0,
        line_item_status_code=LineItemStatus.ACTIVE.value,
        service_fees=0,
        fee_distribution_id=distribution.distribution_code_id
        if distribution else 1)  # TODO
    line_item.save()

    inv_ref: InvoiceReferenceModel = InvoiceReferenceModel(
        invoice_id=invoice.id,
        invoice_number=inv_number,
        reference_number=InvoiceReferenceModel.
        find_any_active_reference_by_invoice_number(
            invoice_number=inv_number).reference_number,
        status_code=InvoiceReferenceStatus.ACTIVE.value)
    inv_ref.save()

    return invoice
Esempio n. 25
0
    def find_by_payment_identifier(identifier: int):
        """Find invoice by payment identifier."""
        invoice_dao = InvoiceModel.find_by_payment_id(identifier)

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

        current_app.logger.debug('>find_by_id')
        return invoice
Esempio n. 26
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
Esempio n. 27
0
def factory_invoice(payment_id: str, account_id: str, status_code: str = Status.DRAFT.value):
    """Factory."""
    return Invoice(
        payment_id=payment_id,
        invoice_status_code=status_code,
        account_id=account_id,
        total=0,
        created_by='test',
        created_on=datetime.now(),
    )
Esempio n. 28
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
Esempio n. 29
0
    def find_by_payment_identifier(identifier: int, skip_auth_check: bool = False):
        """Find invoice by payment identifier."""
        invoice_dao = InvoiceModel.find_by_payment_id(identifier)

        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
Esempio n. 30
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)