hash from ChatGpt
This commit is contained in:
parent
c2d310ab62
commit
8569fc5310
|
|
@ -7,233 +7,172 @@ import { FormsModule } from '@angular/forms';
|
||||||
import { MatDatepicker, MatDatepickerModule } from '@angular/material/datepicker';
|
import { MatDatepicker, MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
import { MatNativeDateModule } from '@angular/material/core';
|
import { MatNativeDateModule } from '@angular/material/core';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { format, parse, addMonths, isAfter } from 'date-fns';
|
import { format, parse } from 'date-fns';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
|
||||||
|
|
||||||
interface FormValues {
|
class AssetLifeChangeWrapper {
|
||||||
initialValueAsset:number;
|
when = signal<string>(YearMonth.todayTxt());
|
||||||
rate: number;
|
initial = signal<number>(0);
|
||||||
startDepreciation: Date;
|
|
||||||
typeDepreciation: TypeDepreciation;
|
|
||||||
factor: number;
|
|
||||||
}
|
|
||||||
class AssetLifeChangeWrapper{
|
|
||||||
|
|
||||||
when = signal<string> ( YearMonth.todayTxt());
|
constructor(assetLifeChange: AssetLifeChange) {
|
||||||
initial = signal<number>( 0 );
|
|
||||||
|
|
||||||
constructor( assetLifeChange : AssetLifeChange ){
|
|
||||||
this.set(assetLifeChange);
|
this.set(assetLifeChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(): AssetLifeChange {
|
get(): AssetLifeChange {
|
||||||
const ym = this.when();
|
return new AssetLifeChange(YearMonth.from(this.when()), this.initial(), 0, 0);
|
||||||
const when = YearMonth.from( ym );
|
|
||||||
return new AssetLifeChange( when, this.initial() , 0, 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set(assetLifeChange: AssetLifeChange) {
|
set(assetLifeChange: AssetLifeChange) {
|
||||||
this.when.set(YearMonth.toTxt(assetLifeChange.when));
|
this.when.set(YearMonth.toTxt(assetLifeChange.when));
|
||||||
this.initial.set(assetLifeChange.initial);
|
this.initial.set(assetLifeChange.initial);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
const NAME_IN_STORAGE = 'assetForCalculator';
|
|
||||||
|
const STORAGE_KEY = 'assetForCalculator';
|
||||||
|
const CACHE_KEY = 'cachedResults';
|
||||||
|
const HASH_KEY = 'assetHash';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-asset-calculator',
|
selector: 'app-asset-calculator',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [DecimalPipe, TranslateModule, FormsModule, MatDatepickerModule, MatNativeDateModule, MatInputModule],
|
imports: [DecimalPipe, TranslateModule, FormsModule, MatDatepickerModule, MatNativeDateModule, MatInputModule],
|
||||||
providers: [
|
providers: [MatDatepickerModule, MatNativeDateModule],
|
||||||
MatDatepickerModule,
|
|
||||||
MatNativeDateModule
|
|
||||||
],
|
|
||||||
templateUrl: "asset-calculator.component.html",
|
templateUrl: "asset-calculator.component.html",
|
||||||
styleUrls: ['asset-calculator.component.css']
|
styleUrls: ['asset-calculator.component.css']
|
||||||
})
|
})
|
||||||
export class AssetCalculatorComponent {
|
export class AssetCalculatorComponent {
|
||||||
|
|
||||||
TypeDepreciation = TypeDepreciation;
|
TypeDepreciation = TypeDepreciation;
|
||||||
|
|
||||||
// Signals for form state and amortizations
|
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[]>([]);
|
||||||
|
|
||||||
initialValueAsset = signal <number> ( 6000 );
|
constructor(private assetService: AssetService, private translate: TranslateService) {}
|
||||||
rate = signal <number> ( 20 );
|
|
||||||
startDepreciation = signal <Date> ( new Date() );
|
|
||||||
typeDepreciation = signal <TypeDepreciation>( TypeDepreciation.linear );
|
|
||||||
factor = signal <number>( 2 );
|
|
||||||
|
|
||||||
formValues = computed<FormValues>(() => {
|
ngOnInit(): void {
|
||||||
return {
|
|
||||||
initialValueAsset: this.initialValueAsset(),
|
|
||||||
rate: this.rate(),
|
|
||||||
startDepreciation: this.startDepreciation(),
|
|
||||||
typeDepreciation: this.typeDepreciation(),
|
|
||||||
factor: this.factor(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
lifeChangesSignal = signal<AssetLifeChangeWrapper[]>([]); // Replaces `lifeFormArray`
|
|
||||||
|
|
||||||
amortizationsSignal =signal<AssetPlanPosition[]>([]);
|
|
||||||
|
|
||||||
constructor( private assetService: AssetService,
|
|
||||||
private translate : TranslateService ) {
|
|
||||||
// Effect to recalculate when form values change
|
|
||||||
effect( () => this.reCalculate( ) )
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private storage = {
|
|
||||||
load: (key: string) => {
|
|
||||||
try {
|
|
||||||
const data = localStorage.getItem(key);
|
|
||||||
return data ? JSON.parse(data) : null;
|
|
||||||
} catch {
|
|
||||||
console.error(`Failed to load ${key} from localStorage`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
save: (key: string, data: any) => {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(key, JSON.stringify(data));
|
|
||||||
} catch {
|
|
||||||
console.error(`Failed to save ${key} to localStorage`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ngOnInit(): void{
|
|
||||||
this.restoreState();
|
this.restoreState();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
const asset = this.controlsToAsset( );
|
this.saveState(this.controlsToAsset());
|
||||||
if( null != asset){
|
|
||||||
this.saveState( asset );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private restoreState() {
|
private restoreState() {
|
||||||
const savedAsset = this.storage.load(NAME_IN_STORAGE);
|
const savedAsset = localStorage.getItem(STORAGE_KEY);
|
||||||
if (savedAsset) this.assetToControls(savedAsset);
|
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) {
|
private saveState(asset: Asset) {
|
||||||
this.storage.save(NAME_IN_STORAGE, asset);
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(asset));
|
||||||
|
localStorage.setItem(HASH_KEY, this.generateHash(asset)); // Zapisujemy nowy hash
|
||||||
}
|
}
|
||||||
|
|
||||||
private assetToControls( asset : Asset ) {
|
private assetToControls(asset: Asset) {
|
||||||
if( asset.life.length > 0 ){
|
if (!asset.life.length) return;
|
||||||
this.updateBatch(asset);
|
const [firstLife, ...restLives] = asset.life;
|
||||||
}
|
const method = asset.depreciationMethods[0];
|
||||||
}
|
|
||||||
|
|
||||||
private updateBatch(asset: Asset) {
|
this.initialValueAsset.set(firstLife.initial);
|
||||||
const [firstLife, ...restLives] = asset.life;
|
this.startDepreciation.set(YearMonth.toDate(firstLife.when));
|
||||||
const method0 = asset.depreciationMethods[0];
|
this.rate.set(method.rate);
|
||||||
|
this.typeDepreciation.set(method.type);
|
||||||
|
this.factor.set(method.factor);
|
||||||
|
this.lifeChangesSignal.set(restLives.map(life => new AssetLifeChangeWrapper(life)));
|
||||||
|
}
|
||||||
|
|
||||||
this.initialValueAsset.set(firstLife.initial);
|
private controlsToAsset(): Asset {
|
||||||
this.startDepreciation.set(YearMonth.toDate(firstLife.when));
|
const when = YearMonth.fromDate(this.startDepreciation());
|
||||||
this.rate.set(method0.rate);
|
const asset = new Asset(when);
|
||||||
this.typeDepreciation.set(method0.type);
|
const method = new AssetDepreciationMethod(when.year, this.rate(), this.typeDepreciation(), this.factor());
|
||||||
this.factor.set(method0.factor);
|
|
||||||
|
|
||||||
const lifeChanges = restLives.map((lifeChange) =>
|
asset.addMethod(method);
|
||||||
new AssetLifeChangeWrapper(lifeChange)
|
asset.addChange(new AssetLifeChange(when, this.initialValueAsset(), 0, 0));
|
||||||
);
|
this.lifeChangesSignal().forEach(lc => asset.addChange(lc.get()));
|
||||||
this.lifeChangesSignal.set(lifeChanges);
|
|
||||||
}
|
|
||||||
|
|
||||||
private controlsToAsset(): Asset {
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
const formValues = this.formValues();
|
private calculateToValues(positions: AssetPlanPosition[]) {
|
||||||
const lifeChanges = this.lifeChangesSignal();
|
let sum = 0, sumThisYear = 0;
|
||||||
|
|
||||||
const when = YearMonth.fromDate( formValues.startDepreciation );
|
positions.forEach(pos => {
|
||||||
const asset = new Asset( when );
|
pos.calculatedDepreciation *= 0.01;
|
||||||
|
sum += pos.calculatedDepreciation;
|
||||||
|
pos.sum = sum;
|
||||||
|
sumThisYear = pos.when.month === 1 ? pos.calculatedDepreciation : sumThisYear + pos.calculatedDepreciation;
|
||||||
|
pos.sumThisYear = sumThisYear;
|
||||||
|
});
|
||||||
|
|
||||||
const method = new AssetDepreciationMethod( when.year, formValues.rate, formValues.typeDepreciation, formValues.factor );
|
// Cache wyników
|
||||||
asset.addMethod( method );
|
localStorage.setItem(CACHE_KEY, JSON.stringify(positions));
|
||||||
|
}
|
||||||
const creationLifeChange = new AssetLifeChange( when, formValues.initialValueAsset, 0, 0 );
|
|
||||||
asset.addChange( creationLifeChange );
|
|
||||||
|
|
||||||
lifeChanges.forEach((lifeChange) => {
|
|
||||||
asset.addChange( lifeChange.get() );
|
|
||||||
});
|
|
||||||
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateToValues( positions:AssetPlanPosition[] ) {
|
|
||||||
let sum = 0;
|
|
||||||
let sumThisYear = 0;
|
|
||||||
positions.forEach( position => {
|
|
||||||
// From gr to zł or from cents to dollars it depends from currency
|
|
||||||
position.calculatedDepreciation *= 0.01;
|
|
||||||
sum += position.calculatedDepreciation;
|
|
||||||
position.sum = sum;
|
|
||||||
|
|
||||||
if( position.when.month === 1 ) {
|
|
||||||
sumThisYear = position.calculatedDepreciation;
|
|
||||||
} else {
|
|
||||||
sumThisYear += position.calculatedDepreciation;
|
|
||||||
}
|
|
||||||
position.sumThisYear = sumThisYear;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private reCalculate() {
|
private reCalculate() {
|
||||||
|
this.calculateAmortizationsForAsset(this.controlsToAsset());
|
||||||
const asset = this.controlsToAsset( );
|
|
||||||
this.calculateAmortizationsForAsset(asset);
|
|
||||||
this.saveState(asset);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateAmortizationsForAsset(asset: Asset) {
|
private calculateAmortizationsForAsset(asset: Asset) {
|
||||||
this.assetService.calculate(asset).subscribe((positions) => {
|
this.assetService.calculate(asset).subscribe(positions => {
|
||||||
this.calculateToValues(positions);
|
this.calculateToValues(positions);
|
||||||
this.amortizationsSignal.set(positions);
|
this.amortizationsSignal.set(positions);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clazz(pos: AssetPlanPosition) {
|
||||||
clazz(pos : AssetPlanPosition ){
|
return pos.when.year % 2 === 0 ? "table-light" : "table-dark";
|
||||||
return pos.when.year % 2 === 0 ? "table-light" : "table-dark";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to add a life change
|
addLifeChange(amount = 1000) {
|
||||||
addLifeChange( ) {
|
const change = new AssetLifeChange(YearMonth.fromDate(this.startDepreciation()), amount, 0, 0);
|
||||||
const assetLifeChange2 : AssetLifeChange
|
this.lifeChangesSignal.update(changes => [...changes, new AssetLifeChangeWrapper(change)]);
|
||||||
= new AssetLifeChange( YearMonth.fromDate( this.formValues().startDepreciation ), 1000, 0, 0 );
|
|
||||||
return this.addLifeChange2( assetLifeChange2 );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addLifeChange2( assetLifeChange : AssetLifeChange ) {
|
|
||||||
const newChangeWrapper = new AssetLifeChangeWrapper( assetLifeChange );
|
|
||||||
this.lifeChangesSignal.update((changes) => [...changes, newChangeWrapper]);
|
|
||||||
return newChangeWrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method to remove a life change
|
|
||||||
removeLifeChange(index: number) {
|
removeLifeChange(index: number) {
|
||||||
this.lifeChangesSignal.update((changes) =>
|
this.lifeChangesSignal.update(changes => changes.filter((_, i) => i !== index));
|
||||||
changes.filter((_, i) => i !== index)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDateFromInput(inputValue: string) {
|
updateDateFromInput(inputValue: string) {
|
||||||
// Konwersja stringa 'YYYY-MM' na Date
|
this.startDepreciation.set(parse(inputValue, 'yyyy-MM', new Date()));
|
||||||
const parsedDate = parse(inputValue, 'yyyy-MM', new Date());
|
|
||||||
this.startDepreciation.set(parsedDate);
|
|
||||||
}
|
}
|
||||||
onMonthSelected(event: Date, datepicker: MatDatepicker<Date>) {
|
|
||||||
this.startDepreciation.set(event); // Aktualizacja pola w formularzu
|
onMonthSelected(event: Date, datepicker: any) {
|
||||||
|
this.startDepreciation.set(event);
|
||||||
datepicker.close();
|
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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,11 +93,12 @@ export class FixedAssetComponent {
|
||||||
private controlsToAssets(): [string, Asset][] {
|
private controlsToAssets(): [string, Asset][] {
|
||||||
|
|
||||||
const assets = new Map<string, Asset>();
|
const assets = new Map<string, Asset>();
|
||||||
|
const today = YearMonth.today();
|
||||||
this.assetFormArray.controls.forEach( control => {
|
this.assetFormArray.controls.forEach( control => {
|
||||||
|
|
||||||
const nrInv = control.get('nrInv')?.value;
|
const nrInv = control.get('nrInv')?.value;
|
||||||
const initialValue = control.get('initialValue')?.value;
|
const initialValue = control.get('initialValue')?.value;
|
||||||
const yearMonth = YearMonth.today();
|
const yearMonth = today;
|
||||||
const asset = new Asset( yearMonth );
|
const asset = new Asset( yearMonth );
|
||||||
const assetLifeChange = new AssetLifeChange( yearMonth, initialValue, 0, 0 );
|
const assetLifeChange = new AssetLifeChange( yearMonth, initialValue, 0, 0 );
|
||||||
asset.addChange( assetLifeChange );
|
asset.addChange( assetLifeChange );
|
||||||
|
|
@ -117,11 +118,11 @@ export class FixedAssetComponent {
|
||||||
this.addRow( "NrInv" + FixedAssetComponent.indexNr++, 1000 );
|
this.addRow( "NrInv" + FixedAssetComponent.indexNr++, 1000 );
|
||||||
}
|
}
|
||||||
|
|
||||||
addRow( nrInv_:string, initial_: number ):void{
|
addRow( nrInv:string, initial: number ):void{
|
||||||
|
|
||||||
const newRow = this.fb.group({
|
const newRow = this.fb.group({
|
||||||
nrInv : [ nrInv_ ],
|
nrInv : [ nrInv ],
|
||||||
initialValue : [ initial_ ],
|
initialValue : [ initial ],
|
||||||
});
|
});
|
||||||
|
|
||||||
// typeDepreciation : [ TypeDepreciation.linear ],
|
// typeDepreciation : [ TypeDepreciation.linear ],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue