You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

134 lines
4.2 KiB

import { FamilyOfficeDataService } from '@ghostfolio/client/services/family-office-data.service';
import { K1ImportDataService } from '@ghostfolio/client/services/k1-import-data.service';
import { K1AggregationResult } from '@ghostfolio/common/interfaces/k1-import.interface';
import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
DestroyRef,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon';
import { MatTableModule } from '@angular/material/table';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page' },
imports: [
CommonModule,
MatButtonModule,
MatCardModule,
MatChipsModule,
MatIconModule,
MatTableModule,
RouterModule
],
selector: 'gf-k-document-detail',
styleUrls: ['./k-document-detail.scss'],
templateUrl: './k-document-detail.html'
})
export class KDocumentDetailComponent implements OnInit {
public aggregations: K1AggregationResult[] = [];
public boxColumns = ['boxNumber', 'value'];
public boxData: Array<{ boxNumber: string; value: number | string | null }> = [];
public error: string | null = null;
public kDocument: any = null;
public kDocumentId: string;
/** Box numbers that represent percentage values (Section J) */
private static readonly PERCENTAGE_BOXES = new Set([
'J_PROFIT_BEGIN', 'J_PROFIT_END',
'J_LOSS_BEGIN', 'J_LOSS_END',
'J_CAPITAL_BEGIN', 'J_CAPITAL_END'
]);
public isPercentage(boxNumber: string): boolean {
return KDocumentDetailComponent.PERCENTAGE_BOXES.has(boxNumber);
}
public isNumeric(value: any): boolean {
return typeof value === 'number';
}
public constructor(
private readonly activatedRoute: ActivatedRoute,
private readonly changeDetectorRef: ChangeDetectorRef,
private readonly destroyRef: DestroyRef,
private readonly familyOfficeDataService: FamilyOfficeDataService,
private readonly k1ImportDataService: K1ImportDataService,
private readonly router: Router
) {}
public ngOnInit(): void {
this.kDocumentId = this.activatedRoute.snapshot.paramMap.get('id') || '';
if (this.kDocumentId) {
this.loadKDocument();
this.loadAggregations();
}
}
public goBack(): void {
this.router.navigate(['/k-documents']);
}
private loadKDocument(): void {
this.familyOfficeDataService
.fetchKDocuments()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: (docs) => {
this.kDocument = docs.find((d) => d.id === this.kDocumentId) || null;
if (this.kDocument?.data) {
const data = this.kDocument.data as Record<string, any>;
this.boxData = Object.entries(data)
.map(([boxNumber, value]) => ({
boxNumber,
value: value ?? null
}))
.sort((a, b) => this.compareBoxNumbers(a.boxNumber, b.boxNumber));
}
this.changeDetectorRef.markForCheck();
},
error: () => {
this.error = 'Failed to load K-Document.';
this.changeDetectorRef.markForCheck();
}
});
}
private loadAggregations(): void {
this.k1ImportDataService
.computeAggregations({ kDocumentId: this.kDocumentId })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: (aggregations) => {
this.aggregations = aggregations;
this.changeDetectorRef.markForCheck();
},
error: () => {
// Aggregations may not be configured yet
this.aggregations = [];
this.changeDetectorRef.markForCheck();
}
});
}
private compareBoxNumbers(a: string, b: string): number {
const numA = parseInt(a.replace(/[^0-9]/g, ''), 10) || 0;
const numB = parseInt(b.replace(/[^0-9]/g, ''), 10) || 0;
if (numA !== numB) {
return numA - numB;
}
return a.localeCompare(b);
}
}