| @@ -1,6 +1,6 @@ | |||
| { | |||
| "name": "broker", | |||
| "version": "2.0.1", | |||
| "version": "2.0.4", | |||
| "lockfileVersion": 1, | |||
| "requires": true, | |||
| "dependencies": { | |||
| @@ -1,12 +1,12 @@ | |||
| { | |||
| "name": "broker", | |||
| "version": "2.0.1", | |||
| "version": "2.0.4", | |||
| "scripts": { | |||
| "ng": "ng", | |||
| "start": "ng serve --proxy-config proxy.conf.json", | |||
| "prebuild": "npm --no-git-tag-version version patch", | |||
| "build": "ng build ", | |||
| "buildsfm": "ng build --prod --base-href=/broker/ --deploy-url=/broker/ ", | |||
| "buildsfm": "npm --no-git-tag-version version patch && ng build --prod --base-href=/broker/ --deploy-url=/broker/ ", | |||
| "test": "ng test", | |||
| "lint": "ng lint", | |||
| "e2e": "ng e2e" | |||
| @@ -28,6 +28,7 @@ import {ListIncomeComponent} from './list-income/list-income.component'; | |||
| import {UploadDetailComponent} from './upload-detail/upload-detail.component'; | |||
| import {LoansAllComponent} from './loans-all/loans-all.component'; | |||
| import {RewardsAllComponent} from './rewards-all/rewards-all.component'; | |||
| import {PayOutDetailsComponent} from './pay-out-details/pay-out-details.component'; | |||
| const routes: Routes = [ | |||
| @@ -60,6 +61,8 @@ const routes: Routes = [ | |||
| {path : 'profile/:id', component: ProfileComponent, canActivate: [AuthGuard] }, | |||
| {path : 'upload-details/:id', component: UploadDetailComponent, canActivate: [AuthGuard] }, | |||
| {path : 'list-income', component: ListIncomeComponent, canActivate: [AuthGuard] }, | |||
| {path : 'payout-details/:id', component: PayOutDetailsComponent, canActivate: [AuthGuard] }, | |||
| {path : 'reward-paid/:id', component: RewardPaidComponent, canActivate: [AuthGuard] }, | |||
| {path : 'e403', component: E403Component }, | |||
| ]; | |||
| @@ -1,12 +1,12 @@ | |||
| //Angular | |||
| // Angular | |||
| import {APP_INITIALIZER, NgModule} from '@angular/core'; | |||
| import { CommonModule } from '@angular/common'; | |||
| import { BrowserModule } from '@angular/platform-browser'; | |||
| import { FormsModule, ReactiveFormsModule } from '@angular/forms'; | |||
| import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | |||
| import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http'; | |||
| //Kendo | |||
| // Kendo | |||
| import { MenuModule, ContextMenuModule } from '@progress/kendo-angular-menu'; | |||
| import { IconsModule } from '@progress/kendo-angular-icons'; | |||
| import { DialogsModule } from '@progress/kendo-angular-dialog'; | |||
| @@ -14,7 +14,7 @@ import {ButtonsModule, FloatingActionButtonModule} from '@progress/kendo-angular | |||
| import { GridModule, PDFModule, ExcelModule } from '@progress/kendo-angular-grid'; | |||
| import { InputsModule } from '@progress/kendo-angular-inputs'; | |||
| //App | |||
| // App | |||
| import { AppComponent } from './app.component'; | |||
| import { AppRoutingModule } from './app-routing.module'; | |||
| import { DashboardComponent } from './dashboard/dashboard.component'; | |||
| @@ -2,7 +2,7 @@ | |||
| [data]="gridView" | |||
| [loading]="loading" | |||
| [sortable]="true" | |||
| [pageable]="true" | |||
| [pageable]="pageable" | |||
| [filterable]="true" | |||
| [resizable]="true" | |||
| [selectable]="selectTableSettings" | |||
| @@ -10,15 +10,20 @@ | |||
| kendoGridSelectBy="Id" | |||
| [selectedKeys]="selected" | |||
| (selectionChange)="onSelectionChange($event)" | |||
| [pageSize]="state.take" | |||
| [skip]="state.skip" | |||
| [sort]="state.sort" | |||
| [filter]="state.filter" | |||
| (dataStateChange)="dataStateChange($event)" | |||
| class="fullheight_grid" | |||
| > | |||
| <ng-template kendoGridToolbarTemplate [position]="'top'"> | |||
| <button (click)="onSelectionEmit()" class="k-button">Make Payment</button> | |||
| <div class="payment-toolbar"> | |||
| <button *ngIf="showNonPaidOnly" (click)="onSelectionEmit()" class="k-button">Make Payment</button> | |||
| <span *ngIf="!showNonPaidOnly"> Today: {{ today | date }}</span> | |||
| </div> | |||
| </ng-template> | |||
| <kendo-grid-checkbox-column *ngIf="EnableSelectButton" width="50" ></kendo-grid-checkbox-column> | |||
| @@ -44,7 +49,7 @@ | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="ToDisplay" title="Beneficiary" width="250"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem.To)}"></div> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem.ToId)}"></div> | |||
| <div class="customer-name"> {{ dataItem.ToDisplay }}</div> | |||
| </ng-template> | |||
| <ng-template kendoGridFilterCellTemplate let-filter let-column="column"> | |||
| @@ -89,7 +94,15 @@ | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Paid" title="Paid"> | |||
| <kendo-grid-column field="Paid" title="Invoice" [sortable]="false" [filterable]="false" width="100"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <button *ngIf="dataItem.Paid" kendoButton [icon]="'attachment'" | |||
| (click)="onGoDetail(dataItem)"> {{dataItem.PayOutId}} </button> | |||
| <kendo-chip *ngIf="! dataItem.Paid" [label]="'$'" (click)="onGoPayment(dataItem)" > | |||
| </kendo-chip> | |||
| </ng-template> | |||
| <ng-template kendoGridFilterCellTemplate let-filter let-column="column"> | |||
| <kendo-grid-boolean-filter-cell | |||
| [column]="column" | |||
| @@ -1,7 +1,14 @@ | |||
| kendo-grid.fullheight_grid{ | |||
| height: 100% !important; | |||
| div.payment-toolbar { | |||
| width: 100%; | |||
| text-align: right; | |||
| display:block; | |||
| } | |||
| } | |||
| .customer-photo{ | |||
| display: inline-block; | |||
| width: 32px; | |||
| @@ -1,11 +1,12 @@ | |||
| import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; | |||
| import {AuthService} from '../service/auth.service'; | |||
| import {HttpClient} from '@angular/common/http'; | |||
| import {RewardByUserModel} from '../models/reward-by-user.model'; | |||
| import {DataStateChangeEvent, GridDataResult, SelectableSettings,} from '@progress/kendo-angular-grid'; | |||
| import {DataStateChangeEvent, GridDataResult, PagerSettings, SelectableSettings,} from '@progress/kendo-angular-grid'; | |||
| import {State, process} from '@progress/kendo-data-query'; | |||
| import {PayInModel} from '../models/pay-in.model'; | |||
| import {RewardModel} from '../models/reward.model'; | |||
| import {RewardExModel} from '../models/reward-ex.model'; | |||
| import {SelectionEvent} from '@progress/kendo-angular-grid/dist/es2015/selection/types'; | |||
| import {RewardService} from '../service/reward.service'; | |||
| import {Router} from '@angular/router'; | |||
| @Component({ | |||
| selector: 'app-list-all-rewards', | |||
| @@ -13,9 +14,15 @@ import {RewardModel} from '../models/reward.model'; | |||
| styleUrls: ['./list-all-rewards.component.scss'] | |||
| }) | |||
| export class ListAllRewardsComponent implements OnInit { | |||
| @Output() confirmSelection = new EventEmitter<RewardExModel[]>(); | |||
| @Input() selectedRewards: RewardExModel[] = []; | |||
| @Input() showNonPaidOnly: false; | |||
| @Input() filterByPayOutId = 0; | |||
| @Input() filterByUserId = ''; | |||
| public today = new Date(); | |||
| public selected: number[] = []; | |||
| public gridData: RewardByUserModel[] = [] ; | |||
| public gridData: RewardExModel[] = [] ; | |||
| public gridView: GridDataResult; | |||
| public state: State = { | |||
| skip: 0, | |||
| @@ -24,6 +31,11 @@ export class ListAllRewardsComponent implements OnInit { | |||
| }; | |||
| public loading = true; | |||
| @Input() public pageable: PagerSettings = { | |||
| pageSizes: [2, 5, 10, 15, 20, 30], | |||
| previousNext: true | |||
| }; | |||
| public selectTableSettings: SelectableSettings | boolean = true; | |||
| // private multipleSelection: SelectableSettings = { | |||
| // checkboxOnly: false, | |||
| @@ -32,40 +44,45 @@ export class ListAllRewardsComponent implements OnInit { | |||
| // }; | |||
| public EnableSelectButton = true; | |||
| @Input() selectedRewards: RewardModel[] = []; | |||
| public selected: number[] = []; | |||
| @Output() RewardsSelected: EventEmitter<PayInModel[]> = new EventEmitter<PayInModel[]>(); | |||
| constructor(private auth: AuthService, private http: HttpClient) { } | |||
| constructor(private auth: AuthService, private rs: RewardService, private router: Router) { } | |||
| ngOnInit(): void { | |||
| this.loadInitRewardGrid(); | |||
| this.EnableSelectButton = this.showNonPaidOnly; | |||
| if ( this.showNonPaidOnly ){ | |||
| this.state.filter.filters.push({ | |||
| field: 'PayOutId', | |||
| operator: 'isnull', | |||
| value: '' | |||
| }); | |||
| } | |||
| this.selectedRewards.forEach(v => this.selected.push(v.Id)); | |||
| this.loadRewardGrid(); | |||
| } | |||
| public loadInitRewardGrid(): void { | |||
| public loadRewardGrid(): void { | |||
| this.loading = true; | |||
| this.gridData = []; | |||
| this.http.get<RewardByUserModel[]>(this.auth.getUrl('user-reward/')).subscribe( | |||
| rsp => { | |||
| this.gridView = {total: 0, data: []}; | |||
| this.rs.getRewardExList(this.state).subscribe( | |||
| resp => { | |||
| this.gridView.total = resp.total; | |||
| resp.data.forEach( v => this.gridView.data.push(new RewardExModel(v)) ); | |||
| this.loading = false; | |||
| }, error => { | |||
| this.loading = false; | |||
| }, () => { | |||
| this.loading = false; | |||
| rsp.forEach(v => { | |||
| this.gridData.push(new RewardByUserModel(v)); | |||
| }); | |||
| this.loadRewards(); | |||
| } | |||
| ); | |||
| } | |||
| private loadRewards(): void { | |||
| this.gridView = process(this.gridData, this.state); | |||
| } | |||
| public dataStateChange(state: DataStateChangeEvent): void { | |||
| this.state = state; | |||
| console.log(state); | |||
| this.loadRewards(); | |||
| this.loadRewardGrid(); | |||
| } | |||
| private photoURL(peopleId: any): string { | |||
| @@ -74,6 +91,24 @@ export class ListAllRewardsComponent implements OnInit { | |||
| } | |||
| public onSelectionEmit(): void { | |||
| console.log(this.selected); | |||
| this.confirmSelection.emit(this.selectedRewards); | |||
| } | |||
| public onSelectionChange(e: SelectionEvent): void { | |||
| this.selectedRewards = this.selectedRewards.filter( v => ! e.deselectedRows.find( deSel => v.Id === deSel.dataItem.Id ) ); | |||
| e.selectedRows.forEach( v => { | |||
| if (! this.selectedRewards.find(existing => existing.Id === v.dataItem.Id) ){ | |||
| this.selectedRewards.push(v.dataItem); | |||
| } | |||
| }); | |||
| return; | |||
| } | |||
| public onGoDetail(item: RewardExModel): void{ | |||
| this.router.navigate(['reward-paid/', item.PayOutId]).then(); | |||
| } | |||
| public onGoPayment( item: RewardExModel): void{ | |||
| this.router.navigate(['reward-paid/']).then(); | |||
| } | |||
| } | |||
| @@ -102,7 +102,7 @@ export class LoanEditComponent implements OnInit { | |||
| public action(status): void { | |||
| this.dialogOpened = false; | |||
| if ( this.navigateTo !== '' ) { | |||
| this.router.navigate([this.navigateTo]); | |||
| this.router.navigate([this.navigateTo]).then(); | |||
| } | |||
| } | |||
| @@ -28,15 +28,12 @@ | |||
| </ng-template> | |||
| </kendo-grid-command-column> | |||
| <kendo-grid-column field="To" title="Name" width="200"> | |||
| <kendo-grid-column field="ToId" title="Name" width="200"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem.To) }"></div> | |||
| <div class="customer-photo" [ngStyle]="{'background-image' : photoURL(dataItem.ToId) }"></div> | |||
| <div class="customer-name"> {{ UserName(dataItem) }}</div> | |||
| </ng-template> | |||
| <ng-template | |||
| kendoGridEditTemplate let-fg="formGroup" let-column="column" let-dataItem="dataItem"> | |||
| <ng-template kendoGridEditTemplate let-fg="formGroup" let-column="column" let-dataItem="dataItem"> | |||
| <kendo-multicolumncombobox | |||
| #selectRevelantPeople | |||
| @@ -10,13 +10,14 @@ import {ClonerService} from '../../service/clone.service'; | |||
| import {PeopleService} from '../../service/people.service'; | |||
| import {Observable, of} from 'rxjs'; | |||
| import {map} from 'rxjs/operators'; | |||
| import {RewardService} from '../../service/reward.service'; | |||
| const createFormGroup = dataItem => new FormGroup({ | |||
| Id: new FormControl(dataItem.Id), | |||
| To: new FormControl(dataItem.To, [Validators.required, Validators.maxLength(128), Validators.minLength(1)]), | |||
| From: new FormControl(dataItem.From), | |||
| ToId: new FormControl(dataItem.ToId, [Validators.required, Validators.maxLength(128), Validators.minLength(1)]), | |||
| FromId: new FormControl(dataItem.FromId), | |||
| Role: new FormControl({value: dataItem.Role, disabled: true} , [Validators.required, Validators.minLength(2), Validators.maxLength(40)]), | |||
| Amount: new FormControl(dataItem.Amount, Validators.compose([Validators.required, Validators.min(0), Validators.max(1000000)])), | |||
| Description: new FormControl(dataItem.Description, [Validators.required, Validators.maxLength(128)]), | |||
| @@ -95,8 +96,8 @@ export class PeopleRewardComponent implements OnInit { | |||
| this.closeEditor(sender); | |||
| this.formGroup = createFormGroup({ | |||
| Id: 0, | |||
| To: '', | |||
| From: '', | |||
| ToId: '', | |||
| FromId: '', | |||
| Role: 'Unknown', | |||
| Amount: 0, | |||
| Description: '', | |||
| @@ -123,8 +124,8 @@ export class PeopleRewardComponent implements OnInit { | |||
| } | |||
| public saveHandler({ sender, rowIndex, formGroup, isNew }): void { | |||
| const reward = formGroup.value; | |||
| reward.From = '0'; // Admin | |||
| const reward: RewardModel = formGroup.value; | |||
| reward.FromId = '0'; // Admin | |||
| // reward.Ts = new Date(); // Now | |||
| reward.LoanId = this.Loan.Id; // Enforce LoanId | |||
| @@ -132,17 +133,18 @@ export class PeopleRewardComponent implements OnInit { | |||
| this.ls.saveReward(reward, isNew).subscribe( | |||
| resp => { | |||
| if ( reward.Id === 0 ) { | |||
| const r = this.Loan.addReward(resp.Amount, resp.Description, resp.Id, resp.LoanId, resp.PayOutId, resp.To, resp.From, resp.Ts); | |||
| const r = this.Loan.addReward( | |||
| resp.Amount, resp.Description, resp.Id, resp.LoanId, resp.PayOutId, resp.ToId, resp.FromId, resp.Ts); | |||
| this.pendingReward.unshift(r); | |||
| }else{ | |||
| const idx = this.pendingReward.findIndex( v => v.Id === reward.Id); | |||
| // update | |||
| this.pendingReward[idx].To = reward.To; | |||
| this.pendingReward[idx].ToId = reward.ToId; | |||
| this.pendingReward[idx].Amount = reward.Amount; | |||
| this.pendingReward[idx].Description = reward.Description; | |||
| this.pendingReward[idx].Ts = reward.Ts; | |||
| // update Loan | |||
| this.Loan.updateReward(resp.Amount, resp.Description, resp.Id, resp.LoanId, resp.PayOutId, resp.To, resp.From, resp.Ts); | |||
| this.Loan.updateReward(resp.Amount, resp.Description, resp.Id, resp.LoanId, resp.PayOutId, resp.ToId, resp.FromId, resp.Ts); | |||
| } | |||
| } | |||
| @@ -34,8 +34,7 @@ export const mainMenuItems: any[] = [ | |||
| { text: '--', separator: 'true' }, | |||
| { text: 'By Broker', icon: 'table', url: './#list-reward-by-broker' }, | |||
| { text: '--', separator: 'true' }, | |||
| { text: 'Not Paid', icon: 'attachment' , url: './#reward-unpaid'}, | |||
| { text: 'Paid', icon: 'attachment' , url: './#reward-paid'}, | |||
| { text: 'Invoice', icon: 'attachment' , url: './#reward-paid'}, | |||
| ] | |||
| }, | |||
| { | |||
| @@ -146,8 +146,8 @@ export class LoanModel { | |||
| reward.Id, | |||
| reward.LoanId, | |||
| reward.PayOutId, | |||
| reward.To, | |||
| reward.From, | |||
| reward.ToId, | |||
| reward.FromId, | |||
| reward.Ts); | |||
| }); | |||
| return; | |||
| @@ -206,15 +206,15 @@ export class LoanModel { | |||
| }); | |||
| } | |||
| public addReward(Amount: number, Description: string, Id: number, | |||
| LoanId: string, PayOutId: number, To: string , From: string, Ts: Date): RewardModel { | |||
| LoanId: string, PayOutId: number, ToId: string , FromId: string, Ts: Date): RewardModel { | |||
| const r = new RewardModel( | |||
| Amount, | |||
| Description, | |||
| Id, | |||
| LoanId, | |||
| PayOutId, | |||
| To, | |||
| From, | |||
| ToId, | |||
| FromId, | |||
| new Date(Ts), | |||
| this.callBacks() | |||
| ); | |||
| @@ -224,15 +224,15 @@ export class LoanModel { | |||
| } | |||
| public updateReward(Amount: number, Description: string, Id: number, | |||
| LoanId: string, PayOutId: number, To: string , From: string, Ts: Date): RewardModel { | |||
| LoanId: string, PayOutId: number, ToId: string , FromId: string, Ts: Date): RewardModel { | |||
| const r = new RewardModel( | |||
| Amount, | |||
| Description, | |||
| Id, | |||
| LoanId, | |||
| PayOutId, | |||
| To, | |||
| From, | |||
| ToId, | |||
| FromId, | |||
| new Date(Ts), | |||
| this.callBacks() | |||
| ); | |||
| @@ -242,8 +242,8 @@ export class LoanModel { | |||
| this.Reward[idx].Description = Description; | |||
| this.Reward[idx].LoanId = r.LoanId; // Ensure Loan Id is correct | |||
| this.Reward[idx].PayOutId = PayOutId; | |||
| this.Reward[idx].To = To; | |||
| this.Reward[idx].From = From; | |||
| this.Reward[idx].ToId = ToId; | |||
| this.Reward[idx].FromId = FromId; | |||
| this.Reward[idx].Ts = new Date(Ts); | |||
| return r; | |||
| @@ -318,24 +318,34 @@ export class LoanModel { | |||
| const result: RelevantPeopleModel[] = []; | |||
| this.Client.forEach(( c => { | |||
| result.push({Id: c.Id, Display: c.Display, Role: 'Client'} ) ; | |||
| if ( !result.find( existing => existing.Id === c.Id ) ){ | |||
| result.push({Id: c.Id, Display: c.Display, Role: 'Client'} ) ; | |||
| } | |||
| })); | |||
| this.Broker.forEach(( c => { | |||
| result.push({Id: c.Id, Display: c.Display, Role: 'Broker'}); | |||
| if ( !result.find( existing => existing.Id === c.Id ) ) { | |||
| result.push({Id: c.Id, Display: c.Display, Role: 'Broker'}); | |||
| } | |||
| })); | |||
| this.OtherRewarder.forEach(( c => { | |||
| result.push({Id: c.Id, Display: c.Display, Role: this.getRoleById(c.Id)}); | |||
| if ( !result.find( existing => existing.Id === c.Id ) ) { | |||
| result.push({Id: c.Id, Display: c.Display, Role: this.getRoleById(c.Id)}); | |||
| } | |||
| })); | |||
| this.RewardPeople.forEach( c => { | |||
| result.push({Id: c.Id, Display: c.Display, Role: 'none'}); | |||
| if ( !result.find( existing => existing.Id === c.Id ) ) { | |||
| result.push({Id: c.Id, Display: c.Display, Role: 'none'}); | |||
| } | |||
| }); | |||
| return [...new Set(result)]; // remove duplicates, if any | |||
| return result ; // [...new Set(result)]; // remove duplicates, if any | |||
| } | |||
| public getRoleById(id: string): string { | |||
| let role = ''; | |||
| this.PeopleMap.every(v => { | |||
| @@ -1,5 +1,3 @@ | |||
| import {LoanModel} from './loan.model'; | |||
| import {UploadMetaModel} from './uploadMetaModel'; | |||
| export class PayInModel { | |||
| public Id: number; | |||
| @@ -1,18 +0,0 @@ | |||
| export class PayoutAudit{ | |||
| id: string; | |||
| //parent | |||
| payOut: string; | |||
| //date | |||
| when : Date; | |||
| //by who, at least 2 | |||
| who : string ; | |||
| //for what? edit? paid? | |||
| what: string; | |||
| //ip | |||
| ip: string; | |||
| } | |||
| @@ -1,4 +1,6 @@ | |||
| import {UserExModel} from './userExModel'; | |||
| import {PayoutModel} from './payout.model'; | |||
| import {RewardExModel} from './reward-ex.model'; | |||
| export class PayOutExModel{ | |||
| Id: number; | |||
| @@ -10,12 +12,14 @@ export class PayOutExModel{ | |||
| PayDate: Date; | |||
| PayAmount: number; | |||
| Status: string; | |||
| constructor( payload: Partial<PayOutExModel>) { | |||
| Rewards?: RewardExModel[]; | |||
| constructor( payload?: Partial<PayOutExModel>) { | |||
| if (! payload ) { payload = {}; } | |||
| this.Id = payload.Id || 0; | |||
| if (payload.Ts){ | |||
| this.Ts = new Date( payload.Ts); | |||
| }else{ | |||
| this.Ts = new Date('1800-01-01'); | |||
| this.Ts = new Date(); | |||
| } | |||
| this.To = new UserExModel(payload.To); | |||
| @@ -30,5 +34,25 @@ export class PayOutExModel{ | |||
| } | |||
| this.PayAmount = payload.PayAmount || 0; | |||
| this.Status = payload.Status || ''; | |||
| this.Rewards = []; | |||
| if ( payload.Rewards && payload.Rewards.length){ | |||
| payload.Rewards.forEach( v => this.Rewards.push( new RewardExModel(v) ) ); | |||
| } | |||
| } | |||
| public toPayOutModel(): PayoutModel { | |||
| const ret = new PayoutModel(); | |||
| ret.Id = this.Id; | |||
| ret.Ts = this.Ts; | |||
| ret.To = this.To.Id; | |||
| ret.Prepared = this.Prepared.Id; | |||
| ret.Approved = this.Approved.Id; | |||
| ret.Paid = this.Paid.Id; | |||
| ret.PayDate = this.PayDate; | |||
| ret.PayAmount = this.PayAmount; | |||
| ret.Status = this.Status; | |||
| ret.RewardIds = this.Rewards.map( v => v.Id); | |||
| return ret; | |||
| } | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| import {UserExModel} from './userExModel'; | |||
| export class PayoutModel { | |||
| Id: number; | |||
| Ts: Date; | |||
| To: string; | |||
| Prepared: string; | |||
| Approved: string; | |||
| Paid: string; | |||
| PayDate: Date; | |||
| PayAmount: number; | |||
| Status: string; | |||
| RewardIds?: number[]; | |||
| constructor( payload?: Partial<PayoutModel>){ | |||
| if (! payload ) { payload = {}; } | |||
| this.Id = payload.Id || 0; | |||
| if ( payload.Ts ){ | |||
| this.Ts = new Date(payload.Ts); | |||
| }else{ | |||
| this.Ts = new Date('1900-01-01'); | |||
| } | |||
| this.To = payload.To || ''; | |||
| this.Prepared = payload.Prepared || ''; | |||
| this.Approved = payload.Approved || ''; | |||
| this.Paid = payload.Paid || ''; | |||
| if (payload.PayDate){ | |||
| this.PayDate = new Date(payload.PayDate); | |||
| }else{ | |||
| this.PayDate = new Date('1900-01-01'); | |||
| } | |||
| this.PayAmount = payload.PayAmount || 0; | |||
| this.Status = payload.Status || ''; | |||
| this.RewardIds = []; | |||
| if ( Array.isArray(payload) ) { | |||
| this.RewardIds = payload.RewardIds; | |||
| } | |||
| } | |||
| } | |||
| @@ -9,7 +9,8 @@ export class PeopleModel{ | |||
| public Nick: string; | |||
| public Enabled: boolean; | |||
| constructor(payload: Partial<PeopleModel>){ | |||
| constructor(payload?: Partial<PeopleModel>){ | |||
| if (! payload ) { payload = {} ; } | |||
| this.Id = payload.Id || ''; | |||
| this.First = payload.First || ''; | |||
| this.Last = payload.Last || ''; | |||
| @@ -0,0 +1,7 @@ | |||
| import {GridDataResult} from '@progress/kendo-angular-grid'; | |||
| import {RewardExModel} from './reward-ex.model'; | |||
| export class RewardExListResultModel implements GridDataResult { | |||
| public data: RewardExModel[]; | |||
| public total: number; | |||
| } | |||
| @@ -1,6 +1,5 @@ | |||
| import {LoanModel} from './loan.model'; | |||
| import {PayOutModel} from './payout.ex.model'; | |||
| import {PeopleModel} from './people.model'; | |||
| import {PayOutExModel} from './payout.ex.model'; | |||
| import {UserExModel} from './userExModel'; | |||
| export class RewardExModel { | |||
| @@ -13,12 +12,33 @@ export class RewardExModel { | |||
| public FromId: string; | |||
| public PayOutId: number; | |||
| public To?: UserExModel; | |||
| public Beneficiary?: UserExModel; | |||
| public Loan?: LoanModel; | |||
| public From?: UserExModel; | |||
| public PayOut?: PayOutModel; | |||
| constructor(payload: Partial<RewardExModel>) { | |||
| // derived content, for compatible with reward-by-user purpose | |||
| public ToFirst: string; | |||
| public ToMiddle: string; | |||
| public ToLast: string; | |||
| public ToTitle: string; | |||
| public ToDisplay: string; | |||
| public ToNick: string; | |||
| public FromFirst: string; | |||
| public FromMiddle: string; | |||
| public FromLast: string; | |||
| public FromTitle: string; | |||
| public FromDisplay: string; | |||
| public FromNick: string; | |||
| // Loan related derived property | |||
| public Item: string; | |||
| public Status: string; | |||
| public Settlement: Date; | |||
| constructor(payload?: Partial<RewardExModel>) { | |||
| if ( !payload ) { payload = {}; } | |||
| this.Id = payload.Id || 0; | |||
| if ( payload.Ts ) { | |||
| this.Ts = new Date(payload.Ts); | |||
| @@ -32,21 +52,63 @@ export class RewardExModel { | |||
| this.FromId = payload.FromId || ''; | |||
| this.PayOutId = payload.PayOutId || 0; | |||
| if (payload.To) { | |||
| this.To = new UserExModel(payload.To); | |||
| // derived content | |||
| // this.ToFirst = payload.ToFirst || ''; | |||
| // this.ToMiddle = payload.ToMiddle || ''; | |||
| // this.ToLast = payload.ToLast || ''; | |||
| // this.ToTitle = payload.ToTitle || ''; | |||
| // this.ToDisplay = payload.ToDisplay || ''; | |||
| // this.ToNick = payload.ToNick || ''; | |||
| // this.Item = payload.Item || ''; | |||
| // this.Status = payload.Status || ''; | |||
| // this.Settlement = new Date(payload.Settlement); | |||
| if (payload.Beneficiary) { | |||
| this.Beneficiary = new UserExModel(payload.Beneficiary); | |||
| this.ToFirst = this.Beneficiary.First || ''; | |||
| this.ToMiddle = this.Beneficiary.Middle || ''; | |||
| this.ToLast = this.Beneficiary.Last || ''; | |||
| this.ToTitle = this.Beneficiary.Title || ''; | |||
| this.ToDisplay = this.Beneficiary.Display || ''; | |||
| this.ToNick = this.Beneficiary.Nick || ''; | |||
| } | |||
| if (payload.Loan) { | |||
| this.Loan = new LoanModel(payload.Loan); | |||
| this.Item = this.Loan.Item || ''; | |||
| this.Status = this.Loan.Status || ''; | |||
| if ( this.Loan.Settlement ){ | |||
| this.Settlement = new Date(payload.Settlement); | |||
| }else{ | |||
| this.Settlement = new Date('1900-01-01'); | |||
| } | |||
| } | |||
| if ( payload.From) { | |||
| this.From = new UserExModel(payload.From); | |||
| this.FromFirst = this.From.First || ''; | |||
| this.FromMiddle = this.From.Middle || ''; | |||
| this.FromLast = this.From.Last || ''; | |||
| this.FromTitle = this.From.Title || ''; | |||
| this.FromDisplay = this.From.Display || ''; | |||
| this.FromNick = this.From.Nick || ''; | |||
| } | |||
| if ( payload. PayOut ) { | |||
| this.PayOut = new PayOutModel(payload.PayOut); | |||
| } | |||
| } | |||
| get Paid(): boolean{ | |||
| return this.PayOutId > 0 ; | |||
| } | |||
| public Equal(v: RewardExModel): boolean{ | |||
| if ( this.Id !== v.Id) { return false; } | |||
| if ( this.Ts !== v.Ts ) { return false; } | |||
| if ( this.ToId !== v.ToId) { return false; } | |||
| if ( this.Amount !== v.Amount ) { return false; } | |||
| if ( this.Description !== v.Description ) { return false; } | |||
| if ( this.LoanId !== v.LoanId ) { return false; } | |||
| if ( this.FromId !== v.FromId ) { return false; } | |||
| if ( this.PayOutId !== v.PayOutId ) { return false; } | |||
| return true; | |||
| } | |||
| } | |||
| @@ -7,35 +7,35 @@ export class RewardModel { | |||
| public Id: number, | |||
| public LoanId: string, | |||
| public PayOutId: number, | |||
| public To: string, | |||
| public From: string, | |||
| public ToId: string, | |||
| public FromId: string, | |||
| public Ts: Date, | |||
| private lc: LoanModelCallBacks | |||
| ){} | |||
| public get Role(): string { | |||
| return this.lc.getUserRole(this.To); | |||
| return this.lc.getUserRole(this.ToId); | |||
| } | |||
| public get UserId(): string { | |||
| return this.To; | |||
| } | |||
| public set UserId(v) { | |||
| this.To = v; | |||
| } | |||
| // public get UserId(): string { | |||
| // return this.ToId; | |||
| // } | |||
| // | |||
| // public set UserId(v) { | |||
| // this.ToId = v; | |||
| // } | |||
| public get UserName(): string { | |||
| return this.lc.getUserName(this.To); | |||
| return this.lc.getUserName(this.ToId); | |||
| } | |||
| public get photoUrlTo(): string{ | |||
| return this.lc.getUserPhotoUrl(this.To); | |||
| return this.lc.getUserPhotoUrl(this.ToId); | |||
| } | |||
| public get photoUrlFrom(): string{ | |||
| return this.lc.getUserPhotoUrl(this.From); | |||
| return this.lc.getUserPhotoUrl(this.FromId); | |||
| } | |||
| } | |||
| @@ -15,8 +15,9 @@ export class UserExModel extends PeopleModel{ | |||
| Role: UserRoles; | |||
| Broker?: BrokerModel; | |||
| Login: string; | |||
| constructor(payload: Partial<UserExModel>) { | |||
| constructor(payload?: Partial<UserExModel>) { | |||
| super(payload); | |||
| if ( ! payload ) { payload = {}; } | |||
| this.Role = payload.Role || 'People' as UserRoles; | |||
| this.Login = payload.Login || ''; | |||
| this.Broker = new BrokerModel(payload); | |||
| @@ -453,15 +453,17 @@ export class PayInComponent implements OnInit { | |||
| private refreshFilterByUpload(): void{ | |||
| // make a copy of existing filters | |||
| const newFilter = { logic: 'and', filters: [], ...this.state.Filter.filters}; | |||
| const newFilter = { logic: 'and', filters: []}; | |||
| // remove any existing filters about UploadsId | |||
| newFilter.filters = newFilter.filters.filter(v => { | |||
| if (!( 'field' in v) && v.operator === 'or') { // could be LoanId or UploadsIds | |||
| const subFilters = v.filters; | |||
| return ! subFilters.every( subf => subf.field === 'UploadIds' ); // if sub-filters are all about UploadsIds, we remove it | |||
| this.state.Filter.filters.forEach(v => { | |||
| if ('field' in v && 'operator' in v && 'value' in v) { | |||
| if ( v.field !== 'UploadIds' ){ | |||
| newFilter.filters.push (v); | |||
| } | |||
| }else{ | |||
| newFilter.filters.push (v); | |||
| } | |||
| return true; | |||
| }); | |||
| // build new filters | |||
| @@ -475,7 +477,10 @@ export class PayInComponent implements OnInit { | |||
| }); | |||
| // add new filter | |||
| newFilter.filters.push( uploadsFilter as CompositeFilterDescriptor ); | |||
| if (uploadsFilter.filters.length > 0){ | |||
| newFilter.filters.push( uploadsFilter as CompositeFilterDescriptor ); | |||
| } | |||
| this.state.Filter = newFilter as CompositeFilterDescriptor; | |||
| } | |||
| @@ -5,27 +5,57 @@ | |||
| <div class="col-md-12"> | |||
| <div class="card"> | |||
| <div class="card-header bg-transparent header-elements-inline"> | |||
| <app-reward-select></app-reward-select> | |||
| <div class="header-elements"> | |||
| <button> test </button> | |||
| <kendo-chip | |||
| [removable]="true" | |||
| (remove)="onRemoveChip('somelabel', $event)" | |||
| > | |||
| <span class="k-avatar-image invoice-chip" [ngStyle]="{'background-image': background}"></span> | |||
| <span> approved</span> | |||
| <kendo-chip *ngIf="model.Prepared.Id !== '' " | |||
| [removable]="model.Approved.Id === ''" | |||
| (remove)="onCancelPrepared()" > | |||
| <span class="k-avatar-image invoice-chip" | |||
| [ngStyle]="{'background-image': photoBackground(model.Prepared.Id)}"></span> | |||
| <span> prepared </span> by <span> {{model.Prepared.Display}}</span> | |||
| </kendo-chip> | |||
| <kendo-chip | |||
| [removable]="false" | |||
| (remove)="onRemoveChip('somelabel', $event)" | |||
| > | |||
| <span class="k-avatar-image invoice-chip" [ngStyle]="{'background-image': background}"></span> | |||
| <span> approved</span> | |||
| <kendo-chip *ngIf="model.Approved.Id !== '' " [removable]="model.Paid.Id === ''" (remove)="onRevoke()" > | |||
| <span class="k-avatar-image invoice-chip" | |||
| [ngStyle]="{'background-image': photoBackground(model.Approved.Id)}"></span> | |||
| <span> approved </span> by <span> {{model.Approved.Display}}</span> | |||
| </kendo-chip> | |||
| <kendo-chip *ngIf="model.Paid.Id !== '' " [removable]="model.Paid.Id !== ''" (remove)="onUnPaid()" > | |||
| <span class="k-avatar-image invoice-chip" | |||
| [ngStyle]="{'background-image': photoBackground(model.Paid.Id)}"></span> | |||
| <span> paid </span> by <span> {{model.Paid.Display}}</span> | |||
| </kendo-chip> | |||
| <button *ngIf="model.Prepared.Id !== '' && model.Approved.Id === ''" | |||
| type="button" class="btn btn-primary" (click)="onApprove()"> | |||
| <b><i class="fa fa-paper-plane-o mr-1"></i></b> Approve | |||
| </button> | |||
| <div class="select-beneficiary" *ngIf="model.Prepared.Id === ''"> | |||
| <app-people-select #selectPeople [placeholder]= "'choose pay to'" | |||
| [(ngModel)]="model.To" | |||
| (ngModelChange)="onToChange($event)"> | |||
| </app-people-select> | |||
| </div> | |||
| <button *ngIf="model.Prepared.Id === '' && model.Approved.Id === '' && model.Rewards.length > 1" | |||
| type="button" class="btn btn-primary" (click)="onPrepared()"> | |||
| <b><i class="fa fa-paper-plane-o mr-1"></i></b> Finish | |||
| </button> | |||
| <div class="make-payment" *ngIf="model.Prepared.Id !== '' && model.Approved.Id !== '' && model.Paid.Id === ''"> | |||
| <kendo-numerictextbox [(ngModel)]="model.PayAmount"> </kendo-numerictextbox> | |||
| <kendo-datepicker [(ngModel)]="model.PayDate"> </kendo-datepicker> | |||
| <button type="button" class="btn btn-primary" (click)="onPaid()"> | |||
| <b><i class="fa fa-paper-plane-o mr-1"></i></b> Paid | |||
| </button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <kendo-pdf-export #pdf paperSize="A4" margin="0.5cm"> | |||
| <kendo-pdf-export #pdf paperSize="A4" [scale]="0.6" margin="0.2cm"> | |||
| <div class="card-body"> | |||
| <div class="row"> | |||
| <div class="col-sm-6"> | |||
| @@ -44,22 +74,33 @@ | |||
| <div class="col-sm-6"> | |||
| <div class="mb-4 "> | |||
| <div class="text-sm-right"> | |||
| <h4 class="invoice-color mb-2 mt-md-2">Invoice #BBB1243</h4> | |||
| <h4 class="invoice-color mb-2 mt-md-2">Invoice #{{model.Id }}</h4> | |||
| <table class="invoice-to-details"> | |||
| <tr (click)="onEditBeneficiary()"> | |||
| <td class="left">To</td><td class="middle">:</td><td class="right"><h5 class="my-2"> | |||
| {{ model.To.Display }}</h5></td> | |||
| </tr> | |||
| <tr> | |||
| <td class="left">To</td><td class="middle">:</td><td class="right"><h5 class="my-2">Tibco Turang</h5></td> | |||
| <td class="left">email</td><td class="middle">:</td><td class="right"> | |||
| {{ model.To.Login}} </td> | |||
| </tr> | |||
| <tr> | |||
| <td class="left">email</td><td class="middle">:</td><td class="right">abc@hotmail.com</td> | |||
| <td class="left">Date</td><td class="middle">:</td><td class="right"> | |||
| <span *ngIf="model.Ts.getFullYear() > 1900"> {{ model.Ts | date: "yyyy-MM-dd" }} </span> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td class="left">Date</td><td class="middle">:</td><td class="right">March 15, 2020</td> | |||
| <td class="left">Pay Date</td><td class="middle">:</td><td class="right"> | |||
| <span *ngIf="model.PayDate.getFullYear() > 1900"> {{model.PayDate | date: "yyyy-MM-dd" }} | |||
| </span></td> | |||
| </tr> | |||
| <tr> | |||
| <td class="left">Pay Date</td><td class="middle">:</td><td class="right">March 15, 2020</td> | |||
| <td class="left">Paid Amount</td><td class="middle">:</td> | |||
| <td class="right"> {{model.PayAmount | currency}}</td> | |||
| </tr> | |||
| <tr> | |||
| <td class="left">Paid To</td><td class="middle">:</td><td class="right">BSB/ACC</td> | |||
| <td class="left">Status</td><td class="middle">:</td> | |||
| <td class="right"> <h5 class="status"> {{model.Status | uppercase}} </h5> </td> | |||
| </tr> | |||
| </table> | |||
| @@ -68,8 +109,21 @@ | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div #anchorSelectReward class="anchor"></div> | |||
| <app-reward-select *ngIf="model.Prepared.Id === ''" | |||
| [selectedRewards]="rewards" | |||
| [showNonPaidOnly]="true" | |||
| (valueChange)="onChangeRewardList($event)"> | |||
| </app-reward-select> | |||
| <kendo-popup class="hint" [anchor]="anchorSelectReward" *ngIf="hintSelectRewardsButton" | |||
| [animate]="false" | |||
| [margin]="{ horizontal: -20, vertical: -10 }"> | |||
| <kendo-icon [name]="'cursor'" [size]="'large'" [themeColor]="'warning'"></kendo-icon> | |||
| </kendo-popup> | |||
| <div class="table-responsive"> | |||
| <app-single-payout-rewards-list></app-single-payout-rewards-list> | |||
| <app-single-payout-rewards-list [items]="rewards"> | |||
| </app-single-payout-rewards-list> | |||
| </div> | |||
| <div class="card-body"> | |||
| <div class="d-md-flex flex-md-wrap"> | |||
| @@ -80,16 +134,16 @@ | |||
| <tbody> | |||
| <tr> | |||
| <th class="text-left">Subtotal:</th> | |||
| <td class="text-right">{{amount | currency}}</td> | |||
| <td class="text-right">{{subTotal | currency}}</td> | |||
| </tr> | |||
| <tr> | |||
| <th class="text-left">Tax: <span class="font-weight-normal">(25%)</span></th> | |||
| <td class="text-right">$27</td> | |||
| <th class="text-left">Tax: <span class="font-weight-normal">(10%)</span></th> | |||
| <td class="text-right">{{tax | currency}}</td> | |||
| </tr> | |||
| <tr> | |||
| <th class="text-left">Total:</th> | |||
| <td class="text-right text-primary"> | |||
| <h5 class="font-weight-semibold">$1,160</h5> | |||
| <h5 class="font-weight-semibold">{{Total | currency}}</h5> | |||
| </td> | |||
| </tr> | |||
| </tbody> | |||
| @@ -99,8 +153,18 @@ | |||
| </div> | |||
| </div> | |||
| </kendo-pdf-export> | |||
| <div class="text-right mt-3"> <button type="button" class="btn btn-primary" (click)="pdf.saveAs('invoice.pdf')"><b><i class="fa fa-paper-plane-o mr-1"></i></b> Send invoice</button> </div> | |||
| <div class="card-footer"> <span class="text-muted">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.Duis aute irure dolor in reprehenderit</span> </div> | |||
| <div class="text-right mt-3"> | |||
| <button type="button" class="btn btn-primary" (click)="pdf.saveAs('invoice.pdf')"> | |||
| <b><i class="fa fa-paper-plane-o mr-1"></i></b> Download invoice</button> | |||
| <button type="button" class="btn btn-primary" (click)="onGotoRewardList()"> | |||
| <b><i class="fa fa-paper-plane-o mr-1"></i></b>Back to reward list</button> | |||
| </div> | |||
| <div class="card-footer"> | |||
| <span class="text-muted"> | |||
| While some lender can only provide residential home loans, At SuperCredit we have the experience and knoweldge in Residential, Commerical, Business & Development Finance. We will not only provide a lending solution for our customer but guide you through every single steps of the process. | |||
| </span> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -1,3 +1,4 @@ | |||
| div.InvoiceBody { | |||
| margin: 0; | |||
| font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | |||
| @@ -9,7 +10,7 @@ div.InvoiceBody { | |||
| background-color: darkgrey; | |||
| padding-top:20px; | |||
| padding-bottom:20px; | |||
| height:100%; | |||
| min-height:100%; | |||
| .container{ | |||
| padding:0; | |||
| @@ -17,6 +18,44 @@ div.InvoiceBody { | |||
| kendo-chip { | |||
| margin-left: 10px; | |||
| } | |||
| button { | |||
| color: #fff; | |||
| background-color: #ca974d; | |||
| border-color: white; | |||
| padding: 3px 11px 3px 3px; | |||
| border-radius: 20px; | |||
| margin-left: 10px; | |||
| } | |||
| div.select-beneficiary{ | |||
| width: 200px; | |||
| display: inline-block; | |||
| margin: 1px 1px 1px 10px; | |||
| border-left: 10px solid orange; | |||
| } | |||
| div.make-payment{ | |||
| display: inline-block; | |||
| vertical-align: bottom; | |||
| margin-left:20px; | |||
| button { | |||
| margin-left: 15px; | |||
| } | |||
| } | |||
| div.anchor{ | |||
| float: right; | |||
| background: yellow; | |||
| height: 10px; | |||
| right: 20px; | |||
| } | |||
| .hint { | |||
| animation: blink normal 0.5s infinite; | |||
| } | |||
| @keyframes blink { 50%{opacity:0} } | |||
| span.k-avatar-image.invoice-chip{ | |||
| display:inline-block; | |||
| width:20px; | |||
| @@ -112,7 +151,7 @@ div.InvoiceBody { | |||
| @media (min-width: 768px) { | |||
| .wmin-md-400 { | |||
| min-width: 400px !important | |||
| max-width: 400px !important | |||
| } | |||
| } | |||
| @@ -1,31 +1,166 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core'; | |||
| import {ChipRemoveEvent} from '@progress/kendo-angular-buttons'; | |||
| import {RewardService} from '../service/reward.service'; | |||
| import {RewardExModel} from '../models/reward-ex.model'; | |||
| import {PayOutExModel} from '../models/payout.ex.model'; | |||
| import {PayOutService} from '../service/payout.service'; | |||
| import {PeopleSelectComponent} from '../people-select/people-select.component'; | |||
| import {AppConfig} from '../app.config'; | |||
| import {UserExModel} from '../models/userExModel'; | |||
| import {SessionService} from '../service/session.service'; | |||
| import {Router} from '@angular/router'; | |||
| @Component({ | |||
| selector: 'app-pay-out-details', | |||
| templateUrl: './pay-out-details.component.html', | |||
| styleUrls: ['./pay-out-details.component.scss'] | |||
| }) | |||
| export class PayOutDetailsComponent implements OnInit { | |||
| sales = []; | |||
| export class PayOutDetailsComponent implements OnInit , AfterViewInit{ | |||
| @Input() Id = 0; | |||
| @Input() public model: PayOutExModel = new PayOutExModel(); | |||
| amount = 3218; | |||
| background = 'url(\'https://svr2021.lawipac.com:8080/api/v1/avatar/0\')'; | |||
| constructor() { } | |||
| @ViewChild('selectPeople', {static: false}) sp: PeopleSelectComponent; | |||
| subTotal = 0; | |||
| tax = 0; | |||
| Total = 0; | |||
| hintSelectRewardsButton = false; | |||
| hinted = false; | |||
| rewards: RewardExModel[] = []; | |||
| constructor(private rs: RewardService, private payOutService: PayOutService, | |||
| private cfg: AppConfig, private ss: SessionService, private router: Router) { } | |||
| ngOnInit(): void { | |||
| setInterval(() => { | |||
| this.amount += 1; | |||
| }, 1000); | |||
| if (this.Id > 0){ | |||
| this.payOutService.getPayOutEx(this.Id).subscribe( | |||
| resp => { | |||
| this.model = new PayOutExModel(resp); | |||
| this.rewards = this.model.Rewards; | |||
| this.updateTotal(this.model.Rewards); | |||
| }); | |||
| } | |||
| } | |||
| ngAfterViewInit(): void { | |||
| if ( this.Id === 0 ) { | |||
| this.sp.showDropDown(); | |||
| } | |||
| } | |||
| public photoBackground(id: string): string { | |||
| return 'url(' + this.cfg.getUrl('avatar/' + id) + ')'; | |||
| } | |||
| public onChangeRewardList( list: RewardExModel[]): void { | |||
| this.rewards = list; | |||
| this.model.Rewards = this.rewards; | |||
| this.payOutService.savePayOut(this.model.toPayOutModel()).subscribe( resp => { | |||
| this.model = new PayOutExModel(resp); | |||
| }); | |||
| this.updateTotal(list); | |||
| } | |||
| private updateTotal(list: RewardExModel[]): void { | |||
| this.Total = 0; | |||
| list.forEach( v => { | |||
| this.Total += v.Amount; | |||
| }); | |||
| this.subTotal = this.Total / 1.1; | |||
| this.tax = this.Total - this.subTotal; | |||
| } | |||
| public onEditBeneficiary(): void{ | |||
| this.sp.showDropDown(); | |||
| } | |||
| public onToChange(e: any): void { | |||
| if ( this.model.Rewards.length === 0 && this.hinted === false ){ | |||
| this.hintSelectRewardsButton = true; | |||
| this.hinted = true; | |||
| setTimeout( () => { | |||
| this.hintSelectRewardsButton = false; | |||
| }, 2000); | |||
| } | |||
| } | |||
| public onPrepared(): void { | |||
| this.model.Prepared.Id = this.ss.CurrentUser.Id; | |||
| this.payOutService.setPayOutPrepared(this.model.toPayOutModel()).subscribe( | |||
| resp => { | |||
| this.model.Prepared = new UserExModel(resp.Prepared); | |||
| this.model.Status = resp.Status; | |||
| } | |||
| ); | |||
| } | |||
| public onCancelPrepared(): void { | |||
| this.model.Prepared.Id = ''; | |||
| this.payOutService.setPayOutUnPrepared(this.model.toPayOutModel()).subscribe( | |||
| resp => { | |||
| this.model.Prepared = new UserExModel(resp.Prepared); | |||
| this.model.Status = resp.Status; | |||
| } | |||
| ); | |||
| } | |||
| public onApprove(): void{ | |||
| this.model.Approved.Id = this.ss.CurrentUser.Id; | |||
| this.payOutService.setPayOutApproved(this.model.toPayOutModel()).subscribe( | |||
| resp => { | |||
| this.model.Approved = new UserExModel(resp.Approved); | |||
| this.model.Status = resp.Status; | |||
| // prepare payment | |||
| if ( this.model.PayDate.getFullYear() <= 1900 ){ | |||
| this.model.PayDate = new Date(); | |||
| } | |||
| if (this.model.PayAmount === 0 && this.Total !== 0){ | |||
| this.model.PayAmount = this.subTotal; | |||
| } | |||
| } | |||
| ); | |||
| } | |||
| public onRevoke(): void { | |||
| this.payOutService.setPayOutUnapproved(this.model.toPayOutModel()).subscribe( | |||
| resp => { | |||
| this.model.Approved = new UserExModel(resp.Approved); | |||
| this.model.Status = resp.Status; | |||
| this.model.Paid.Id = ''; | |||
| this.model.PayDate = new Date('1900-01-01'); | |||
| this.model.PayAmount = 0; | |||
| } | |||
| ); | |||
| } | |||
| public onPaid(): void { | |||
| this.model.Paid.Id = this.ss.CurrentUser.Id; | |||
| this.payOutService.setPayOutPaid(this.model.toPayOutModel()).subscribe( | |||
| resp => { | |||
| this.model.Paid = new UserExModel(resp.Paid); | |||
| this.model.Status = resp.Status; | |||
| this.model.PayDate = new Date(resp.PayDate); | |||
| this.model.PayAmount = resp.PayAmount; | |||
| } | |||
| ); | |||
| } | |||
| range(start: number, end: number): number[] { | |||
| if (start === end) { return [start]; } | |||
| return [start, ...this.range(start + 1, end)]; | |||
| public onUnPaid(): void{ | |||
| this.payOutService.setPayOutUnpaid(this.model.toPayOutModel()).subscribe( | |||
| resp => { | |||
| this.model.Paid = new UserExModel(resp.Paid); | |||
| this.model.Status = resp.Status; | |||
| this.model.PayDate = new Date(resp.PayDate); | |||
| this.model.PayAmount = resp.PayAmount; | |||
| } | |||
| ); | |||
| } | |||
| onRemoveChip(s: string, e: ChipRemoveEvent): void { | |||
| console.log(s, e); | |||
| public onGotoRewardList(): void{ | |||
| this.router.navigate(['/list-all-rewards']).then(); | |||
| } | |||
| } | |||
| @@ -2,25 +2,25 @@ | |||
| #list | |||
| [data]="searchResult" | |||
| [textField]="'Display'" | |||
| [valueField]="getEffectiveField()" | |||
| [valueField]="'Id'" | |||
| [valuePrimitive]="true" | |||
| [listHeight]="300" | |||
| placeholder="Type here to search" | |||
| [placeholder]="placeholder" | |||
| [suggest]="true" | |||
| [filterable]="true" | |||
| [clearButton]="true" | |||
| [allowCustom]="false" | |||
| [virtual]="{itemHeight: 36}" | |||
| [formControl]="formControl" | |||
| (filterChange)="filterChange($event)" | |||
| (valueChange)="valueChange($event)" | |||
| (valueChange)="onSelectionChange($event)" | |||
| > | |||
| <kendo-combobox-column | |||
| [field]="'Display'" | |||
| [title]="'Contact Name'" | |||
| [width]="width" | |||
| > | |||
| <ng-template | |||
| kendoMultiColumnComboBoxColumnCellTemplate | |||
| @@ -1,11 +1,9 @@ | |||
| import {Component, forwardRef, Input, OnInit, ViewChild} from '@angular/core'; | |||
| import {Component, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild} from '@angular/core'; | |||
| import {AuthService} from '../service/auth.service'; | |||
| import { debounce } from 'ts-debounce'; | |||
| import {PeopleModel} from '../models/people.model'; | |||
| import {ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR} from '@angular/forms'; | |||
| import {MultiColumnComboBoxComponent} from '@progress/kendo-angular-dropdowns'; | |||
| import {Observable} from 'rxjs'; | |||
| import {PeopleService} from '../service/people.service'; | |||
| import {UserExModel} from '../models/userExModel'; | |||
| @Component({ | |||
| selector: 'app-people-select', | |||
| @@ -22,61 +20,53 @@ import {PeopleService} from '../service/people.service'; | |||
| export class PeopleSelectComponent implements OnInit, ControlValueAccessor { | |||
| @Input() disabled = false; | |||
| @Input() translateId: true; // always tralsnate user Id, because name is not unique | |||
| @Input() width: number; | |||
| @Input() initSearch: PeopleModel[]; | |||
| @Input() placeholder: string; | |||
| @Input() searchWithin: UserExModel[] = []; | |||
| @Input() formControl: FormControl = new FormControl(); // this is a dummy place holder | |||
| @Output() valueChange: EventEmitter<UserExModel> = new EventEmitter<UserExModel>(); | |||
| @ViewChild('list', {static: true}) public text: MultiColumnComboBoxComponent; | |||
| public searchResult: PeopleModel[] = []; | |||
| public searchResult: UserExModel[] = []; | |||
| public value = ''; // selecting the default and only empty contact element // TODO: remove ngModel | |||
| public total = 0; | |||
| private debounceFilter: any ; | |||
| // Function to call when the rating changes. | |||
| private onChange = ( nameOrId: string) => {}; | |||
| private onChange = ( nameOrId: UserExModel) => {}; | |||
| // Function to call when the input is touched (when a star is clicked). | |||
| private onTouched = () => {}; | |||
| constructor(private auth: AuthService, private ps: PeopleService) { } | |||
| ngOnInit(): void { | |||
| this.prepareSearchPeople(); | |||
| this.initSearchResult(); | |||
| } | |||
| private initSearchResult(): void { | |||
| this.initSearch.forEach( v => { | |||
| this.searchResult.push(v); | |||
| }); | |||
| this.total = this.initSearch.length; | |||
| } | |||
| private prepareSearchPeople(): void{ | |||
| this.debounceFilter = debounce( (filter: string): void => { | |||
| this.ps.getPeopleList(filter).subscribe( | |||
| resp => { | |||
| this.text.loading = false; | |||
| this.searchResult = resp.List; | |||
| this.total = resp.Count; | |||
| }, | |||
| error => { this.text.loading = false; }, | |||
| () => { this.text.loading = false; } | |||
| ); | |||
| }, 500) ; | |||
| this.loadFilteredPeopleList(''); // load every one | |||
| } | |||
| getEffectiveField(): string { | |||
| return this.translateId ? 'Id' : 'Display'; | |||
| private loadFilteredPeopleList( filter: string): void { | |||
| this.text.loading = true; | |||
| this.ps.getUserExList(filter).subscribe( | |||
| resp => { | |||
| this.text.loading = false; | |||
| this.searchWithin = resp.List; | |||
| this.total = resp.Count; | |||
| this.searchResult = this.searchWithin; | |||
| }, | |||
| error => { this.text.loading = false; }, | |||
| () => { this.text.loading = false; } | |||
| ); | |||
| } | |||
| filterChange(filter: string): void { | |||
| if ( filter.length > 0 ) { | |||
| this.text.loading = true; | |||
| this.debounceFilter(filter); // conduct search even if filter is empty | |||
| } | |||
| this.searchResult = []; | |||
| this.searchWithin.forEach( v => { | |||
| if ( filter === '' ){ | |||
| this.searchResult.push(v); | |||
| } else if ( v.Id === filter) { | |||
| this.searchResult.push (v); | |||
| } else if ( v.Display.includes( filter ) ){ | |||
| this.searchResult.push(v); | |||
| } | |||
| }); | |||
| } | |||
| public getContactImageUrl(contactId: string): string { | |||
| @@ -84,133 +74,25 @@ export class PeopleSelectComponent implements OnInit, ControlValueAccessor { | |||
| } | |||
| // ComboBox emit event on value change | |||
| public valueChange(str: string): void { | |||
| return; | |||
| console.log('value change', str); | |||
| this.onChange(this.value); | |||
| } | |||
| // public selectionChange(value: PeopleModel): void { | |||
| // this.onTouched(); | |||
| // console.log('selectionChange', value); | |||
| // this.value = this.translateId ? value.Id : value.FullName; | |||
| // this.onChange(this.value); | |||
| // } | |||
| // public open(): void { | |||
| // this.onTouched(); | |||
| // // console.log('open', this.text); | |||
| // } | |||
| // | |||
| // public close(): void { | |||
| // this.onTouched(); | |||
| // // console.log('close', this.text); | |||
| // } | |||
| // | |||
| // public focus(): void { | |||
| // this.onTouched(); | |||
| // // console.log('focus', this.text); | |||
| // } | |||
| // | |||
| // public blur(): void { | |||
| // // console.log('blur', this.text); | |||
| // } | |||
| // Allows Angular to update the model (name or ID). | |||
| // Update the model and changes needed for the view here. | |||
| writeValue(nameOrId: string): void { | |||
| if ( nameOrId === undefined ){ | |||
| console.log('who called me for write', this); | |||
| return; | |||
| } | |||
| const changed = nameOrId !== this.value; | |||
| if (this.needSearch(nameOrId) ){ | |||
| this.text.loading = true; | |||
| console.log('searching ... ', nameOrId, this.translateId ? 'by id' : 'by name'); | |||
| this.searchUser(nameOrId, this.translateId).subscribe( | |||
| ppl => { | |||
| const person = new PeopleModel(ppl); | |||
| console.log('got search result ', person); | |||
| this.searchResult.push(person); // make sure it's available for selection, thus proper display | |||
| this.value = this.translateId ? person.Id : person.FullName; | |||
| console.log('before update', this.text.value); | |||
| this.text.value = this.value; | |||
| console.log('after update', this.text.value); | |||
| }, error => { console.error(error); this.text.loading = false; }, | |||
| () => {this.text.loading = false; } | |||
| ); | |||
| if ( changed ) { | |||
| this.onChange(nameOrId); | |||
| } | |||
| } | |||
| } | |||
| needSearch(incoming: string ): boolean { | |||
| if ( incoming === undefined || incoming === '' || incoming === this.value ) { | |||
| return false; | |||
| public onSelectionChange(str: string): void { | |||
| const p = this.searchWithin.find( v => v.Id === str); | |||
| if ( p ){ | |||
| this.onChange(p); | |||
| } | |||
| const idx = this.searchResult.findIndex( person => { | |||
| if ( this.translateId) { | |||
| return person.Id === incoming; | |||
| }else{ | |||
| return person.FullName.includes(incoming); | |||
| } | |||
| }); | |||
| return idx === -1; // not found need search | |||
| } | |||
| // Search a user either based on partial name or a complete ID | |||
| searchUser(nameOrId: string, translateId: boolean): Observable<PeopleModel>{ | |||
| if ( translateId ) { | |||
| return this.ps.getPeopleById(nameOrId); | |||
| // return this.searchPersonById( nameOrId) ; | |||
| writeValue(inV: UserExModel): void { | |||
| console.log( ' value in', inV); | |||
| if (inV){ | |||
| this.value = inV.Id; | |||
| }else{ | |||
| // return this.ps.searchById(nameOrId); | |||
| // return this.searchPersonByName(nameOrId) ; | |||
| this.value = null; | |||
| } | |||
| } | |||
| // searchPersonByName(name: string): Observable<PeopleModel>{ | |||
| // return new Observable ( observer => { | |||
| // const dummy: PeopleModel = new PeopleModel( | |||
| // 'dummy-id', | |||
| // 'FSearch', | |||
| // 'LResult', | |||
| // '', | |||
| // 'Mr.', | |||
| // 'Display Name', | |||
| // 'Nick Name' | |||
| // ); | |||
| // setTimeout(() => { | |||
| // observer.next(dummy); | |||
| // observer.complete(); | |||
| // }, 1000); | |||
| // }); | |||
| // } | |||
| // | |||
| // searchPersonById( id: string): Observable<PeopleModel> { | |||
| // return new Observable ( observer => { | |||
| // const dummy: PeopleModel = new PeopleModel( | |||
| // id, | |||
| // 'FSearch', | |||
| // 'LResult', | |||
| // '', | |||
| // 'Mr.', | |||
| // 'P:' + id, | |||
| // 'Nick Name' | |||
| // ); | |||
| // setTimeout(() => { | |||
| // observer.next(dummy); | |||
| // observer.complete(); | |||
| // }, 1000); | |||
| // }); | |||
| // } | |||
| // Allows Angular to register a function to call when the model (rating) changes. | |||
| // Save the function as a property to call later here. | |||
| registerOnChange(fn: (nameOrId: string) => void): void { | |||
| registerOnChange(fn: (nameOrId: UserExModel) => void): void { | |||
| this.onChange = fn; | |||
| } | |||
| // Allows Angular to register a function to call when the input has been touched. | |||
| @@ -223,4 +105,7 @@ export class PeopleSelectComponent implements OnInit, ControlValueAccessor { | |||
| this.disabled = isDisabled; | |||
| } | |||
| public showDropDown(): void{ | |||
| this.text.toggle(true); | |||
| } | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| <div class="box"> | |||
| <app-pay-out-details></app-pay-out-details> | |||
| <app-pay-out-details [Id]="Id"></app-pay-out-details> | |||
| </div> | |||
| @@ -1,4 +1,5 @@ | |||
| import { Component, OnInit } from '@angular/core'; | |||
| import {ActivatedRoute, Router} from '@angular/router'; | |||
| @Component({ | |||
| selector: 'app-reward-paid', | |||
| @@ -6,11 +7,11 @@ import { Component, OnInit } from '@angular/core'; | |||
| styleUrls: ['./reward-paid.component.scss'] | |||
| }) | |||
| export class RewardPaidComponent implements OnInit { | |||
| constructor() { } | |||
| Id = 0; | |||
| constructor(private actRoute: ActivatedRoute) { } | |||
| ngOnInit(): void { | |||
| this.Id = this.actRoute.snapshot.params.id; | |||
| } | |||
| } | |||
| @@ -8,6 +8,10 @@ | |||
| (click)="onToggle()" | |||
| ></button> | |||
| <div class='popup-content'> | |||
| <app-list-all-rewards></app-list-all-rewards> | |||
| <app-list-all-rewards (confirmSelection)="onConfirmSelection($event)" | |||
| [selectedRewards]="selectedRewards" | |||
| [showNonPaidOnly]="showNonPaidOnly" | |||
| > | |||
| </app-list-all-rewards> | |||
| </div> | |||
| </kendo-popup> | |||
| @@ -2,6 +2,7 @@ import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angula | |||
| import {ListAllLoansComponent} from '../list-all-loans/list-all-loans.component'; | |||
| import {LoanModel} from '../models/loan.model'; | |||
| import {RewardModel} from '../models/reward.model'; | |||
| import {RewardExModel} from '../models/reward-ex.model'; | |||
| @Component({ | |||
| selector: 'app-reward-select', | |||
| @@ -12,8 +13,10 @@ export class RewardSelectComponent implements OnInit { | |||
| // @ViewChild('list', {static: false} ) list: list-all; | |||
| @Input() Max = 1; // we do not support multiple loan selection at the moment | |||
| @Input() readOnly = false; | |||
| @Output() valueChange: EventEmitter<RewardModel[]> = new EventEmitter<RewardModel[]>(); | |||
| public Rewards: RewardModel[]; | |||
| @Input() showNonPaidOnly = false; | |||
| @Output() valueChange: EventEmitter<RewardExModel[]> = new EventEmitter<RewardExModel[]>(); | |||
| @Input() public selectedRewards: RewardExModel[] = []; | |||
| // popup | |||
| public showPopup = false; | |||
| @@ -27,4 +30,24 @@ export class RewardSelectComponent implements OnInit { | |||
| public onToggle(): void { | |||
| this.showPopup = !this.showPopup; | |||
| } | |||
| public onConfirmSelection(sel: RewardExModel[]): void { | |||
| if (this.isDiff(this.selectedRewards, sel)) { | |||
| this.valueChange.emit (sel); | |||
| this.selectedRewards = sel; | |||
| console.log('reward-select', this.selectedRewards); | |||
| } | |||
| this.showPopup = !this.showPopup; | |||
| } | |||
| private isDiff(a: RewardExModel[], b: RewardExModel[]): boolean { | |||
| if ( !a || !b ) { return a !== b; } | |||
| if ( a.length !== b.length) { return true; } | |||
| for ( let i = 0; i < a.length; i++ ){ | |||
| if ( ! a[i].Equal(b[i]) ){ | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| } | |||
| @@ -4,16 +4,45 @@ import {AuthService} from './auth.service'; | |||
| import {Observable} from 'rxjs'; | |||
| import {PayOutExModel} from '../models/payout.ex.model'; | |||
| import {CompositeFilterDescriptor} from '@progress/kendo-data-query'; | |||
| import {PayoutModel} from '../models/payout.model'; | |||
| @Injectable({providedIn: 'root'}) | |||
| export class PayOutService { | |||
| constructor(private http: HttpClient, private auth: AuthService ){ } | |||
| public getPayOutEx(id: string): Observable<PayOutExModel> { | |||
| public getPayOutEx(id: number): Observable<PayOutExModel> { | |||
| return this.http.get<PayOutExModel>(this.auth.getUrl('payout-ex/' + id)); | |||
| } | |||
| public getPayOutList(filter: CompositeFilterDescriptor): Observable<PayOutExModel[]> { | |||
| return this.http.post<PayOutExModel[]>(this.auth.getUrl('payout-ex-list/'), filter); | |||
| } | |||
| public savePayOut(po: PayoutModel): Observable<PayOutExModel>{ | |||
| return this.http.post<PayOutExModel>(this.auth.getUrl('payout/'), po); | |||
| } | |||
| public setPayOutPrepared(po: PayoutModel): Observable<PayOutExModel>{ | |||
| return this.http.post<PayOutExModel>(this.auth.getUrl('payout-prepared/'), po); | |||
| } | |||
| public setPayOutUnPrepared( po: PayoutModel): Observable<PayOutExModel>{ | |||
| return this.http.post<PayOutExModel>(this.auth.getUrl('payout-unprepared/'), po); | |||
| } | |||
| public setPayOutApproved(po: PayoutModel): Observable<PayOutExModel>{ | |||
| return this.http.post<PayOutExModel>(this.auth.getUrl('payout-approved/' ), po); | |||
| } | |||
| public setPayOutUnapproved(po: PayoutModel): Observable<PayOutExModel>{ | |||
| return this.http.post<PayOutExModel>(this.auth.getUrl('payout-unapproved/'), po); | |||
| } | |||
| public setPayOutPaid( po: PayoutModel): Observable<PayOutExModel>{ | |||
| return this.http.post<PayOutExModel>(this.auth.getUrl('payout-paid/' ), po); | |||
| } | |||
| public setPayOutUnpaid(po: PayoutModel): Observable<PayOutExModel>{ | |||
| return this.http.post<PayOutExModel>(this.auth.getUrl('payout-unpaid/'), po); | |||
| } | |||
| } | |||
| @@ -7,6 +7,7 @@ import {BrokerModel} from '../models/broker.model'; | |||
| import {LoanModel} from '../models/loan.model'; | |||
| import {ChangePassword} from '../models/change-password.model'; | |||
| import {UserExtraModel} from '../models/user-extra.model'; | |||
| import {UserExModel} from '../models/userExModel'; | |||
| @Injectable({providedIn: 'root'}) | |||
| export class PeopleService { | |||
| @@ -24,6 +25,11 @@ export class PeopleService { | |||
| return this.http.get<{Count: number, List: PeopleModel[]}>(this.auth.getUrl( 'people-list/'), { params}); | |||
| } | |||
| public getUserExList(filter: string): Observable<{Count: number, List: UserExModel[]}> { | |||
| const params = new HttpParams().set('filter', filter); | |||
| return this.http.get<{Count: number, List: UserExModel[]}>(this.auth.getUrl( 'user-ex-list/'), { params}); | |||
| } | |||
| public getBrokerList(filter: string): Observable<{Count: number, List: BrokerModel[]}> { | |||
| const params = new HttpParams().set('filter', filter); | |||
| return this.http.get<{Count: number, List: BrokerModel[]}>(this.auth.getUrl( 'broker-list/'), { params}); | |||
| @@ -0,0 +1,35 @@ | |||
| import {Injectable} from '@angular/core'; | |||
| import {RewardExModel} from '../models/reward-ex.model'; | |||
| import {Observable} from 'rxjs'; | |||
| import {HttpClient} from '@angular/common/http'; | |||
| import {AppConfig} from '../app.config'; | |||
| import {DataStateChangeEvent} from '@progress/kendo-angular-grid'; | |||
| import {RewardExListResultModel} from '../models/reward-ex-list-result.model'; | |||
| import {State} from '@progress/kendo-data-query'; | |||
| @Injectable({providedIn: 'root'}) | |||
| export class RewardService { | |||
| constructor(private http: HttpClient, private cfg: AppConfig) { | |||
| } | |||
| public getRewardExListByPayoutId(payOutId: number): Observable<RewardExListResultModel>{ | |||
| const state = { | |||
| skip: 0, take: 0, filter: { | |||
| logic: 'and', | |||
| filters: [ | |||
| {field: 'PayOutId', operator: 'eq', value: payOutId} | |||
| ] | |||
| } | |||
| }; | |||
| return this.getRewardExList(state as State); | |||
| } | |||
| public getUserReward(userid?: string): Observable<RewardExModel[]>{ | |||
| if (! userid ) { userid = ''; } | |||
| return this.http.get<RewardExModel[]>(this.cfg.getUrl('user-reward/' + userid)); | |||
| } | |||
| public getRewardExList(state: State): Observable<RewardExListResultModel> { | |||
| return this.http.post<RewardExListResultModel>(this.cfg.getUrl('reward-ex-list/'), state); | |||
| } | |||
| } | |||
| @@ -1,3 +1,29 @@ | |||
| <kendo-grid [scrollable]="false"> | |||
| <kendo-grid [resizable]="true" | |||
| [scrollable]="'none'" | |||
| [data]="items"> | |||
| <kendo-grid-column field="ToDisplay" title="Beneficiary" width="100"> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Description"> | |||
| <ng-template kendoGridCellTemplate let-dataItem> | |||
| <div class="invoice-item-description"> | |||
| <span class="label"> For: </span> <span class="beneficiary"> {{dataItem.Description }} </span> | |||
| <p class="description" *ngIf="dataItem.LoanId !== '' "> | |||
| <span class="label"> Regarding Loan: </span> | |||
| <span class="regarding"> {{dataItem.Item}}, </span> | |||
| </p> | |||
| <span class="label"> Issued on : </span> | |||
| <span class="issued-on-date">{{dataItem.Ts | date:"yyyy-MMMM-dd" }} </span> | |||
| <span *ngIf="dataItem.FromId !== '0' && dataItem.FromId !== '' " class="money-giver"> | |||
| From: {{dataItem.From.Display}} | |||
| </span> | |||
| <sub > * SFM Reference: {{ dataItem.Id}} *</sub> | |||
| </div> | |||
| </ng-template> | |||
| </kendo-grid-column> | |||
| <kendo-grid-column field="Amount" format="{0:c}" width="200"></kendo-grid-column> | |||
| </kendo-grid> | |||
| @@ -0,0 +1,24 @@ | |||
| div.invoice-item-description{ | |||
| width: 100%; | |||
| p.description{ | |||
| margin-bottom: 1px; | |||
| } | |||
| span.label { | |||
| font-family: "Courier New"; | |||
| color: #ab2828 | |||
| } | |||
| span.beneficiary{ | |||
| font-weight: bold; | |||
| color: black; | |||
| } | |||
| span.regarding{ | |||
| font-family: "Courier New"; | |||
| } | |||
| span.issued-on-date { | |||
| font-family: "Courier New"; | |||
| color: darkgrey; | |||
| } | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| import {Component, Input, OnInit} from '@angular/core'; | |||
| import {Component, Input, OnChanges, OnInit} from '@angular/core'; | |||
| import {RewardModel} from '../models/reward.model'; | |||
| @Component({ | |||
| @@ -6,12 +6,15 @@ import {RewardModel} from '../models/reward.model'; | |||
| templateUrl: './single-payout-rewards-list.component.html', | |||
| styleUrls: ['./single-payout-rewards-list.component.scss'] | |||
| }) | |||
| export class SinglePayoutRewardsListComponent implements OnInit { | |||
| export class SinglePayoutRewardsListComponent implements OnInit, OnChanges { | |||
| @Input() items: RewardModel[] = []; | |||
| constructor() { } | |||
| ngOnInit(): void { | |||
| // this.items.push(new RewardModel({})); | |||
| } | |||
| ngOnChanges(changes): void { | |||
| // console.log(changes, this.items); | |||
| } | |||
| } | |||