Bladeren bron

loan select incorporated

tags/2.037
Patrick Sun 4 jaren geleden
bovenliggende
commit
9e8e8ef36a
24 gewijzigde bestanden met toevoegingen van 732 en 214 verwijderingen
  1. +40
    -36
      src/app/app.module.ts
  2. +6
    -6
      src/app/chart-past-year-monthly-performance/chart-past-year-monthly-performance.component.spec.ts
  3. +28
    -4
      src/app/list-all-loans/list-all-loans.component.html
  4. +27
    -1
      src/app/list-all-loans/list-all-loans.component.scss
  5. +138
    -10
      src/app/list-all-loans/list-all-loans.component.ts
  6. +7
    -35
      src/app/list-income/list-income.component.html
  7. +1
    -4
      src/app/list-income/list-income.component.scss
  8. +43
    -6
      src/app/list-income/list-income.component.ts
  9. +2
    -1
      src/app/loan-card/loan-card.component.html
  10. +10
    -1
      src/app/loan-card/loan-card.component.scss
  11. +18
    -5
      src/app/loan-card/loan-card.component.ts
  12. +1
    -1
      src/app/loan-edit/trail-income/trail-income.component.ts
  13. +19
    -0
      src/app/loan-select/loan-select.component.html
  14. +25
    -0
      src/app/loan-select/loan-select.component.scss
  15. +25
    -0
      src/app/loan-select/loan-select.component.spec.ts
  16. +74
    -0
      src/app/loan-select/loan-select.component.ts
  17. +4
    -3
      src/app/main-menu-items.ts
  18. +13
    -0
      src/app/models/pay-in-ex.model.ts
  19. +18
    -3
      src/app/models/pay-in.model.ts
  20. +27
    -15
      src/app/pay-in/pay-in.component.html
  21. +16
    -14
      src/app/pay-in/pay-in.component.scss
  22. +139
    -53
      src/app/pay-in/pay-in.component.ts
  23. +44
    -16
      src/app/upload-detail/upload-detail.component.html
  24. +7
    -0
      src/app/upload-detail/upload-detail.component.scss

+ 40
- 36
src/app/app.module.ts Bestand weergeven

@@ -97,6 +97,8 @@ import { PopupIncomeFilterComponent } from './popup-income-filter/popup-income-f
import { LoanCardComponent } from './loan-card/loan-card.component';
import {AppConfig} from './app.config';
import { UploadingProgressCardComponent } from './uploading-progress-card/uploading-progress-card.component';
import { LoanSelectComponent } from './loan-select/loan-select.component';
import {PopupModule} from '@progress/kendo-angular-popup';



@@ -164,43 +166,45 @@ export function initializeApp(appConfig: AppConfig): () => Promise<void> {
ImagePopupDialogComponent,
PopupIncomeFilterComponent,
LoanCardComponent,
UploadingProgressCardComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
CommonModule,
HttpClientModule,
ReactiveFormsModule,
AppRoutingModule,
MenuModule,
ContextMenuModule,
BrowserAnimationsModule,
DialogsModule,
ButtonsModule,
FloatingActionButtonModule,
GridModule,
PDFModule,
ExcelModule,
InputsModule,
IconsModule,
FontAwesomeModule,
NavigationModule,
LayoutModule,
IndicatorsModule,
LabelModule,
NotificationModule,
ChartsModule,
DateInputsModule,
DropDownsModule,
ExcelExportModule,
EditorModule,
UploadModule,
FileSelectModule,
ProgressBarModule,
PagerModule
UploadingProgressCardComponent,
LoanSelectComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
CommonModule,
HttpClientModule,
ReactiveFormsModule,
AppRoutingModule,
MenuModule,
ContextMenuModule,
BrowserAnimationsModule,
DialogsModule,
ButtonsModule,
FloatingActionButtonModule,
GridModule,
PDFModule,
ExcelModule,
InputsModule,
IconsModule,
FontAwesomeModule,
NavigationModule,
LayoutModule,
IndicatorsModule,
LabelModule,
NotificationModule,
ChartsModule,
DateInputsModule,
DropDownsModule,
ExcelExportModule,
EditorModule,
UploadModule,
FileSelectModule,
ProgressBarModule,
PagerModule,
PopupModule
],
providers: [
MenuService,
AuthGuard,

+ 6
- 6
src/app/chart-past-year-monthly-performance/chart-past-year-monthly-performance.component.spec.ts Bestand weergeven

@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { ChartPastYearMontnlyPerformanceComponent } from './chart-past-year-monthly-performance.component';
import { ChartPastYearMonthlyPerformanceComponent } from './chart-past-year-monthly-performance.component';

describe('ChartPastYearMontnlyPerformanceComponent', () => {
let component: ChartPastYearMontnlyPerformanceComponent;
let fixture: ComponentFixture<ChartPastYearMontnlyPerformanceComponent>;
describe('ChartPastYearMonthlyPerformanceComponent', () => {
let component: ChartPastYearMonthlyPerformanceComponent;
let fixture: ComponentFixture<ChartPastYearMonthlyPerformanceComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ChartPastYearMontnlyPerformanceComponent ]
declarations: [ ChartPastYearMonthlyPerformanceComponent ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(ChartPastYearMontnlyPerformanceComponent);
fixture = TestBed.createComponent(ChartPastYearMonthlyPerformanceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

+ 28
- 4
src/app/list-all-loans/list-all-loans.component.html Bestand weergeven

@@ -10,21 +10,45 @@
[height]="1000"
[navigable]="true"
[filterable]="true"
[selectable]="selectableSettings"
kendoGridSelectBy="Id"
[selectedKeys]="gridSelection"
(selectionChange)="onSelectionChange($event)"
(dataStateChange)="dataStateChange($event)"
(filterChange)="filterChange($event)"
(cellClick)="onCellClick($event)"
class="fullheight_grid"
>
<ng-template kendoGridToolbarTemplate>
<button kendoGridExcelCommand type="button" icon="file-excel" style="float:right;">Export to Excel</button>
<button kendoGridPDFCommand icon="file-pdf" style="float:right;">Export to PDF</button>
<ng-template *ngIf="EnableExportExcel || EnableExportPdf || EnableSelectButton" kendoGridToolbarTemplate>
<button *ngIf="EnableExportExcel" kendoGridExcelCommand type="button" icon="file-excel" style="float:right;">Export to Excel</button>
<button *ngIf="EnableExportPdf" kendoGridPDFCommand icon="file-pdf" style="float:right;">Export to PDF</button>
<div class="selected-loans" #notification>
<app-loan-card *ngFor="let l of SelectedLoans" [Loan]="l" [ShowCloseButton]="true"
(Closed)="RemoveFromSelection(l)"
@fadeIn @slideOutUp
></app-loan-card>
</div>
<div class="selection-percentage" *ngIf="SelectedLoans.length >0">
<span class="badge badge-info">{{SelectedLoans.length}}</span>/
<span class="badge badge-info">{{MaxSelect}}</span>
</div>
<button *ngIf="EnableSelectButton" kendoButton [primary]=true
class="select-loan" icon="close-circle" style="float:right;"
(click)="onFinishSelection()"
>
Finish Selection</button>

</ng-template>

<kendo-grid-column field="Id" width="50" [class]="'topAlign'" [sortable]="false" [filterable]="false">
<kendo-grid-checkbox-column *ngIf="EnableSelectButton" width="50" ></kendo-grid-checkbox-column>


<kendo-grid-column field="Id" title="#" width="50" [class]="'topAlign'" [sortable]="false" [filterable]="false">
<ng-template kendoGridCellTemplate let-dataItem let-rowIndex="rowIndex" >
<p title="{{dataItem.Id}}" > {{dataItem.Index}}</p>
</ng-template>
</kendo-grid-column>

<kendo-grid-column-group title="People" [columnMenu]="false" [headerClass]="'colGroupPeople'">
<kendo-grid-column field="Client" width="220" title="Client(s)" [class]="'topAlign'" [headerClass]="'colClient'">
<ng-template kendoGridCellTemplate let-dataItem>

+ 27
- 1
src/app/list-all-loans/list-all-loans.component.scss Bestand weergeven

@@ -1,5 +1,5 @@
.fullheight_grid {
height: calc(100vh - 48px) !important;
height: 100% !important;
}

.k-grid td:first-child{
@@ -14,6 +14,32 @@
text-align: left;
}


div.selected-loans{
position: relative;
padding-right:200px;
width: calc(100% - 150px);
min-height: 60px;
overflow: auto hidden;
app-loan-card{
display: block;
}
}

div.selection-percentage{
float:right;
position:absolute;
right: 10px;
bottom: 1px;
}


button.select-loan{
position: absolute;
right: 10px;
top: 10px;
}

.customer-photo{
display: inline-block;
width: 32px;

+ 138
- 10
src/app/list-all-loans/list-all-loans.component.ts Bestand weergeven

@@ -1,16 +1,41 @@
import {Component, OnInit, ViewChild, ViewEncapsulation} from '@angular/core';
import {CellCloseEvent, DataStateChangeEvent, GridComponent, GridDataResult} from '@progress/kendo-angular-grid';
import {Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {
CellCloseEvent,
DataStateChangeEvent,
GridComponent,
GridDataResult,
SelectableSettings,
SelectionEvent
} from '@progress/kendo-angular-grid';
import {CompositeFilterDescriptor, SortDescriptor, toODataString} from '@progress/kendo-data-query';
import {LoanSummaryService} from '../service/loan_summary.service';
import {AuthService} from '../service/auth.service';
import {Observable} from 'rxjs';
import {Router} from '@angular/router';
import {LoanModel} from '../models/loan.model';
import {LoanSelectComponent} from '../loan-select/loan-select.component';
import {NotificationService, Type} from '@progress/kendo-angular-notification';
import {animate, style, transition, trigger} from '@angular/animations';

@Component({
selector: 'app-list-all-loans',
templateUrl: './list-all-loans.component.html',
styleUrls: ['./list-all-loans.component.scss'],
encapsulation: ViewEncapsulation.None,
animations: [
trigger('fadeIn', [
transition(':enter', [
style({ opacity: '0' }),
animate('.5s ease-out', style({ opacity: '1'})),
]),
]),
trigger('slideOutUp', [
transition(':leave', [
style({ opacity: '1' }),
animate('.5s ease-out', style({ opacity: '0' })),
]),
]),
],
})
export class ListAllLoansComponent implements OnInit {
public view: LoanSummaryService;
@@ -18,13 +43,25 @@ export class ListAllLoansComponent implements OnInit {
public filter: CompositeFilterDescriptor;
public pageSize = 10;
public skip = 0;



@ViewChild(GridComponent, { static: true }) public grid: GridComponent;


constructor(private service: LoanSummaryService, private auth: AuthService, private router: Router) { }
@Output() LoanSelected: EventEmitter<LoanModel[]|LoanModel> = new EventEmitter<LoanModel[]|LoanModel>();

@Input() EnableExportExcel = false;
@Input() EnableExportPdf = false;
@Input() EnableSelectButton = false;
@Input() MaxSelect = 1;
@Input() Preselect: LoanModel[] = [];
public SelectedLoans: LoanModel[] = [];
public selectableSettings: SelectableSettings|boolean;
private privateSingleSelection = true;
public gridSelection: string[] = [];
@ViewChild(GridComponent, { static: true }) public grid: GridComponent;
@ViewChild(GridComponent, { read: ViewContainerRef }) public gridVR: ViewContainerRef;


constructor(private service: LoanSummaryService,
private auth: AuthService,
private router: Router,
private notificationService: NotificationService) { }

public ngOnInit(): void {
// Bind directly to the service as it is a Subject
@@ -32,6 +69,39 @@ export class ListAllLoansComponent implements OnInit {

// Fetch the data with the initial state
this.loadData();

// selectable
this.setSelectableSettings();

// Preselect on Init only
this.Preselect.forEach( v => {
if ( v !== undefined && v !== null && v.Id !== '' ){
this.AddLoanToSelection(v);
this.gridSelection.unshift(v.Id);
}
});
}

@Input() set SingleSelection(single: boolean) {
this.privateSingleSelection = single;
this.setSelectableSettings();
}

get SingleSelection(): boolean{
return this.privateSingleSelection;
}

public setSelectableSettings(): void {
this.privateSingleSelection = this.MaxSelect === 1;
if ( this.EnableSelectButton ){
this.selectableSettings = {
checkboxOnly: true,
mode: this.privateSingleSelection ? 'single' : 'multiple',
drag: false,
};
}else{
this.selectableSettings = false;
}
}

public dataStateChange({ skip, take, sort, filter }: DataStateChangeEvent): void {
@@ -70,8 +140,66 @@ export class ListAllLoansComponent implements OnInit {
}

public onCellClick(event: CellCloseEvent): void {
this.router.navigate(['/edit-loan/' + event.dataItem.Id]);
// this.LoanSelected.emit(event.dataItem);
}

public AddLoanToSelection(loan: LoanModel): void{
const idx = this.SelectedLoans.findIndex( v => {
return v.Id === loan.Id;
});
if ( idx === -1 ) { // not found
this.SelectedLoans.unshift(loan);
}
}

public onFinishSelection(): void{
if ( this.MaxSelect === 1 ){
this.LoanSelected.emit(this.SelectedLoans[0]);
}else{
this.LoanSelected.emit(this.SelectedLoans);
}

console.log('loan selected', this);
this.notifyUser('ended');
}

public RemoveFromSelection(l: LoanModel): void{
this.SelectedLoans = this.SelectedLoans.filter( v => l.Id !== v.Id );
this.gridSelection = this.gridSelection.filter( v => v !== l.Id );
}

public onSelectionChange(sel: SelectionEvent): void {
// to avoid race condition, we do action after 200ms
setTimeout( () => {
sel.deselectedRows.forEach(v => {
this.RemoveFromSelection(v.dataItem);
});

sel.selectedRows.forEach(v => {
if ( this.SelectedLoans.length < this.MaxSelect) {
this.AddLoanToSelection(v.dataItem);
}else{
// remove those that was added by grid selection
this.gridSelection = this.gridSelection.filter( existing => existing !== v.dataItem.Id );
this.notifyUser('Maximum selection reached', { style: 'warning', icon: true });
}
});
}, 200);
}

public notifyUser( msg: string, lookAndFeel?: Type): void {
if (lookAndFeel === undefined ){
lookAndFeel = { style: 'success', icon: true };
}
this.notificationService.show({
appendTo: this.gridVR,
content: msg,
cssClass: 'button-notification',
animation: { type: 'slide', duration: 400 },
position: { horizontal: 'right', vertical: 'top' },
type: lookAndFeel,
closable: false,
hideAfter: 5000,
});
}
}

+ 7
- 35
src/app/list-income/list-income.component.html Bestand weergeven

@@ -1,37 +1,9 @@
<div class="income-container">
<kendo-splitter orientation="vertical" style="height: 100%;">

<kendo-splitter-pane [collapsible]="false">
<div class="pane-content">
<h3>Outer splitter / Middle pane</h3>
<p>Resizable only.</p>
</div>
</kendo-splitter-pane>


<kendo-splitter-pane [collapsible]="true" size="50%">
<kendo-splitter class="full-height">

<kendo-splitter-pane size="300px">
<div class="pane-content">
<app-lender-uploads (uploadSuccessful)="onSuccess($event)"></app-lender-uploads>
</div>
</kendo-splitter-pane>

<kendo-splitter-pane >
<div class="pane-content">
<div *ngFor="let u of uploads" class="aaaa">
Type: {{u.files[0].extension}}
Name: {{u.files[0].name}}
Funder: Name: {{u.response.body.Funder}}
</div>
</div>
</kendo-splitter-pane>
</kendo-splitter>




</kendo-splitter-pane>
</kendo-splitter>
<app-pay-in [uploadMeta]="uploadMeta"
[Loan]="Loan"
[showLoanColumn]="true"
[showUploadColumn]="true"
[LoadDataNow]="startLoad"
>
</app-pay-in>
</div>

+ 1
- 4
src/app/list-income/list-income.component.scss Bestand weergeven

@@ -1,7 +1,4 @@
div.income-container {
height: calc(100vh - 48px);
}

div.pane-content{
height:100%;
overflow:hidden;
}

+ 43
- 6
src/app/list-income/list-income.component.ts Bestand weergeven

@@ -1,5 +1,8 @@
import { Component, OnInit } from '@angular/core';
import {SuccessEvent} from '@progress/kendo-angular-upload';
import {UploadMetaModel} from '../models/uploadMetaModel';
import {UploadAttachService} from '../service/upload.attach.service';
import {LoanModel} from '../models/loan.model';
import {LoanSingleService} from '../service/loan.single.service';

@Component({
selector: 'app-list-income',
@@ -7,15 +10,49 @@ import {SuccessEvent} from '@progress/kendo-angular-upload';
styleUrls: ['./list-income.component.scss']
})
export class ListIncomeComponent implements OnInit {
public uploads: SuccessEvent[] = [];
public uploadMeta: UploadMetaModel = new UploadMetaModel({});
public Loan: LoanModel = new LoanModel({});
public startLoad = false

constructor() { }
private UploadMetaFilterIsReady = false;
private LoanFilterIsReady = false;
constructor(private uas: UploadAttachService, private lss: LoanSingleService) { }

ngOnInit(): void {
this.UploadMetaFilterIsReady = true;

// this.UploadMetaFilterIsReady = false;
// this.uas.getUploadMeta(1).subscribe(
// resp => {
// this.UploadMetaFilterIsReady = true;
// this.checkStartUpload();
// // this.uploadMeta = new UploadMetaModel(resp);
// }, error => {
// this.UploadMetaFilterIsReady = true;
// this.checkStartUpload();
// }, () => {
// this.UploadMetaFilterIsReady = true;
// this.checkStartUpload();
// }
// );

// this.lss.getLoan('0482b524-d396-4b03-8abb-f8325c87e2ed').subscribe(
// resp => {
// this.Loan = new LoanModel(resp);
// this.LoanFilterIsReady = true;
// this.checkStartUpload();
// }, err => {
// this.LoanFilterIsReady = true;
// this.checkStartUpload();
// }, () => {
// this.LoanFilterIsReady = true;
// this.checkStartUpload();
// }
// );
this.startLoad = true;
}

public onSuccess(e: SuccessEvent): void {
this.uploads.push (e);
console.log(e);
private checkStartUpload(): void {
this.startLoad = this.UploadMetaFilterIsReady
}
}

+ 2
- 1
src/app/loan-card/loan-card.component.html Bestand weergeven

@@ -1,6 +1,7 @@
<div class="card loan" (click)="gotoLoan(LoanId)">
<div class="card loan" (click)="onClick()">
<span class="badge badge-success">{{Loan.Item}}</span>
<span> Amount: {{Loan.Amount | currency }}</span>
<span> Settlement: {{Loan.Settlement | date:'yyyy-mm-dd' }}</span>
<span *ngIf="ShowCloseButton" class="k-icon k-i-close-circle close" (click)="onClose()" size="large"></span>
</div>


+ 10
- 1
src/app/loan-card/loan-card.component.scss Bestand weergeven

@@ -11,7 +11,16 @@ div.loan.card {
-o-transition: all 0.4s cubic-bezier(0.645, 0.045, 0.355, 1);
transition: all 0.4s cubic-bezier(0.645, 0.045, 0.355, 1);
border-radius: 10px;
overflow: hidden;
overflow: unset;
-webkit-box-shadow: 15px 15px 27px #e1e1e3, -15px -15px 27px #ffffff;
box-shadow: 15px 15px 27px #e1e1e3, -15px -15px 27px #ffffff;
margin-top:10px;
margin-right: 10px;

span.k-icon.close{
position:absolute;
top: -10px;
right: -10px;
border-radius: 100%;
}
}

+ 18
- 5
src/app/loan-card/loan-card.component.ts Bestand weergeven

@@ -1,4 +1,4 @@
import {Component, Input, OnInit} from '@angular/core';
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {LoanModel} from '../models/loan.model';
import {LoanSingleService} from '../service/loan.single.service';
import {Router} from '@angular/router';
@@ -10,13 +10,26 @@ import {Router} from '@angular/router';
})
export class LoanCardComponent implements OnInit {
@Input() LoanId = '';
public Loan: LoanModel = new LoanModel({});
@Input() ShowCloseButton = false;
@Input() public Loan: LoanModel = new LoanModel({});
@Output() Selected: EventEmitter<LoanModel> = new EventEmitter<LoanModel>();
@Output() Closed: EventEmitter<LoanModel> = new EventEmitter<LoanModel>();
constructor(private lss: LoanSingleService, private router: Router) { }

ngOnInit(): void {
this.lss.getLoan(this.LoanId).subscribe( resp => {
this.Loan.Response = resp;
});
if ( this.LoanId !== undefined && this.LoanId !== '' && this.Loan.Id === ''){
this.lss.getLoan(this.LoanId).subscribe( resp => {
this.Loan.Response = resp;
});
}
}

public onClick(): void {
this.Selected.emit(this.Loan);
}

public onClose(): void{
this.Closed.emit(this.Loan);
}

public gotoLoan(id: string): void {

+ 1
- 1
src/app/loan-edit/trail-income/trail-income.component.ts Bestand weergeven

@@ -107,7 +107,7 @@ export class TrailIncomeComponent implements OnInit {
pi.LoanNumber = this.Loan.LenderLoanNumber;
pi.OffsetBalance = v.OffsetBalance;
pi.Settlement = this.Loan.Settlement;
pi.Trail = v.Trail;
pi.IncomeAmount = v.Trail; //TODO: rename Trail to incomeAmount and add IncomeType
pi.Ts = new Date(v.Ts);
pi.UploadId = v.UploadId;


+ 19
- 0
src/app/loan-select/loan-select.component.html Bestand weergeven

@@ -0,0 +1,19 @@
<div #anchor class="anchor wrapper">
<button kendoButton *ngIf="!readOnly" (click)="onToggle()" [icon]="'edit'" ></button>
<app-loan-card *ngIf="Loan!==undefined && Loan !== null && Loan.Id !=='' "
[Loan]="Loan" [ShowCloseButton]="false"></app-loan-card>
</div>

<kendo-popup [anchor]="anchor" [offset]="Offset" (anchorViewportLeave)="showPopup = false" *ngIf="showPopup">
<div class='popup-content'>
<app-list-all-loans #list
(LoanSelected)="onLoanSelected($event)"
[EnableSelectButton]="true"
[SingleSelection]="true"
[MaxSelect]="Max"
[Preselect]="[Loan]"
>
</app-list-all-loans>
</div>
</kendo-popup>


+ 25
- 0
src/app/loan-select/loan-select.component.scss Bestand weergeven

@@ -0,0 +1,25 @@
.popup-content {
box-shadow: 1px 1px 10px black;
padding: 10px;
width: 90vw;
height: 50vw;
color: #787878;
background-color: #fcf7f8;
border: 1px solid rgba(0,0,0,.05);
border-radius:20px;
}

div.anchor.wrapper {
width:100%;
position: relative;

button{
position: absolute;
/* left: 0px; */
right: 0px;
top: -10px;
color: red;
border-radius: 100%;
z-index: 1;
}
}

+ 25
- 0
src/app/loan-select/loan-select.component.spec.ts Bestand weergeven

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { LoanSelectComponent } from './loan-select.component';

describe('LoanSelectComponent', () => {
let component: LoanSelectComponent;
let fixture: ComponentFixture<LoanSelectComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LoanSelectComponent ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(LoanSelectComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});

+ 74
- 0
src/app/loan-select/loan-select.component.ts Bestand weergeven

@@ -0,0 +1,74 @@
import { Component, Input, OnInit, ViewChild} from '@angular/core';
import {LoanModel} from '../models/loan.model';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {ListAllLoansComponent} from '../list-all-loans/list-all-loans.component';

@Component({
selector: 'app-loan-select',
templateUrl: './loan-select.component.html',
styleUrls: ['./loan-select.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: LoanSelectComponent
}
]
})
export class LoanSelectComponent implements OnInit, ControlValueAccessor {
@ViewChild('list', {static: false} ) list: ListAllLoansComponent;
@Input() Max = 1; // we do not support multiple loan selection at the moment
@Input() readOnly = false;
public Loan: LoanModel; // for multiple selection ,it has to be array; we don't support it now.

// popup
public showPopup = false;
public Offset = { left: 0, top: 0 };

// formControl state
private disabled = false;
private touched = false;

// Control Value Accessor
onChange = (Loan) => {}; // empty
onTouched = () => {};

constructor() { }

ngOnInit(): void {
console.log('init loan-select', this);
}

writeValue(loan: LoanModel): void {
this.Loan = loan;
}

registerOnChange(onChange: any): void {
this.onChange = onChange;
}

registerOnTouched(onTouched: any): void {
this.onTouched = onTouched;
}

private markAsTouched(): void {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}

onLoanSelected(loan: LoanModel): void {
this.Loan = loan;
this.markAsTouched();
this.onTouched();
this.onChange(loan);
this.showPopup = false;
}


public onToggle(): void {
this.showPopup = !this.showPopup;
}

}

+ 4
- 3
src/app/main-menu-items.ts Bestand weergeven

@@ -20,11 +20,12 @@ export const mainMenuItems: any[] = [
{ text: 'Start New Loan', icon: 'plus', url: './#edit-loan/' },
{ text: 'List All', icon: 'table' , url: './#list-all-loans' },
{ text: '--', separator: 'true' },
{ text: 'Income', icon: 'dollar', url: './#pay-in' },
{ text: 'list income', icon: 'dollar', url: './#list-income' },
{ text: '--', separator: 'true' },
{ text: 'Uploads', icon: 'dollar', url: './#lender-uploads' },
{ text: 'Uploads by Id', icon: 'dollar', url: './#upload-details/30' },
{ text: 'list income', icon: 'dollar', url: './#list-income' },
{ text: '--', separator: 'true' },
{ text: 'Test Uploads by Id', icon: 'dollar', url: './#upload-details/1' },
{ text: 'Test Pay-in', icon: 'dollar', url: './#pay-in' },
]
},
{

+ 13
- 0
src/app/models/pay-in-ex.model.ts Bestand weergeven

@@ -0,0 +1,13 @@
import {LoanModel} from './loan.model';
import {UploadMetaModel} from './uploadMetaModel';
import {PayInModel} from './pay-in.model';

export class PayInModelEx extends PayInModel{
public Loan: LoanModel;
public UploadMeta: UploadMetaModel;
constructor(payload: Partial<PayInModelEx>){
super(payload);
this.Loan = new LoanModel( payload.Loan || {});
this.UploadMeta = new UploadMetaModel(payload.UploadMeta || {});
}
}

+ 18
- 3
src/app/models/pay-in.model.ts Bestand weergeven

@@ -1,3 +1,5 @@
import {LoanModel} from './loan.model';
import {UploadMetaModel} from './uploadMetaModel';

export class PayInModel {
public Id: number;
@@ -8,7 +10,8 @@ export class PayInModel {
public LoanNumber: string;
public OffsetBalance: number;
public Settlement: Date;
public Trail: number;
public IncomeAmount: number;
public IncomeType: string;
public Ts: Date;
public UploadId: number;
constructor(payload: Partial<PayInModel>){
@@ -22,9 +25,21 @@ export class PayInModel {
this.LoanId = payload.LoanId || '' ;
this.LoanNumber = payload.LoanNumber || '';
this.OffsetBalance = payload.OffsetBalance || 0;
if (payload.Settlement === undefined || payload.Settlement === null) {
this.Settlement = new Date();
}else{
this.Settlement = new Date(payload.Settlement);
}
this.Settlement = new Date(payload.Settlement);
this.Trail = payload.Trail || 0 ;
this.Ts = new Date(payload.Ts);
this.IncomeAmount = payload.IncomeAmount || 0 ;
this.IncomeType = payload.IncomeType || '';
if (payload.Ts === undefined || payload.Ts === null) {
this.Ts = new Date();
}else{
this.Ts = new Date(payload.Ts);
}
this.UploadId = payload.UploadId;
}
}



+ 27
- 15
src/app/pay-in/pay-in.component.html Bestand weergeven

@@ -3,7 +3,7 @@
[pageSize]="filter.Take"
[skip]="filter.Skip"
[sortable]="sortable"
[filterable]="filterable"
[filterable]="false"
[loading]="loading"

[sort]="filter.Sort"
@@ -21,10 +21,12 @@
<div style="width:100%; margin:0px; display:block">
<div *ngIf="allowAddNew" class="add-new-tool-bar">
<button kendoGridAddCommand icon="plus" >Add new Income</button>

</div>
<div [ngStyle]="{'width': allowAddNew? '70%': '100%'}" class="filter-panel-wrapper" >
<button kendoButton icon="filter" (click)="showFilter()" >Filter</button>
<!-- <button kendoButton icon="filter" >Upload</button>-->
<span *ngIf="uploadMeta.Id > 0 " class="badge badge-pill badge-primary specific-upload"> {{uploadMeta.Id}} </span>
<span *ngIf="uploadMeta.Id > 0 " class="badge badge-secondary specific-upload"> {{uploadMeta.FileName}} </span>
<button kendoButton icon="filter" (click)="showFilter()" >Filter</button>
</div>
</div>
</ng-template>
@@ -46,7 +48,13 @@
</ng-template>
</kendo-grid-column>

<kendo-grid-column field="Trail" title="Trail Received" width="150" format="{0:c}" editor="numeric">
<kendo-grid-column field="Lender" title="Lender" width="150" editor="string">
</kendo-grid-column>

<kendo-grid-column field="IncomeAmount" title="Income" width="150" format="{0:c}" editor="numeric">
</kendo-grid-column>

<kendo-grid-column field="IncomeType" title="Type" width="150" editor="string">
</kendo-grid-column>

<kendo-grid-column field="Ts" title="Trail Date" editor="date" width="100">
@@ -55,10 +63,6 @@
</ng-template>
</kendo-grid-column>

<kendo-grid-column field="Amount" title="Loan Amount" width="150" format="{0:c}"
[editable]="false" [sortable]="false">
</kendo-grid-column>

<kendo-grid-column field="Balance" title="Balance" width="150">
<ng-template kendoGridCellTemplate let-dataItem>
<div *ngIf="dataItem.Balance >=0 "> {{ dataItem.Balance | currency}} </div>
@@ -77,7 +81,7 @@
>
</kendo-switch>

<kendo-numerictextbox *ngIf="showBalance" name="balance"
<kendo-numerictextbox *ngIf="showBalance"
[formControl]="incomeFormGroup.get('Balance')"
[min]="-1" [max]="999999999" [autoCorrect]="true" class="balance">
</kendo-numerictextbox>
@@ -103,26 +107,34 @@
>
</kendo-switch>

<kendo-numerictextbox *ngIf="showOffsetBalance" name="offsetBalance"
<kendo-numerictextbox *ngIf="showOffsetBalance"
[formControl]="incomeFormGroup.get('OffsetBalance')"
[min]="-1" [max]="999999999" [autoCorrect]="true" class="balance">
</kendo-numerictextbox>
</ng-template>
</kendo-grid-column>

<kendo-grid-column field="UploadId" title="Uploads" width="100" format="{0:c}" [editable]="false" [sortable]="false">
<kendo-grid-column *ngIf="showUploadColumn"
field="UploadId" title="Uploads" width="100" format="{0:c}" [editable]="false" [sortable]="false">
<ng-template kendoGridCellTemplate let-dataItem >
<button kendoButton *ngIf="dataItem.UploadId > 0"
(click)="showUpload(dataItem.UploadId)" icon="attachment"> {{ dataItem.UploadId }}
(click)="showUpload(dataItem.UploadId)" icon="attachment"> {{ dataItem.UploadId }}
</button>
<p *ngIf="dataItem.UploadId <=0" > - </p>
</ng-template>
</kendo-grid-column>

<kendo-grid-column field="LoanId" title="Loan" width="210" [editable]="false" [sortable]="false">
<kendo-grid-column *ngIf="showLoanColumn"
field="LoanId" title="Loan" width="210"
[editable]="true "
[sortable]="false">
<ng-template kendoGridCellTemplate let-dataItem>

<app-loan-card [LoanId]="dataItem.LoanId"></app-loan-card>
<app-loan-card *ngIf="dataItem.LoanId !== '' " [LoanId]="dataItem.LoanId"></app-loan-card>
</ng-template>
<ng-template kendoGridEditTemplate let-dataItem>
<app-loan-select [formControl]="incomeFormGroup.get('Loan')"
[readOnly]="this.filterLoan !== undefined && this.filterLoan.Id !==''"
></app-loan-select>
</ng-template>
</kendo-grid-column>


+ 16
- 14
src/app/pay-in/pay-in.component.scss Bestand weergeven

@@ -1,21 +1,23 @@
kendo-grid {
height: calc(100vh - 48px);
height: 100%; // calc(100vh - 48px);

.add-new-tool-bar{
width:30%;
display: inline-block;
}
.add-new-tool-bar{
width:30%;
display: inline-block;
}

.filter-panel-wrapper{
display: inline-block;
text-align:right;
}
.filter-panel-wrapper{
display: inline-block;
text-align:right;

span.specific-upload{
margin-right: 10px;
}
}
}

.balance {
display: inline-block;
margin-left: 0px !important;
width: calc(100% - 60px) !important;
display: inline-block;
margin-left: 0px !important;
width: calc(100% - 60px) !important;
}



+ 139
- 53
src/app/pay-in/pay-in.component.ts Bestand weergeven

@@ -1,4 +1,4 @@
import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import {PayInModel} from '../models/pay-in.model';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {PayInListFilterModel} from '../models/pay-in-list.filter.model';
@@ -8,14 +8,13 @@ import {Router} from '@angular/router';
import {PopupIncomeFilterComponent} from '../popup-income-filter/popup-income-filter.component';
import {PageChangeEvent, SortSettings} from '@progress/kendo-angular-grid';
import {SortDescriptor} from '@progress/kendo-data-query';
import {UploadMetaModel} from '../models/uploadMetaModel';
import {debounce} from 'ts-debounce';
import {LoanModel} from '../models/loan.model';
import {LoanSingleService} from '../service/loan.single.service';
import {PayInModelEx} from '../models/pay-in-ex.model';


const createFormGroup = dataItem => new FormGroup({
Id: new FormControl({value: dataItem.Id, disabled: true}, Validators.required),
Trail : new FormControl(dataItem.Trail, Validators.required),
Ts: new FormControl(dataItem.Ts, Validators.required),
Balance: new FormControl(dataItem.Balance, Validators.required),
OffsetBalance: new FormControl(dataItem.OffsetBalance, Validators.required),
});


@Component({
@@ -26,8 +25,15 @@ const createFormGroup = dataItem => new FormGroup({
export class PayInComponent implements OnInit {
@Input() allowAddNew = true;
@Input() allowEdit = true;
@Input() showLoanColumn = true;
@Input() showUploadColumn = true;
private filterUploadMeta: UploadMetaModel = new UploadMetaModel({});
public filterLoan = new LoanModel({});
@Input() filter: PayInListFilterModel = new PayInListFilterModel({});
@Output() errorOccurred = new EventEmitter<string>();
@ViewChild('filterDialog', {static: true}) filterDialog: PopupIncomeFilterComponent;
private privateLoadDataNow = false;


public gridData: PayInListResult = { data: [], total: 0};
public incomeFormGroup: FormGroup;
@@ -40,14 +46,14 @@ export class PayInComponent implements OnInit {
public sortable: SortSettings = {
mode: 'single'
};
public filterable = false;

public loading = false;
private debouncedLoadFilteredData = () => {};

constructor(private pis: PayInService, private router: Router) { }
constructor(private pis: PayInService, private lss: LoanSingleService, private router: Router) { }

ngOnInit(): void {
this.loadFilteredData();
this.debouncedLoadFilteredData = debounce(this.loadFilteredData, 100);
}

public loadFilteredData(): void{
@@ -57,9 +63,9 @@ export class PayInComponent implements OnInit {
this.gridData.total = resp.total;
this.gridData.data = [];
resp.data.forEach(v => {
this.gridData.data.push(new PayInModel(v));
this.loading = false;
this.gridData.data.push(new PayInModelEx(v));
});
this.loading = false;
}, err => {
this.loading = false;
}, () => {
@@ -68,6 +74,48 @@ export class PayInComponent implements OnInit {
);
}

// Upload Filter
@Input() set uploadMeta(value: UploadMetaModel) {
this.filterUploadMeta = value;
if ( value.Id <= 0 ) {
this.filter.UploadIds = [];
} else{
this.filter.UploadIds = [ value.Id.toString() ];
}
this.debouncedLoadFilteredData();
console.log('upload filter changed', value, this.filter);
}

get uploadMeta(): UploadMetaModel {
return this.filterUploadMeta;
}

// LoanId filter
@Input() set Loan(value: LoanModel){
this.filterLoan = value;
if ( value.Id === '' ) {
this.filter.LoanIds = [];
}else{
this.filter.LoanIds = [ value.Id ];
}
this.debouncedLoadFilteredData();
console.log('filter loanId changed', value, this.filter);
}

get Loan(): LoanModel {
return this.filterLoan;
}

@Input() set LoadDataNow( value: boolean) {
this.privateLoadDataNow = value;
if ( value === true ) {
this.debouncedLoadFilteredData();
}
}
get LoadDataNow(): boolean {
return this.privateLoadDataNow;
}

public addHandler({ sender }): void {
this.closeEditor(sender);

@@ -75,23 +123,37 @@ export class PayInComponent implements OnInit {
const offsetBalance = -1;
this.showBalance = balance >= 0 ;
this.showOffsetBalance = offsetBalance >= 0 ;
this.incomeFormGroup = createFormGroup({
Id: 0,
Trail: 168,
Ts: new Date(),
Balance: balance,
OffsetBalance: offsetBalance
});
this.incomeFormGroup = this.createFormGroup(new PayInModelEx({}));

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

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

return new FormGroup({
Id: new FormControl({value: dataItem.Id, disabled: true}, Validators.required),
Lender: new FormControl(dataItem.Lender),
Amount: new FormControl(dataItem.Amount),
LoanNumber: new FormControl(dataItem.LoanNumber),
IncomeAmount: new FormControl(dataItem.IncomeAmount, Validators.required),
IncomeType: new FormControl(dataItem.IncomeType, Validators.required),
Ts: new FormControl(dataItem.Ts, Validators.required),
Balance: new FormControl(dataItem.Balance, Validators.required),
OffsetBalance: new FormControl(dataItem.OffsetBalance, Validators.required),
Loan: new FormControl(Loan),
UploadMeta: new FormControl(UploadMeta)
});
}

public editHandler({ sender, rowIndex, dataItem }): void {
this.closeEditor(sender);

this.showBalance = dataItem.Balance >= 0 ;
this.showOffsetBalance = dataItem.OffsetBalance >= 0 ;
this.incomeFormGroup = createFormGroup(dataItem);
this.incomeFormGroup = this.createFormGroup(dataItem);
this.editedRowIndex = rowIndex;
sender.editRow(rowIndex, this.incomeFormGroup);

@@ -108,42 +170,66 @@ export class PayInComponent implements OnInit {
if ( !this.showBalance) { v.Balance = -1; }
if ( !this.showOffsetBalance) { v.OffsetBalance = -1; }

// const pi = new PayInModel(
// v.Id,
// this.Loan.Amount,
// v.Balance,
// this.Loan.Lender,
// this.Loan.Id,
// this.Loan.LenderLoanNumber,
// v.OffsetBalance,
// this.Loan.Settlement,
// v.Trail,
// v.Ts,
// 0,
// );

// console.log('saving PayIn', pi);
// this.ls.savePayIn(pi, isNew).subscribe(
// (resp: PayInModel) => {
// this.Loan.cuPayIn(resp);
// },
// err => {
// this.errorOccurred.emit('Error saving Income');
// }
// );

const pi = this.buildPayInForSave(v, isNew);
this.lss.savePayIn(pi, isNew).subscribe(
(resp: PayInModel) => {
this.Loan.cuPayIn(resp);
this.updatePiInGrid(new PayInModel(resp));
},
err => {
// TODO: this.errorOccurred.emit('Error saving Income');
}
);
sender.closeRow(rowIndex);
}

private buildPayInForSave(v: any, isNew: boolean): PayInModel {
const pi = new PayInModel({});
if ( isNew ) { pi.Id = 0; }

// are we using filtered loan?
let loan = v.Loan;

if (loan === null || this.filterLoan !== undefined && this.filterLoan.Id !== '') {
loan = this.filterLoan;
}

pi.Id = v.Id;
pi.Amount = loan.Amount;
pi.Balance = v.Balance;
pi.Lender = loan.Id === '' ? v.Lender : loan.Lender;
pi.LoanId = loan.Id;
pi.LoanNumber = loan.LenderLoanNumber;
pi.OffsetBalance = v.OffsetBalance;
pi.Settlement = loan.Settlement;
pi.IncomeAmount = v.IncomeAmount;
pi.IncomeType = v.IncomeType;
pi.UploadId = this.filterUploadMeta.Id;
pi.Ts = v.Ts;
return pi;
}

private updatePiInGrid(pi: PayInModel): void {
const notFound = this.gridData.data.every( v => {
if ( v.Id === pi.Id ){
Object.assign(v, pi);
return false;
}
return true;
});

if ( notFound ) {
this.gridData.data.unshift(pi);
}
}

public removeHandler({ dataItem }): void {
console.log(dataItem);

// const na = this.Loan.PayIn.filter(v => {
// return v.Id !== dataItem.Id;
// });
//
// this.Loan.PayIn = na;
// this.ls.removePayIn(dataItem.Id);
console.log('delete', dataItem);

this.Loan.PayIn = this.Loan.PayIn.filter(v => v.Id !== dataItem.Id );
this.gridData.data = this.gridData.data.filter(v => v.Id !== dataItem.Id) ;

this.lss.removePayIn(dataItem.Id);
}

private closeEditor(grid, rowIndex = this.editedRowIndex): void{

+ 44
- 16
src/app/upload-detail/upload-detail.component.html Bestand weergeven

@@ -52,20 +52,43 @@
</div>
</div>
<div *ngIf="scanDone" class="analysis-result">
<kendo-grid *ngIf="ua.AAA !==undefined && ua.AAA !== null" [data]="analysisAAA">
<ng-template kendoGridToolbarTemplate>
{{ ua.Funders[0] }}
</ng-template>
<kendo-grid-column field="Period" format='{0:yyyy MMMM}'> </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="LoanAmount" 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>
<div *ngIf="ua.Funders.length === 0">
<kendo-grid> </kendo-grid>
</div>
<div *ngIf="ua.Funders === undefined || ua.Funders.length == 0"> No result analyzed </div>
<kendo-tabstrip *ngIf="ua.Funders !== undefined && ua.Funders.length > 0">
<kendo-tabstrip-tab *ngIf="ua.AAA !==undefined && ua.AAA !== null" [title]="'AAA'" [selected]="true">
<ng-template kendoTabContent>
<kendo-grid [data]="analysisAAA">
<ng-template kendoGridToolbarTemplate>
{{ ua.Funders[0] }}
</ng-template>
<kendo-grid-column field="Period" format='{0:yyyy MMMM}'> </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="LoanAmount" 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>
</ng-template>
</kendo-tabstrip-tab>
<kendo-tabstrip-tab [title]="'Paris'" >
<ng-template kendoTabContent>
<p>
Paris is the capital and most populous city of France. It has an area of 105 square kilometres (41 square miles) and a population in 2013 of 2,229,621 within its administrative limits. The city is both a commune and department, and forms the centre and headquarters of the Île-de-France, or Paris Region, which has an area of 12,012 square kilometres (4,638 square miles) and a population in 2014 of 12,005,077, comprising 18.2 percent of the population of France.
</p>
</ng-template>
</kendo-tabstrip-tab>

<kendo-tabstrip-tab [title]="'Tallinn'">
<ng-template kendoTabContent>
<p>
Tallinn is the capital and largest city of Estonia. It is situated on the northern coast of the country, on the shore of the Gulf of Finland, 80 km (50 mi) south of Helsinki, east of Stockholm and west of Saint Petersburg. From the 13th century until 1918 (and briefly during the Nazi occupation of Estonia from 1941 to 1944), the city was known as Reval. Tallinn occupies an area of 159.2 km2 (61.5 sq mi) and has a population of 443,894. Approximately 32% of Estonia's total population lives in Tallinn.
</p>
<p>
Tallinn was founded in 1248, but the earliest human settlements are over 5,000 years old, making it one of the oldest capital cities of Northern Europe. Due to its strategic location, the city became a major trade hub, especially from the 14th to the 16th century, when it grew in importance as part of the Hanseatic League.
</p>
</ng-template>
</kendo-tabstrip-tab>
<kendo-tabstrip-tab [title]="'Sydney'" [disabled]="true"></kendo-tabstrip-tab>
</kendo-tabstrip>
</div>

</ng-template>
@@ -80,8 +103,13 @@
(sizeChange)="onPayInSizeChange($event)"
[(size)]="PayInSize">
<div class="pane-content">
<h3>Outer splitter / Bottom pane</h3>
<p>Non-resizable and non-collapsible.</p>
<app-pay-in *ngIf="ua.Input !== undefined && ua.Input !== null"
[uploadMeta]="ua.Input"
[showUploadColumn]="false"
[showLoanColumn]="true"
[LoadDataNow] = "true"
>
</app-pay-in>
</div>
</kendo-splitter-pane>
</kendo-splitter>

+ 7
- 0
src/app/upload-detail/upload-detail.component.scss Bestand weergeven

@@ -181,6 +181,13 @@ div.analysis-result{
height: 100%;
width: 100%;
background-color: lightgoldenrodyellow;
padding: 10px;
box-shadow: inset 1px 1px 10px #9c9c8a;
overflow: scroll;

kendo-tabstrip{
box-shadow: 1px 1px 20px black;
}
kendo-grid{
height: 100%;
}

Laden…
Annuleren
Opslaan