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
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)