Просмотр исходного кода

upload lender automatically if there is already a match.

tags/2.037
Patrick Sun 4 лет назад
Родитель
Сommit
562b82658c
11 измененных файлов: 172 добавлений и 38 удалений
  1. +53
    -0
      src/app/app.component.scss
  2. +12
    -2
      src/app/loan-select/loan-select.component.ts
  3. +14
    -4
      src/app/models/Pay.In.AAA.Row.model.ts
  4. +1
    -1
      src/app/models/pay-in-list.filter.model.ts
  5. +14
    -0
      src/app/models/pay-in.model.ts
  6. +0
    -13
      src/app/observable/pay.in.observable.ts
  7. +4
    -2
      src/app/pay-in/lender-aaa-income/lender-aaa-income.component.html
  8. +9
    -8
      src/app/pay-in/lender-aaa-income/lender-aaa-income.component.ts
  9. +5
    -2
      src/app/pay-in/pay-in.component.html
  10. +14
    -0
      src/app/pay-in/pay-in.component.scss
  11. +46
    -6
      src/app/pay-in/pay-in.component.ts

+ 53
- 0
src/app/app.component.scss Просмотреть файл

.upload-details div[role="tabpanel"]{ .upload-details div[role="tabpanel"]{
padding:0px; padding:0px;
} }

@keyframes flash-background {
from {background-color: white;}
to {background-color: lightgoldenrodyellow;}
}
kendo-grid.filterByUploadMeta {
.k-grid-add-row.k-grid-edit-row.ng-star-inserted {
animation: flash-background 1s infinite;
opacity: 1;
}

.k-state-selected {
animation: flash-background 1s 3;
opacity: 1;
}

tr {
position: relative;
}
@keyframes flash{
50% { opacity: 0.5 }
}

tr.dup {
td:first-child{
position: relative;
div {
display:unset;
transition: display 1s;
z-index:1;
}
}
}

tr.missingLoan {
td:last-child {
text-align: left;
&::before{
color: #020202;
background: #ffe172;
width: 110px;
content: "missing loan";
text-align: right;
position: absolute;
overflow:hidden;
right: 0;
border-radius: 10px 0 0 10px;
animation: flash 1s 3;
}
};
}
}


+ 12
- 2
src/app/loan-select/loan-select.component.ts Просмотреть файл

} }


onLoanSelected(loan: LoanModel): void { onLoanSelected(loan: LoanModel): void {
if (loan !== undefined && this.Loan !== undefined && this.Loan.Id !== loan.Id){
let changed = false;
if ( loan === null || loan === undefined) {
if (this.Loan.Id !== '') {
changed = true;
}
}else{
changed = this.Loan.Id !== loan.Id;
}

if (changed ){
this.valueChange.emit(loan); this.valueChange.emit(loan);
this.onChange(loan); this.onChange(loan);
this.Loan = loan;
} }

// update changes
if (loan === undefined || loan == null ){ if (loan === undefined || loan == null ){
this.Loan = new LoanModel({}); this.Loan = new LoanModel({});
}else{ }else{

+ 14
- 4
src/app/models/Pay.In.AAA.Row.model.ts Просмотреть файл

public Period: Date; public Period: Date;
public LoanNumber: string; public LoanNumber: string;
public Settlement: Date; public Settlement: Date;
public LoanAmount: number;
public LoanFacility: number;
public Balance: number; public Balance: number;
public InTrail: number; public InTrail: number;


this.Period = new Date(payload.Period); this.Period = new Date(payload.Period);
this.LoanNumber = payload.LoanNumber || ''; this.LoanNumber = payload.LoanNumber || '';
this.Settlement = new Date(payload.Settlement); this.Settlement = new Date(payload.Settlement);
this.LoanAmount = payload.LoanAmount || 0;
this.Balance = payload.Balance || 0;
this.LoanFacility = payload.LoanFacility || 0;
this.Balance = payload.Balance || -1;
this.InTrail = payload.InTrail || 0; this.InTrail = payload.InTrail || 0;
this.matchedPayIn = payload.matchedPayIn || -1; this.matchedPayIn = payload.matchedPayIn || -1;
} }
const pi = new PayInModel({}); const pi = new PayInModel({});
pi.Lender = this.Lender; pi.Lender = this.Lender;
pi.LoanNumber = this.LoanNumber; pi.LoanNumber = this.LoanNumber;
pi.Amount = this.LoanAmount;
pi.Amount = this.LoanFacility;
pi.Settlement = this.Settlement; pi.Settlement = this.Settlement;
pi.IncomeAmount = this.InTrail; pi.IncomeAmount = this.InTrail;
pi.IncomeType = 'Trail'; pi.IncomeType = 'Trail';
pi.Balance = this.Balance;
pi.OffsetBalance = -1;
pi.Ts = this.Period; pi.Ts = this.Period;


return pi; return pi;
} }

public isMatch(pi: PayInModel): boolean {
return this.LoanNumber === pi.LoanNumber && this.LoanNumber !== '' &&
this.Lender === pi.Lender && this.Lender !== '' &&
this.InTrail === pi.IncomeAmount &&
this.Balance === pi.Balance &&
'Trail' === pi.IncomeType;
}
} }

+ 1
- 1
src/app/models/pay-in-list.filter.model.ts Просмотреть файл

} }


const bigNumber = 9999999999; const bigNumber = 9999999999;
this.Take = payload.Take || 10;
this.Take = payload.Take || 0;
this.Skip = payload.Skip || 0; this.Skip = payload.Skip || 0;
this.TsFrom = payload.TsFrom? new Date(payload.TsFrom) : new Date('1900-01-01'); this.TsFrom = payload.TsFrom? new Date(payload.TsFrom) : new Date('1900-01-01');
this.TsTo = payload.TsTo? new Date(payload.TsTo) : new Date('2038-01-01'); this.TsTo = payload.TsTo? new Date(payload.TsTo) : new Date('2038-01-01');

+ 14
- 0
src/app/models/pay-in.model.ts Просмотреть файл

this.UploadId = payload.UploadId; this.UploadId = payload.UploadId;
} }


public isEqual(pi: PayInModel): boolean {
return pi !== undefined && pi !== null &&
this.Amount === pi.Amount &&
this.Balance === pi.Balance &&
this.Lender === pi.Lender &&
this.LoanId === pi.LoanId &&
this.LoanNumber === pi.LoanNumber &&
this.OffsetBalance === pi.OffsetBalance &&
this.Settlement.getDate() === pi.Settlement.getDate() &&
this.IncomeAmount === pi.IncomeAmount &&
this.IncomeType === pi.IncomeType &&
this.Ts.getDate() === pi.Ts.getDate() &&
this.UploadId === pi.UploadId;
}
} }





+ 0
- 13
src/app/observable/pay.in.observable.ts Просмотреть файл

import {BehaviorSubject} from 'rxjs';
import {PayInModel} from '../models/pay-in.model';


export class PayInObservable extends BehaviorSubject<PayInModel[]> {
constructor() {
super(null);
}

Next(pis: PayInModel[]): void {
super.next(pis);
}
}

+ 4
- 2
src/app/pay-in/lender-aaa-income/lender-aaa-income.component.html Просмотреть файл

<kendo-grid-column field="Period" format='{0:yyyy MMMM}'> </kendo-grid-column> <kendo-grid-column field="Period" format='{0:yyyy MMMM}'> </kendo-grid-column>
<kendo-grid-column field="LoanNumber"> </kendo-grid-column> <kendo-grid-column field="LoanNumber"> </kendo-grid-column>
<kendo-grid-column field="Settlement" format='{0:MM/dd/yyyy h:mm a}'> </kendo-grid-column> <kendo-grid-column field="Settlement" format='{0:MM/dd/yyyy h:mm a}'> </kendo-grid-column>
<kendo-grid-column field="LoanAmount" format='{0:c}' > </kendo-grid-column>
<kendo-grid-column field="LoanFacility" format='{0:c}' > </kendo-grid-column>
<kendo-grid-column field="Balance" format='{0:c}' > </kendo-grid-column> <kendo-grid-column field="Balance" format='{0:c}' > </kendo-grid-column>
<kendo-grid-column field="InTrail" format='{0:c}'> </kendo-grid-column> <kendo-grid-column field="InTrail" format='{0:c}'> </kendo-grid-column>
<kendo-grid-column field="matchedPayIn" title="Matched" format='{0:c}'> <kendo-grid-column field="matchedPayIn" title="Matched" format='{0:c}'>
<ng-template kendoGridCellTemplate let-dataItem> <ng-template kendoGridCellTemplate let-dataItem>
<button kendoButton iconClass="fa fa-calendar fa-fw"></button>

<kendo-icon *ngIf="dataItem.matchedPayIn === -1" [name]="'close-circle'" [size]="'small'" [themeColor]="'warning'"></kendo-icon>
<kendo-icon *ngIf="dataItem.matchedPayIn !== -1" [name]="'tick'" [size]="'small'" [themeColor]="'success'"></kendo-icon>
{{ dataItem.matchedPayIn === -1? 'Not matched': dataItem.matchedPayIn.Id}} {{ dataItem.matchedPayIn === -1? 'Not matched': dataItem.matchedPayIn.Id}}
</ng-template> </ng-template>
</kendo-grid-column> </kendo-grid-column>

+ 9
- 8
src/app/pay-in/lender-aaa-income/lender-aaa-income.component.ts Просмотреть файл

this.analysis.AAA.forEach(v => { this.analysis.AAA.forEach(v => {
v.matchedPayIn = this.matchPayIn(v); v.matchedPayIn = this.matchPayIn(v);
this.data.push(new PayInAAARowModel(v)); this.data.push(new PayInAAARowModel(v));
v.InTrail = 20;
this.data.push(new PayInAAARowModel(v));
}); });
console.log(this);
} }


ngOnChanges(changes): void { ngOnChanges(changes): void {
if (changes.newPayInUpdate){ if (changes.newPayInUpdate){
const piNew = changes.newPayInUpdate.currentValue as PayInModel;
this.data.forEach(v => { this.data.forEach(v => {
// remove old matches
if (v.matchedPayIn !== -1 && v.matchedPayIn.Id === piNew.Id){
v.matchedPayIn = -1;
}

// add new matches
if ( this.isMatch(v, changes.newPayInUpdate.currentValue) ){ if ( this.isMatch(v, changes.newPayInUpdate.currentValue) ){
v.matchedPayIn = changes.newPayInUpdate.currentValue; v.matchedPayIn = changes.newPayInUpdate.currentValue;
} }
if ( v === null || pi === null ){ if ( v === null || pi === null ){
return false; return false;
} }
return v.LoanNumber === pi.LoanNumber &&
this.Lender === pi.Lender &&
v.InTrail === pi.IncomeAmount &&
'Trail' === pi.IncomeType;
return v.isMatch(pi);
} }



@Input() set payIn(pis: PayInModel[]) { @Input() set payIn(pis: PayInModel[]) {
this.payInCandidates = []; this.payInCandidates = [];
pis.forEach( v => this.payInCandidates.push(new PayInModel(v))); pis.forEach( v => this.payInCandidates.push(new PayInModel(v)));
v.matchedPayIn = this.matchPayIn(v); v.matchedPayIn = this.matchPayIn(v);
}); });


console.log ('new payin comes in');
} }


get PayIn(): PayInModel[] { get PayIn(): PayInModel[] {

+ 5
- 2
src/app/pay-in/pay-in.component.html Просмотреть файл

kendoGridSelectBy kendoGridSelectBy
[selectedKeys]="gridSelection" [selectedKeys]="gridSelection"


[rowClass]="rowClassCallbackBind"


(add)="addHandler($event)" (add)="addHandler($event)"
(cancel)="cancelHandler($event)" (cancel)="cancelHandler($event)"


(pageChange)="pageChange($event)" (pageChange)="pageChange($event)"
(sortChange)="sortChange($event)" (sortChange)="sortChange($event)"
[ngClass]="{ 'filterByUploadMeta': uploadMeta.Id > 0 }"
> >
<ng-template kendoGridToolbarTemplate> <ng-template kendoGridToolbarTemplate>
<div style="width:100%; margin:0px; display:block"> <div style="width:100%; margin:0px; display:block">


<kendo-grid-command-column *ngIf="allowEdit" title="command" width="100"> <kendo-grid-command-column *ngIf="allowEdit" title="command" width="100">
<ng-template kendoGridCellTemplate let-isNew="isNew" let-dataItem> <ng-template kendoGridCellTemplate let-isNew="isNew" let-dataItem>
<div class="duplicate">dup</div>
<button kendoGridEditCommand *ngIf="!dataItem.Uploads !== 0" icon="edit"></button> <button kendoGridEditCommand *ngIf="!dataItem.Uploads !== 0" icon="edit"></button>
<button kendoGridRemoveCommand *ngIf="!dataItem.Uploads !== 0" icon="delete"></button> <button kendoGridRemoveCommand *ngIf="!dataItem.Uploads !== 0" icon="delete"></button>
<button kendoGridSaveCommand [disabled]="incomeFormGroup?.invalid" icon="save"></button> <button kendoGridSaveCommand [disabled]="incomeFormGroup?.invalid" icon="save"></button>
</kendo-grid-column> </kendo-grid-column>


<kendo-grid-column field="Lender" title="Lender" width="150" > <kendo-grid-column field="Lender" title="Lender" width="150" >
<ng-template kendoGridEditTemplate let-dataItem>
<ng-template kendoGridEditTemplate let-dataItem let-formGroup>
<kendo-combobox <kendo-combobox
[formControl]="incomeFormGroup.get('Lender')"
[formControl]="formGroup.get('Lender')"
[data]="lenderListView | async" [data]="lenderListView | async"
[loading]="lenderNameService.loading"> [loading]="lenderNameService.loading">
</kendo-combobox> </kendo-combobox>

+ 14
- 0
src/app/pay-in/pay-in.component.scss Просмотреть файл

margin-right: 10px; margin-right: 10px;
} }
} }

div.duplicate {
background: darkgrey;
position: absolute;
top: 0px;
transform: rotate(
-45deg
);
display: none;
width: 50px;
left: -15px;
text-align: center;
color: white;
}
} }


.balance { .balance {

+ 46
- 6
src/app/pay-in/pay-in.component.ts Просмотреть файл

import {PayInListResult} from '../models/pay-in-list-result.model'; import {PayInListResult} from '../models/pay-in-list-result.model';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {PopupIncomeFilterComponent} from '../popup-income-filter/popup-income-filter.component'; import {PopupIncomeFilterComponent} from '../popup-income-filter/popup-income-filter.component';
import {GridComponent, PageChangeEvent, SortSettings} from '@progress/kendo-angular-grid';
import {GridComponent, PageChangeEvent, RowClassArgs, SortSettings} from '@progress/kendo-angular-grid';
import {SortDescriptor} from '@progress/kendo-data-query'; import {SortDescriptor} from '@progress/kendo-data-query';
import {UploadMetaModel} from '../models/uploadMetaModel'; import {UploadMetaModel} from '../models/uploadMetaModel';
import {debounce} from 'ts-debounce'; import {debounce} from 'ts-debounce';
public showOffsetBalance = true; public showOffsetBalance = true;


@Input() public pageable = true; @Input() public pageable = true;
@Input() public pageSize = 15;


public gridSelection: number[] = []; public gridSelection: number[] = [];
public sortable: SortSettings = { public sortable: SortSettings = {


public loading = false; public loading = false;
public lenderListView: Observable<string[]>; public lenderListView: Observable<string[]>;
public rowClassCallbackBind: any;
private debouncedLoadFilteredData = () => {}; private debouncedLoadFilteredData = () => {};





constructor(private pis: PayInService, constructor(private pis: PayInService,
private lss: LoanSingleService, private lss: LoanSingleService,
private router: Router, private router: Router,
private lenderNameService: LenderNameService) { private lenderNameService: LenderNameService) {
this.lenderListView = this.lenderNameService; this.lenderListView = this.lenderNameService;
this.lenderNameService.query(); this.lenderNameService.query();
if ( this.pageable ) {
this.filter.Take = this.pageSize;
}
this.rowClassCallbackBind = this.rowClassCallback.bind(this);
} }


ngOnInit(): void { ngOnInit(): void {
this.filter.UploadIds = [ value.Id.toString() ]; this.filter.UploadIds = [ value.Id.toString() ];
} }
this.debouncedLoadFilteredData(); this.debouncedLoadFilteredData();
//console.log('upload filter changed', value, this.filter);
// console.log('upload filter changed', value, this.filter);
} }


get uploadMeta(): UploadMetaModel { get uploadMeta(): UploadMetaModel {
this.showOffsetBalance = offsetBalance >= 0 ; this.showOffsetBalance = offsetBalance >= 0 ;
this.incomeFormGroup = this.createFormGroup(new PayInModelEx({})); this.incomeFormGroup = this.createFormGroup(new PayInModelEx({}));




// console.log(this.incomeFormGroup); // console.log(this.incomeFormGroup);
sender.addRow(this.incomeFormGroup); sender.addRow(this.incomeFormGroup);
} }



private createFormGroup(dataItem: PayInModelEx): FormGroup { private createFormGroup(dataItem: PayInModelEx): FormGroup {
const Loan = this.filterLoan.Id !== '' ? this.filterLoan : dataItem.Loan; const Loan = this.filterLoan.Id !== '' ? this.filterLoan : dataItem.Loan;
const UploadMeta = this.filterUploadMeta.Id !== 0 ? this.filterUploadMeta : dataItem.UploadMeta ; const UploadMeta = this.filterUploadMeta.Id !== 0 ? this.filterUploadMeta : dataItem.UploadMeta ;
} }


public cancelHandler({ sender, rowIndex }): void { public cancelHandler({ sender, rowIndex }): void {
// console.log(sender);
this.closeEditor(sender, rowIndex); this.closeEditor(sender, rowIndex);
} }


this.Loan.cuPayIn( model); this.Loan.cuPayIn( model);
this.updatePiInGrid(model); this.updatePiInGrid(model);
this.Updated.emit(model); this.Updated.emit(model);
console.log(this);
}, },
err => { err => {
// TODO: this.errorOccurred.emit('Error saving Income'); // TODO: this.errorOccurred.emit('Error saving Income');
} }


private closeEditor(grid, rowIndex = this.editedRowIndex): void{ private closeEditor(grid, rowIndex = this.editedRowIndex): void{
grid.closeRow(rowIndex);
if ( rowIndex !== undefined ){
grid.closeRow(rowIndex);
}
this.editedRowIndex = undefined; this.editedRowIndex = undefined;
this.incomeFormGroup = undefined; this.incomeFormGroup = undefined;
} }
} }


public onLoanChange(loan: LoanModel): void { public onLoanChange(loan: LoanModel): void {
if ( loan.Id !== '' ) {
if ( loan !== undefined && loan.Id !== '' ) {
this.incomeFormGroup.get('Lender').setValue(loan.Lender); this.incomeFormGroup.get('Lender').setValue(loan.Lender);
this.incomeFormGroup.get('Lender').disable({onlySelf: true, emitEvent: false}); this.incomeFormGroup.get('Lender').disable({onlySelf: true, emitEvent: false});


this.incomeFormGroup.get('LoanNumber').setValue(loan.LenderLoanNumber); this.incomeFormGroup.get('LoanNumber').setValue(loan.LenderLoanNumber);
this.incomeFormGroup.get('LoanNumber').disable({onlySelf: true, emitEvent: false}); this.incomeFormGroup.get('LoanNumber').disable({onlySelf: true, emitEvent: false});
}else{
this.incomeFormGroup.get('Lender').enable({onlySelf: true, emitEvent: false});
this.incomeFormGroup.get('LoanNumber').enable({onlySelf: true, emitEvent: false});
} }
} }


} }


public selectOrAddPayIn(pi: PayInModel): void{ public selectOrAddPayIn(pi: PayInModel): void{

const row = this.gridData.data.findIndex( v => { const row = this.gridData.data.findIndex( v => {
return v.LoanNumber === pi.LoanNumber && return v.LoanNumber === pi.LoanNumber &&
v.Lender === pi.Lender && v.Lender === pi.Lender &&
if ( row >= 0 ) { // we found it if ( row >= 0 ) { // we found it
this.ScrollTo(row); this.ScrollTo(row);
} else{ } else{
this.gridSelection = []; // select none
this.incomeFormGroup = this.createFormGroup(new PayInModelEx(pi)); this.incomeFormGroup = this.createFormGroup(new PayInModelEx(pi));
// this.closeEditor(this.grid);
this.grid.addRow(this.incomeFormGroup); this.grid.addRow(this.incomeFormGroup);
} }
} }

public rowClassCallback( context: RowClassArgs): any {
const pi = context.dataItem as PayInModel;
const rowIndex = context.index;

const duplicate = this.checkDup(pi, rowIndex);

return {
dup: duplicate,
missingLoan: pi.LoanId === ''
};
}
private checkDup(pi: PayInModel, rowIndex: number): boolean {
let found = false;
if (this.uploadMeta.Id <= 0 ) {
return false; // dup check is only available for upload filtered situation.
}
this.gridData.data.forEach( (v, index) => {
if ( v.isEqual(pi) && index !== rowIndex ){
found = true;
}
});
return found;
}
} }

Загрузка…
Отмена
Сохранить