import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { SpinnerService } from '@core/services/spinner.service'; import { StatusService } from '@core/services/status.service'; import { BatchItem, BatchItemFromApi, ProcessingTypes } from '@core/typings/payment.typing'; import { BatchStatuses } from '@core/typings/status.typing'; import { ClientSettingsService } from '@features/client-settings/client-settings.service'; import { SendToProcessingModalComponent } from '@features/payment-processing/send-to-processing-modal/send-to-processing-modal.component'; import { ALL_SKIP_FILTER, AutoTableRepository, AutoTableRepositoryFactory, DebounceFactory, PaginationOptions, TopLevelFilter, TopLevelFilterOptionsConfig, TypeSafeFormBuilder, TypeSafeFormGroup, ValueComparisonService } from '@yourcause/common'; import { CachedAttr, CACHE_TYPES } from '@yourcause/common/cache'; import { I18nService } from '@yourcause/common/i18n'; import { ConfirmationModalComponent, ModalFactory } from '@yourcause/common/modals'; import { NotifierService } from '@yourcause/common/notifier'; import { from as observableFrom, map, Subscription } from 'rxjs'; import { PaymentProcessingResources } from '../payment-processing.resources'; import { PaymentProcessingService } from '../payment-processing.service'; import { UpdateBatchStatusModalComponent } from '../update-batch-status-modal/update-batch-status-modal.component'; interface ScheduleGroup { [x: string]: any; } @Component({ selector: 'gc-schedule-page', templateUrl: './schedule-page.component.html', styleUrls: ['./schedule-page.component.scss'] }) export class SchedulePageComponent implements OnInit { repository: AutoTableRepository; topLevelFilters: TopLevelFilter[] = []; formGroup: TypeSafeFormGroup; batchStatusMap = this.statusService.get('batchStatusMap'); tableDataFactory = DebounceFactory.createSimple( (options: PaginationOptions) => { options.sortColumns = [{ columnName: 'batchStatusLastUpdatedDate', sortAscending: false }]; return observableFrom( this.paymentProcessingResources.getBatches(options) ).pipe(map(result => ({ success: true, data: { recordCount: result.recordCount, records: this.adaptBatches(result.records) } }))); }, 0); sub = new Subscription(); statusOptions: TopLevelFilterOptionsConfig = { selectOptions: [{ value: ALL_SKIP_FILTER, display: this.i18n.translate('common:lblAllCap') }, { value: BatchStatuses.Open, display: this.i18n.translate('GLOBAL:textOpen') }, { value: BatchStatuses.Reviewed, display: this.i18n.translate('GLOBAL:textReviewed') }, { value: BatchStatuses.Processing, display: this.i18n.translate( 'GLOBAL:textSentToProcessingPending', {}, 'Sent to processing - Pending' ) }, { value: BatchStatuses.Funded, display: this.i18n.translate('GLOBAL:textFunded') }, { value: BatchStatuses.Disbursed, display: this.i18n.translate('GLOBAL:lblDisbursed') }, { value: BatchStatuses.Complete, display: this.i18n.translate('GLOBAL:textComplete') }] }; @CachedAttr(CACHE_TYPES.LOCALSTORAGE, ALL_SKIP_FILTER) status: number; constructor ( private notifierService: NotifierService, private valueComparisonService: ValueComparisonService, private autoTableFactory: AutoTableRepositoryFactory, private cdr: ChangeDetectorRef, private router: Router, private modalFactory: ModalFactory, private spinnerService: SpinnerService, private paymentProcessingService: PaymentProcessingService, private paymentProcessingResources: PaymentProcessingResources, private statusService: StatusService, private clientSettingsService: ClientSettingsService, private i18n: I18nService, private formBuilder: TypeSafeFormBuilder ) { } get isAvailable () { return location.pathname.includes('available'); } get branding () { return this.clientSettingsService.get('clientBranding'); } get availablePayments () { return this.paymentProcessingService.get('availablePayments'); } ngOnInit () { if (!this.isAvailable) { this.setTopLevelFilters(); this.repository = this.autoTableFactory.create({ key: 'BATCHES', columns: [], notifier: this.notifierService, rowsPerPage: 10, valueComparisonService: this.valueComparisonService, tableDataFactory: this.tableDataFactory, cdr: this.cdr, topLevelFilters: this.topLevelFilters, formGroup: this.formGroup }); } } setTopLevelFilters () { this.topLevelFilters = [ new TopLevelFilter( 'text', 'name', '', this.i18n.translate( 'MANAGE:textSearchByBatchName', {}, 'Search by batch name' ) ), new TopLevelFilter( 'typeaheadSingleEquals', 'batchStatusId', this.status, '', this.statusOptions, this.i18n.translate('common:lblStatusFilter') ) ]; this.formGroup = this.formBuilder.group( this.topLevelFilters.reduce((formGroup, filter) => ({ ...formGroup, [filter.column]: [filter.value] }), {}) ); const existingRepo = this.autoTableFactory.getRepository('BATCHES'); if ( existingRepo && existingRepo.topLevelFormGroup !== this.formGroup ) { this.formGroup = existingRepo.topLevelFormGroup; } } onTopLevelFilterChange (filter: TopLevelFilter) { if (filter.column === 'batchStatusId') { this.status = filter.value; } } adaptBatches (batches: BatchItemFromApi[]): BatchItem[] { return batches.map((batch) => { return { id: batch.batchId, name: `${ batch.externalBatchId ? batch.externalBatchId + ' - ' : '' }${batch.name}`, processorName: batch.processingTypeId === ProcessingTypes.Client ? this.branding.name : 'YourCause', processorType: batch.processingTypeId, paymentDates: '', batchDetails: { statusDetailText: this.getStatusText(batch), totalPaymentsNumber: batch.totalPaymentsInBatch, totalPaymentsAmount: batch.totalPaymentsAmount }, statusId: batch.batchStatusId, availableDetails: null, externalBatchId: batch.externalBatchId }; }); } getStatusText (batch: BatchItemFromApi) { return this.paymentProcessingService.getStatusText(batch); } managePayments (item: BatchItem) { const id = this.isAvailable ? ( item.processorType === ProcessingTypes.YourCause ? 'yourcause' : 'external' ) : item.id; this.router.navigate([ `management/payment-processing/${ this.isAvailable ? 'manage-available' : 'manage-batch' }/${id}/included` ]); } viewPayments (item: BatchItem) { this.router.navigate([ `management/payment-processing/manage-batch/${item.id}/included` ]); } async deleteBatch (item: BatchItem) { const deps = { confirmButtonText: this.i18n.translate( 'common:btnDelete', {}, 'Delete' ), confirmText: this.i18n.translate( 'MANAGE:textAreYouSureDeleteTheBatch', {}, 'Are you sure you want to delete the batch? This will move all associated batch payments to a status of Pending.' ), modalHeader: this.i18n.translate( 'MANAGE:hdrDeleteBatch', { name: item.name }, 'Delete Batch - __name__' ) }; const proceed = await this.modalFactory.open( ConfirmationModalComponent, deps ); if (proceed) { this.spinnerService.startSpinner(); await this.paymentProcessingService.handleDeleteBatch(item.id); this.spinnerService.stopSpinner(); } } async sendToProcessing (item: BatchItem) { const totalPaymentsNumber = item.availableDetails ? item.availableDetails.includedPayments.number : item.batchDetails.totalPaymentsNumber; const totalPaymentsAmount = item.availableDetails ? item.availableDetails.includedPayments.total : item.batchDetails.totalPaymentsAmount; const adapted = { ...item, batchDetails: { totalPaymentsNumber, totalPaymentsAmount } }; const response = await this.modalFactory.open( SendToProcessingModalComponent, { item: adapted, isAvailable: this.isAvailable } ); if (response) { this.spinnerService.startSpinner(); if (this.isAvailable) { await this.paymentProcessingService.handleSendAvailableToProcessing( item, response, false ); await this.paymentProcessingService.resetPaymentStats(); } else { await this.paymentProcessingService.handleSendBatchToProcessing( item.id, response.notes, response.paymentStatus, response.issueDate ); } this.spinnerService.stopSpinner(); } } async updateClientBatchStatus (data: { item: BatchItem; newStatus: BatchStatuses; }) { const response = await this.modalFactory.open( UpdateBatchStatusModalComponent, { newStatus: data.newStatus, item: data.item } ); if (!!response) { this.spinnerService.startSpinner(); await this.paymentProcessingService.updateBatchStatusModal( data.item, response.notes, data.newStatus ); this.spinnerService.stopSpinner(); } } }