enterprise_assest_managemen.../node_api/tests/depreciation.test.js

426 lines
14 KiB
JavaScript

// node_api/tests/depreciation.test.js
const { DepreciationCalculator } = require('../jobs/depreciationCalculator');
const { DepreciationJob } = require('../jobs/depreciationJob');
describe('DepreciationCalculator', () => {
let calculator;
beforeEach(() => {
calculator = new DepreciationCalculator();
});
describe('Straight Line Depreciation', () => {
test('should calculate monthly straight-line depreciation correctly', () => {
const asset = {
id: 'test-asset-1',
acquisition_cost: 10000,
salvage_value: 1000,
expected_useful_life_months: 60,
depreciation_method: 'straight_line'
};
const result = calculator.calculateMonthlyDepreciation(asset);
expect(result.method).toBe('straight_line');
expect(result.monthlyDepreciation).toBe(150); // (10000 - 1000) / 60
expect(result.calculation.acquisitionCost).toBe(10000);
expect(result.calculation.salvageValue).toBe(1000);
expect(result.calculation.usefulLifeMonths).toBe(60);
});
test('should use depreciation rate when provided', () => {
const asset = {
id: 'test-asset-2',
acquisition_cost: 10000,
expected_useful_life_months: 60,
depreciation_method: 'straight_line',
depreciation_percentage_rate: 20
};
const result = calculator.calculateMonthlyDepreciation(asset);
expect(result.monthlyDepreciation).toBeCloseTo(166.67, 2); // (10000 * 20%) / 12
});
test('should handle zero salvage value', () => {
const asset = {
id: 'test-asset-3',
acquisition_cost: 12000,
expected_useful_life_months: 48,
depreciation_method: 'straight_line'
};
const result = calculator.calculateMonthlyDepreciation(asset);
expect(result.monthlyDepreciation).toBe(250); // 12000 / 48
});
});
describe('Declining Balance Depreciation', () => {
test('should calculate declining balance depreciation correctly', () => {
const asset = {
id: 'test-asset-4',
acquisition_cost: 10000,
depreciation_percentage_rate: 20,
total_accumulated_depreciation: 0,
depreciation_method: 'declining_balance'
};
const result = calculator.calculateMonthlyDepreciation(asset);
expect(result.method).toBe('declining_balance');
expect(result.monthlyDepreciation).toBeCloseTo(166.67, 2); // 10000 * (20% / 12)
expect(result.calculation.currentBookValue).toBe(10000);
});
test('should handle existing accumulated depreciation', () => {
const asset = {
id: 'test-asset-5',
acquisition_cost: 10000,
depreciation_percentage_rate: 20,
total_accumulated_depreciation: 2000,
depreciation_method: 'declining_balance'
};
const result = calculator.calculateMonthlyDepreciation(asset);
expect(result.monthlyDepreciation).toBeCloseTo(133.33, 2); // 8000 * (20% / 12)
expect(result.calculation.currentBookValue).toBe(8000);
});
});
describe('Sum of Years Depreciation', () => {
test('should calculate sum of years depreciation correctly', () => {
const asset = {
id: 'test-asset-6',
acquisition_cost: 10000,
salvage_value: 1000,
expected_useful_life_months: 60, // 5 years
acquisition_date: new Date('2020-01-01'),
depreciation_method: 'sum_of_years'
};
const calculationDate = new Date('2020-02-01');
const result = calculator.calculateMonthlyDepreciation(asset, calculationDate);
expect(result.method).toBe('sum_of_years');
expect(result.calculation.usefulLifeYears).toBe(5);
expect(result.calculation.sumOfYears).toBe(15); // 1+2+3+4+5
expect(result.calculation.remainingYears).toBe(5);
// First year depreciation: (10000-1000) * (5/15) = 3000 annual, 250 monthly
expect(result.monthlyDepreciation).toBe(250);
});
});
describe('Units of Production Depreciation', () => {
test('should calculate units of production depreciation correctly', () => {
const asset = {
id: 'test-asset-7',
acquisition_cost: 10000,
salvage_value: 1000,
expected_total_units: 90000,
current_month_units: 1000,
depreciation_method: 'units_of_production'
};
const result = calculator.calculateMonthlyDepreciation(asset);
expect(result.method).toBe('units_of_production');
expect(result.calculation.depreciationPerUnit).toBe(0.1); // (10000-1000)/90000
expect(result.monthlyDepreciation).toBe(100); // 0.1 * 1000
});
test('should handle zero monthly units', () => {
const asset = {
id: 'test-asset-8',
acquisition_cost: 10000,
salvage_value: 1000,
expected_total_units: 90000,
current_month_units: 0,
depreciation_method: 'units_of_production'
};
const result = calculator.calculateMonthlyDepreciation(asset);
expect(result.monthlyDepreciation).toBe(0);
});
});
describe('Asset Validation', () => {
test('should validate straight-line depreciation parameters', () => {
const asset = {
id: 'test-asset-9',
acquisition_cost: 10000,
depreciation_method: 'straight_line'
// Missing expected_useful_life_months
};
const errors = calculator.validateAsset(asset);
expect(errors).toContain('Expected useful life is required for straight-line depreciation');
});
test('should validate declining balance parameters', () => {
const asset = {
id: 'test-asset-10',
acquisition_cost: 10000,
depreciation_method: 'declining_balance'
// Missing depreciation_percentage_rate
};
const errors = calculator.validateAsset(asset);
expect(errors).toContain('Depreciation rate is required for declining balance method');
});
test('should validate units of production parameters', () => {
const asset = {
id: 'test-asset-11',
acquisition_cost: 10000,
depreciation_method: 'units_of_production'
// Missing expected_total_units
};
const errors = calculator.validateAsset(asset);
expect(errors).toContain('Expected total units is required for units of production method');
});
});
describe('Asset Eligibility', () => {
test('should not depreciate fully depreciated assets', () => {
const asset = {
id: 'test-asset-12',
acquisition_cost: 10000,
total_accumulated_depreciation: 10000,
salvage_value: 0,
status: 'active',
acquisition_date: new Date('2020-01-01'),
expected_useful_life_months: 60
};
const shouldDepreciate = calculator.shouldDepreciate(asset);
expect(shouldDepreciate).toBe(false);
});
test('should not depreciate inactive assets', () => {
const asset = {
id: 'test-asset-13',
acquisition_cost: 10000,
total_accumulated_depreciation: 0,
status: 'retired',
acquisition_date: new Date('2020-01-01'),
expected_useful_life_months: 60
};
const shouldDepreciate = calculator.shouldDepreciate(asset);
expect(shouldDepreciate).toBe(false);
});
test('should not depreciate assets with future acquisition dates', () => {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const asset = {
id: 'test-asset-14',
acquisition_cost: 10000,
total_accumulated_depreciation: 0,
status: 'active',
acquisition_date: tomorrow,
expected_useful_life_months: 60
};
const shouldDepreciate = calculator.shouldDepreciate(asset);
expect(shouldDepreciate).toBe(false);
});
test('should not depreciate assets beyond useful life', () => {
const asset = {
id: 'test-asset-15',
acquisition_cost: 10000,
total_accumulated_depreciation: 0,
status: 'active',
acquisition_date: new Date('2020-01-01'),
expected_useful_life_months: 12
};
const calculationDate = new Date('2022-01-01'); // 24 months later
const shouldDepreciate = calculator.shouldDepreciate(asset, calculationDate);
expect(shouldDepreciate).toBe(false);
});
test('should depreciate eligible assets', () => {
const recentDate = new Date();
recentDate.setMonth(recentDate.getMonth() - 10); // 10 months ago
const asset = {
id: 'test-asset-16',
acquisition_cost: 10000,
total_accumulated_depreciation: 1000,
status: 'active',
acquisition_date: recentDate,
expected_useful_life_months: 60
};
const shouldDepreciate = calculator.shouldDepreciate(asset);
expect(shouldDepreciate).toBe(true);
});
});
describe('Helper Methods', () => {
test('should calculate months difference correctly', () => {
const startDate = new Date('2020-01-01');
const endDate = new Date('2020-06-01');
const monthsDiff = calculator.getMonthsDifference(startDate, endDate);
expect(monthsDiff).toBe(5);
});
test('should handle year boundaries in months difference', () => {
const startDate = new Date('2020-10-01');
const endDate = new Date('2021-03-01');
const monthsDiff = calculator.getMonthsDifference(startDate, endDate);
expect(monthsDiff).toBe(5);
});
});
describe('Error Handling', () => {
test('should throw error for unsupported depreciation method', () => {
const asset = {
id: 'test-asset-17',
acquisition_cost: 10000,
depreciation_method: 'unsupported_method'
};
expect(() => {
calculator.calculateMonthlyDepreciation(asset);
}).toThrow('Unsupported depreciation method: unsupported_method');
});
test('should throw error for missing acquisition cost', () => {
const asset = {
id: 'test-asset-18',
depreciation_method: 'straight_line',
expected_useful_life_months: 60
};
expect(() => {
calculator.calculateMonthlyDepreciation(asset);
}).toThrow('Missing required parameters for straight-line depreciation');
});
});
});
describe('DepreciationJob Integration', () => {
let job;
let mockAsset;
beforeEach(() => {
job = new DepreciationJob();
mockAsset = {
id: 'test-asset-job-1',
name: 'Test Asset',
acquisition_cost: 10000,
salvage_value: 1000,
expected_useful_life_months: 60,
depreciation_method: 'straight_line',
total_accumulated_depreciation: 0,
net_book_value: 10000,
status: 'active',
acquisition_date: new Date('2020-01-01')
};
});
describe('Asset Processing', () => {
test('should process asset depreciation correctly', async () => {
// Mock database methods
job.database.depreciationRecordExists = jest.fn().mockResolvedValue(false);
job.database.createDepreciationRecord = jest.fn().mockResolvedValue('record-id-123');
const result = await job.processAssetDepreciation(mockAsset, new Date('2020-02-01'));
expect(result.skipped).toBe(false);
expect(result.method).toBe('straight_line');
expect(result.monthlyDepreciation).toBe(150); // (10000-1000)/60
expect(result.accumulatedDepreciation).toBe(150);
expect(result.netBookValue).toBe(9850);
expect(job.database.createDepreciationRecord).toHaveBeenCalledWith(
expect.objectContaining({
asset_id: mockAsset.id,
monthly_depreciation: 150,
accumulated_depreciation_after: 150,
net_book_value_after: 9850
})
);
});
test('should skip asset if record already exists', async () => {
job.database.depreciationRecordExists = jest.fn().mockResolvedValue(true);
const result = await job.processAssetDepreciation(mockAsset, new Date('2020-02-01'));
expect(result.skipped).toBe(true);
expect(result.reason).toBe('Depreciation record already exists for this month');
});
test('should skip asset if validation fails', async () => {
const invalidAsset = { ...mockAsset, acquisition_cost: null };
job.database.depreciationRecordExists = jest.fn().mockResolvedValue(false);
const result = await job.processAssetDepreciation(invalidAsset, new Date('2020-02-01'));
expect(result.skipped).toBe(true);
expect(result.reason).toContain('Validation failed');
});
});
describe('Salvage Value Handling', () => {
test('should not depreciate below salvage value', async () => {
const nearlyFullyDepreciatedAsset = {
...mockAsset,
total_accumulated_depreciation: 8900,
net_book_value: 1100
};
job.database.depreciationRecordExists = jest.fn().mockResolvedValue(false);
job.database.createDepreciationRecord = jest.fn().mockResolvedValue('record-id-124');
const result = await job.processAssetDepreciation(nearlyFullyDepreciatedAsset, new Date('2020-02-01'));
expect(result.skipped).toBe(false);
expect(result.monthlyDepreciation).toBe(100); // Only depreciate down to salvage value
expect(result.netBookValue).toBe(1000); // Salvage value
});
});
});
// Mock implementations for testing
jest.mock('../jobs/depreciationDatabase', () => ({
DepreciationDatabase: jest.fn().mockImplementation(() => ({
getAssetsForDepreciation: jest.fn(),
depreciationRecordExists: jest.fn(),
createDepreciationRecord: jest.fn(),
getDepreciationSummary: jest.fn(),
getMonthlyDepreciationReport: jest.fn(),
getDepreciationIssues: jest.fn()
}))
}));
jest.mock('../utils/logger', () => ({
logger: {
info: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
debug: jest.fn(),
job: jest.fn(),
depreciation: jest.fn()
}
}));