178 lines
6.0 KiB
TypeScript
Executable File
178 lines
6.0 KiB
TypeScript
Executable File
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');
|
||
}
|
||
} |