<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
azaharizaman / nexus-account-variance-analysis example snippets
interface VarianceCalculatorInterface
{
/**
* Calculate variance between actual and comparison values
*
* @param Money $actual The actual amount
* @param Money $comparison The budget/forecast/prior amount
* @param VarianceType $type The type of variance calculation
* @return VarianceResult
*/
public function calculate(
Money $actual,
Money $comparison,
VarianceType $type = VarianceType::BUDGET
): VarianceResult;
/**
* Calculate variances for multiple accounts
*
* @param array<AccountVariance> $accounts
* @return array<VarianceResult>
*/
public function calculateBatch(array $accounts): array;
/**
* Calculate favorable/unfavorable direction
* (Revenue: over = favorable; Expense: under = favorable)
*/
public function determineDirection(
VarianceResult $variance,
AccountCategory $category
): VarianceDirection;
}
interface TrendAnalyzerInterface
{
/**
* Analyze trend for an account over multiple periods
*
* @param array<PeriodValue> $periodValues Historical values by period
* @param int $periodsForward Periods to project forward
* @return TrendData
*/
public function analyze(array $periodValues, int $periodsForward = 3): TrendData;
/**
* Detect trend direction (up, down, stable)
*/
public function detectTrendDirection(array $periodValues): TrendDirection;
/**
* Calculate moving average
*/
public function calculateMovingAverage(
array $periodValues,
int $periods = 3
): array;
/**
* Identify seasonality patterns
*/
public function detectSeasonality(
array $monthlyValues,
int $yearsOfData = 2
): SeasonalityPattern;
}
interface SignificanceEvaluatorInterface
{
/**
* Evaluate significance of a variance
*
* @param VarianceResult $variance The calculated variance
* @param SignificanceThreshold $threshold Threshold configuration
* @return SignificanceLevel
*/
public function evaluate(
VarianceResult $variance,
SignificanceThreshold $threshold
): SignificanceLevel;
/**
* Check if variance exceeds any threshold
*/
public function isSignificant(
VarianceResult $variance,
SignificanceThreshold $threshold
): bool;
/**
* Get all variances exceeding threshold
*
* @param array<VarianceResult> $variances
* @return array<VarianceResult>
*/
public function filterSignificant(
array $variances,
SignificanceThreshold $threshold
): array;
}
interface AttributionAnalyzerInterface
{
/**
* Attribute variance to contributing factors
*
* @param VarianceResult $variance The total variance
* @param array<string, mixed> $factors Factor data for attribution
* @return VarianceAttribution
*/
public function attribute(
VarianceResult $variance,
array $factors
): VarianceAttribution;
/**
* Perform price/volume variance analysis
* (Common for revenue and cost analysis)
*/
public function priceVolumeAnalysis(
float $actualPrice,
float $budgetPrice,
float $actualVolume,
float $budgetVolume
): PriceVolumeBreakdown;
/**
* Attribute to organizational units
*/
public function attributeByDimension(
VarianceResult $variance,
array $dimensionData,
string $dimensionName
): array;
}
interface VarianceDataProviderInterface
{
/**
* Get actual account balances for a period
*/
public function getActualBalances(
string $tenantId,
string $periodId
): array;
/**
* Get budget amounts for a period
*/
public function getBudgetAmounts(
string $tenantId,
string $periodId
): array;
/**
* Get forecast amounts for a period
*/
public function getForecastAmounts(
string $tenantId,
string $periodId
): array;
/**
* Get prior period balances
*/
public function getPriorPeriodBalances(
string $tenantId,
string $periodId,
int $periodsBack = 1
): array;
/**
* Get historical values for trend analysis
*/
public function getHistoricalValues(
string $tenantId,
string $accountId,
int $periods = 12
): array;
}
final readonly class VarianceResult
{
public function __construct(
public string $accountId,
public string $accountName,
public Money $actualAmount,
public Money $comparisonAmount,
public Money $varianceAmount,
public float $variancePercentage,
public VarianceType $type,
public VarianceStatus $status,
public bool $isFavorable
) {}
public function isOverBudget(): bool
{
return $this->varianceAmount->isPositive();
}
public function isUnderBudget(): bool
{
return $this->varianceAmount->isNegative();
}
}
final readonly class TrendData
{
public function __construct(
public string $accountId,
public TrendDirection $direction,
public float $averageGrowthRate,
public float $standardDeviation,
public array $movingAverages,
public array $projectedValues,
public float $rSquared,
public float $trendSlope
) {}
public function isVolatile(float $threshold = 0.15): bool
{
return $this->standardDeviation > $threshold;
}
}
final readonly class SignificanceThreshold
{
public function __construct(
public float $percentageThreshold = 10.0,
public Money $absoluteThreshold = null,
public bool $usePercentage = true,
public bool $useAbsolute = false,
public bool $, usePercentage: false, useAbsolute: true);
}
}
final readonly class VarianceAttribution
{
public function __construct(
public VarianceResult $totalVariance,
public array $attributions,
public float $explainedPercentage,
public Money $unexplainedAmount
) {}
/**
* @return array<string, Money>
*/
public function getTopContributors(int $count = 5): array
{
// Return top N contributing factors
}
}
final readonly class AccountVariance
{
public function __construct(
public string $accountId,
public string $accountCode,
public string $accountName,
public AccountCategory $category,
public Money $actualAmount,
public Money $budgetAmount,
public ?Money $priorPeriodAmount = null,
public ?Money $forecastAmount = null
) {}
}
final readonly class ForecastVariance
{
public function __construct(
public string $accountId,
public Money $originalForecast,
public Money $revisedForecast,
public Money $actualToDate,
public float $forecastAccuracy,
public string $revisionReason
) {}
}
enum VarianceType: string
{
case BUDGET = 'budget';
case FORECAST = 'forecast';
case PRIOR_PERIOD = 'prior_period';
case YEAR_OVER_YEAR = 'year_over_year';
case PLAN = 'plan';
}
enum VarianceStatus: string
{
case FAVORABLE = 'favorable';
case UNFAVORABLE = 'unfavorable';
case ON_TARGET = 'on_target';
case NOT_CALCULATED = 'not_calculated';
}
enum TrendDirection: string
{
case INCREASING = 'increasing';
case DECREASING = 'decreasing';
case STABLE = 'stable';
case VOLATILE = 'volatile';
}
enum SignificanceLevel: string
{
case CRITICAL = 'critical'; // > 3x threshold
case HIGH = 'high'; // > 2x threshold
case MEDIUM = 'medium'; // > 1.5x threshold
case LOW = 'low'; // > 1x threshold
case INSIGNIFICANT = 'insignificant';
}
use Nexus\AccountVarianceAnalysis\Contracts\VarianceCalculatorInterface;
use Nexus\AccountVarianceAnalysis\Contracts\SignificanceEvaluatorInterface;
use Nexus\AccountVarianceAnalysis\ValueObjects\SignificanceThreshold;
use Nexus\AccountVarianceAnalysis\Enums\VarianceType;
final readonly class BudgetVarianceReportService
{
public function __construct(
private VarianceCalculatorInterface $calculator,
private SignificanceEvaluatorInterface $evaluator,
private VarianceDataProviderInterface $dataProvider
) {}
public function generateReport(string $tenantId, string $periodId): array
{
// Get actual and budget data
$actuals = $this->dataProvider->getActualBalances($tenantId, $periodId);
$budgets = $this->dataProvider->getBudgetAmounts($tenantId, $periodId);
// Define significance threshold
$threshold = SignificanceThreshold::percentage(10.0);
$variances = [];
$significantVariances = [];
foreach ($actuals as $accountId => $actual) {
$budget = $budgets[$accountId] ?? null;
if ($budget === null) continue;
// Calculate variance
$variance = $this->calculator->calculate(
actual: $actual,
comparison: $budget,
type: VarianceType::BUDGET
);
$variances[] = $variance;
// Check significance
if ($this->evaluator->isSignificant($variance, $threshold)) {
$significantVariances[] = $variance;
}
}
return [
'all_variances' => $variances,
'significant_variances' => $significantVariances,
'total_favorable' => $this->sumFavorable($variances),
'total_unfavorable' => $this->sumUnfavorable($variances),
];
}
}
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.