Split 49 modules/suites into independent git repos; untrack from monorepo
Some checks failed
fusion_accounting CI / test (fusion_accounting_ai) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_core) (push) Has been cancelled
fusion_accounting CI / test (fusion_accounting_migration) (push) Has been cancelled

Each top-level module/suite folder is now its own private repo on GitHub
(gsinghpal/<name>) and gitea (admin/<name>), with a fresh single initial
commit. The monorepo no longer tracks them (added to .gitignore + git rm
--cached); working-tree files are retained on disk and managed in their
own repos. The monorepo keeps shared root files (CLAUDE.md, docs/, scripts/,
tools/, AGENTS.md, WIP/obsolete dirs) and full history.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-06-07 01:54:34 -04:00
parent 2a7b315e98
commit a66cdefc01
6740 changed files with 51 additions and 1277207 deletions

View File

@@ -1,96 +0,0 @@
"""Asset utilization anomaly detection.
Flags assets where actual usage / posted depreciation deviates significantly
from the expected schedule. Three signal types:
- behind_schedule: actual depreciation < expected by > threshold pct
- ahead_of_schedule: actual > expected (over-depreciated; scrap or recompute)
- low_utilization: units_used < expected_units_per_period (waste alert)
"""
from dataclasses import dataclass
@dataclass
class AssetAnomaly:
asset_id: int
asset_name: str
anomaly_type: str
severity: str
expected: float
actual: float
variance_pct: float
detail: str
def to_dict(self):
return {
'asset_id': self.asset_id,
'asset_name': self.asset_name,
'anomaly_type': self.anomaly_type,
'severity': self.severity,
'expected': self.expected,
'actual': self.actual,
'variance_pct': self.variance_pct,
'detail': self.detail,
}
DEFAULT_LOW_THRESHOLD_PCT = 10.0
DEFAULT_MEDIUM_THRESHOLD_PCT = 25.0
DEFAULT_HIGH_THRESHOLD_PCT = 50.0
def detect_schedule_variance(*, asset_id: int, asset_name: str,
expected_accumulated: float,
actual_accumulated: float) -> AssetAnomaly | None:
"""Compare expected accumulated depreciation vs actual posted."""
if expected_accumulated <= 0:
return None
variance_amt = actual_accumulated - expected_accumulated
variance_pct = abs(variance_amt) / expected_accumulated * 100
if variance_pct < DEFAULT_LOW_THRESHOLD_PCT:
return None
direction = 'ahead_of_schedule' if variance_amt > 0 else 'behind_schedule'
if variance_pct >= DEFAULT_HIGH_THRESHOLD_PCT:
severity = 'high'
elif variance_pct >= DEFAULT_MEDIUM_THRESHOLD_PCT:
severity = 'medium'
else:
severity = 'low'
detail = f"Posted ${actual_accumulated:,.2f} vs expected ${expected_accumulated:,.2f}"
return AssetAnomaly(
asset_id=asset_id,
asset_name=asset_name,
anomaly_type=direction,
severity=severity,
expected=expected_accumulated,
actual=actual_accumulated,
variance_pct=round(variance_pct, 1),
detail=detail,
)
def detect_low_utilization(*, asset_id: int, asset_name: str,
expected_units: float,
actual_units: float) -> AssetAnomaly | None:
"""For units-of-production assets: flag low actual usage."""
if expected_units <= 0:
return None
if actual_units >= expected_units * 0.9:
return None
deficit_pct = (expected_units - actual_units) / expected_units * 100
if deficit_pct >= 50:
severity = 'high'
elif deficit_pct >= 25:
severity = 'medium'
else:
severity = 'low'
return AssetAnomaly(
asset_id=asset_id,
asset_name=asset_name,
anomaly_type='low_utilization',
severity=severity,
expected=expected_units,
actual=actual_units,
variance_pct=round(deficit_pct, 1),
detail=f"Used {actual_units:.0f} of expected {expected_units:.0f} units",
)