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
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()
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()
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
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
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
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)
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()
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
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()
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)
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
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())
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
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 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()
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
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, )
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)
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
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'
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
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()
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
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)
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
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
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
# 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."""
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