arti-angular-app/src/app/asset-calculator/asset-calculator.component.ts

178 lines
6.0 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Component, signal, effect, computed } from '@angular/core';
import { DecimalPipe} from '@angular/common';
import { Asset, AssetPlanPosition, TypeDepreciation, YearMonth, AssetLifeChange, AssetDepreciationMethod } from '../assets/asset';
import { AssetService } from '../assets/service/asset.service';
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { FormsModule } from '@angular/forms';
import { MatDatepicker, MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule } from '@angular/material/core';
import { MatInputModule } from '@angular/material/input';
import { format, parse } from 'date-fns';
import { createHash } from 'crypto';
class AssetLifeChangeWrapper {
when = signal<string>(YearMonth.todayTxt());
initial = signal<number>(0);
constructor(assetLifeChange: AssetLifeChange) {
this.set(assetLifeChange);
}
get(): AssetLifeChange {
return new AssetLifeChange(YearMonth.from(this.when()), this.initial(), 0, 0);
}
set(assetLifeChange: AssetLifeChange) {
this.when.set(YearMonth.toTxt(assetLifeChange.when));
this.initial.set(assetLifeChange.initial);
}
}
const STORAGE_KEY = 'assetForCalculator';
const CACHE_KEY = 'cachedResults';
const HASH_KEY = 'assetHash';
@Component({
selector: 'app-asset-calculator',
standalone: true,
imports: [DecimalPipe, TranslateModule, FormsModule, MatDatepickerModule, MatNativeDateModule, MatInputModule],
providers: [MatDatepickerModule, MatNativeDateModule],
templateUrl: "asset-calculator.component.html",
styleUrls: ['asset-calculator.component.css']
})
export class AssetCalculatorComponent {
TypeDepreciation = TypeDepreciation;
initialValueAsset = signal<number>(6000);
rate = signal<number>(20);
startDepreciation = signal<Date>(new Date());
typeDepreciation = signal<TypeDepreciation>(TypeDepreciation.linear);
factor = signal<number>(2);
lifeChangesSignal = signal<AssetLifeChangeWrapper[]>([]);
amortizationsSignal = signal<AssetPlanPosition[]>([]);
constructor(private assetService: AssetService, private translate: TranslateService) {}
ngOnInit(): void {
this.restoreState();
}
ngOnDestroy(): void {
this.saveState(this.controlsToAsset());
}
private restoreState() {
const savedAsset = localStorage.getItem(STORAGE_KEY);
const savedResults = localStorage.getItem(CACHE_KEY);
const savedHash = localStorage.getItem(HASH_KEY);
if (savedAsset) {
const asset: Asset = JSON.parse(savedAsset);
this.assetToControls(asset);
// Generujemy nowy hash
const newHash = this.generateHash(asset);
// Jeśli hash jest taki sam, ładujemy wyniki z cache
if (savedHash === newHash && savedResults) {
this.amortizationsSignal.set(JSON.parse(savedResults));
console.log("✅ Załadowano wyniki z cache brak przeliczeń.");
return;
}
console.log("🔄 Hash zmieniony wykonuję przeliczenie.");
this.reCalculate();
}
}
private saveState(asset: Asset) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(asset));
localStorage.setItem(HASH_KEY, this.generateHash(asset)); // Zapisujemy nowy hash
}
private assetToControls(asset: Asset) {
if (!asset.life.length) return;
const [firstLife, ...restLives] = asset.life;
const method = asset.depreciationMethods[0];
this.initialValueAsset.set(firstLife.initial);
this.startDepreciation.set(YearMonth.toDate(firstLife.when));
this.rate.set(method.rate);
this.typeDepreciation.set(method.type);
this.factor.set(method.factor);
this.lifeChangesSignal.set(restLives.map(life => new AssetLifeChangeWrapper(life)));
}
private controlsToAsset(): Asset {
const when = YearMonth.fromDate(this.startDepreciation());
const asset = new Asset(when);
const method = new AssetDepreciationMethod(when.year, this.rate(), this.typeDepreciation(), this.factor());
asset.addMethod(method);
asset.addChange(new AssetLifeChange(when, this.initialValueAsset(), 0, 0));
this.lifeChangesSignal().forEach(lc => asset.addChange(lc.get()));
return asset;
}
private calculateToValues(positions: AssetPlanPosition[]) {
let sum = 0, sumThisYear = 0;
positions.forEach(pos => {
pos.calculatedDepreciation *= 0.01;
sum += pos.calculatedDepreciation;
pos.sum = sum;
sumThisYear = pos.when.month === 1 ? pos.calculatedDepreciation : sumThisYear + pos.calculatedDepreciation;
pos.sumThisYear = sumThisYear;
});
// Cache wyników
localStorage.setItem(CACHE_KEY, JSON.stringify(positions));
}
private reCalculate() {
this.calculateAmortizationsForAsset(this.controlsToAsset());
}
private calculateAmortizationsForAsset(asset: Asset) {
this.assetService.calculate(asset).subscribe(positions => {
this.calculateToValues(positions);
this.amortizationsSignal.set(positions);
});
}
clazz(pos: AssetPlanPosition) {
return pos.when.year % 2 === 0 ? "table-light" : "table-dark";
}
addLifeChange(amount = 1000) {
const change = new AssetLifeChange(YearMonth.fromDate(this.startDepreciation()), amount, 0, 0);
this.lifeChangesSignal.update(changes => [...changes, new AssetLifeChangeWrapper(change)]);
}
removeLifeChange(index: number) {
this.lifeChangesSignal.update(changes => changes.filter((_, i) => i !== index));
}
updateDateFromInput(inputValue: string) {
this.startDepreciation.set(parse(inputValue, 'yyyy-MM', new Date()));
}
onMonthSelected(event: Date, datepicker: any) {
this.startDepreciation.set(event);
datepicker.close();
}
private generateHash(asset: Asset): string {
const data = JSON.stringify({
initialValue: asset.life[0]?.initial,
rate: asset.depreciationMethods[0]?.rate,
type: asset.depreciationMethods[0]?.type,
factor: asset.depreciationMethods[0]?.factor,
lifeChanges: asset.life.map(lc => ({ when: lc.when, initial: lc.initial }))
});
return createHash('sha256').update(data).digest('hex');
}
}