Automated issue wrap-up including: - Implementation completion verification - Test execution and validation - Cost tracking and note generation - Repository state commit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -908,4 +908,217 @@ def current_period(date_str: Optional[str], db_path: Optional[str]):
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"Error getting current period: {e}", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@cost_commands.group(name='allocate')
|
||||
def cost_allocate():
|
||||
"""Allocate costs to active issues for specified periods."""
|
||||
pass
|
||||
|
||||
|
||||
@cost_allocate.command('period')
|
||||
@click.argument('period_id', type=int)
|
||||
@click.option('--database', 'db_path', help='Database path (defaults to config)')
|
||||
@click.option('--dry-run', is_flag=True, help='Show what would be allocated without making changes')
|
||||
def allocate_period(period_id: int, db_path: Optional[str], dry_run: bool):
|
||||
"""Allocate costs for a specific period to active issues."""
|
||||
try:
|
||||
# Import allocation engine
|
||||
from .allocation_engine import AllocationEngine, AllocationStatus
|
||||
|
||||
# Get database path
|
||||
if not db_path:
|
||||
config_manager = ConfigurationManager()
|
||||
config = config_manager.get_current_config()
|
||||
db_path = config.get('database_path')
|
||||
|
||||
if not db_path:
|
||||
click.echo("Error: No database path specified.", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
# Initialize allocation engine
|
||||
engine = AllocationEngine(db_path)
|
||||
|
||||
if dry_run:
|
||||
# TODO: Implement dry-run functionality
|
||||
click.echo(f"🔍 Dry run for period {period_id} allocation")
|
||||
click.echo("(Dry-run functionality will be implemented in future version)")
|
||||
return
|
||||
|
||||
# Perform allocation
|
||||
result = engine.allocate_period_costs(period_id)
|
||||
|
||||
# Display results based on status
|
||||
if result.status == AllocationStatus.SUCCESS:
|
||||
click.echo(f"✅ Cost Allocation Complete - Period {period_id}")
|
||||
click.echo("=" * 50)
|
||||
click.echo(f"Total Costs Allocated: €{result.total_costs:.2f}")
|
||||
click.echo(f"Active Issues: {len(result.active_issues)}")
|
||||
click.echo(f"Cost Per Issue: €{result.cost_per_issue:.2f}")
|
||||
click.echo(f"Allocations Created: {result.allocations_created}")
|
||||
click.echo(f"Transactions Created: {result.transactions_created}")
|
||||
|
||||
if result.active_issues:
|
||||
click.echo(f"\nIssues that received allocations:")
|
||||
for issue_id in result.active_issues:
|
||||
click.echo(f" Issue #{issue_id}: €{result.cost_per_issue:.2f}")
|
||||
|
||||
elif result.status == AllocationStatus.NO_ACTIVE_ISSUES:
|
||||
click.echo(f"⚠️ No Active Issues Found - Period {period_id}")
|
||||
click.echo("=" * 45)
|
||||
click.echo(f"Total Costs: €{result.total_costs:.2f}")
|
||||
click.echo(f"Loss Carried Forward: €{result.loss_carried_forward:.2f}")
|
||||
click.echo("All costs have been carried forward to the next period.")
|
||||
|
||||
elif result.status == AllocationStatus.NO_COSTS_TO_ALLOCATE:
|
||||
click.echo(f"ℹ️ No Costs to Allocate - Period {period_id}")
|
||||
click.echo("=" * 40)
|
||||
click.echo("Period has no costs to allocate.")
|
||||
|
||||
elif result.status == AllocationStatus.PERIOD_CLOSED:
|
||||
click.echo(f"⚠️ Period Already Closed - Period {period_id}")
|
||||
click.echo("=" * 40)
|
||||
click.echo("This period has already been processed and closed.")
|
||||
|
||||
else:
|
||||
click.echo(f"❌ Allocation Failed - Period {period_id}")
|
||||
click.echo("=" * 35)
|
||||
click.echo(f"Error: {result.message}")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"Error performing allocation: {e}", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@cost_allocate.command('show')
|
||||
@click.argument('target', type=str)
|
||||
@click.option('--format', 'output_format',
|
||||
type=click.Choice(['table', 'json']),
|
||||
default='table', help='Output format')
|
||||
@click.option('--database', 'db_path', help='Database path (defaults to config)')
|
||||
def show_allocations(target: str, output_format: str, db_path: Optional[str]):
|
||||
"""Show allocations for an issue (issue:ID) or period (period:ID)."""
|
||||
try:
|
||||
# Import allocation engine
|
||||
from .allocation_engine import AllocationEngine
|
||||
|
||||
# Get database path
|
||||
if not db_path:
|
||||
config_manager = ConfigurationManager()
|
||||
config = config_manager.get_current_config()
|
||||
db_path = config.get('database_path')
|
||||
|
||||
if not db_path:
|
||||
click.echo("Error: No database path specified.", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
# Parse target (issue:123 or period:456)
|
||||
if ':' not in target:
|
||||
click.echo("Error: Target must be in format 'issue:ID' or 'period:ID'", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
target_type, target_id_str = target.split(':', 1)
|
||||
try:
|
||||
target_id = int(target_id_str)
|
||||
except ValueError:
|
||||
click.echo("Error: Target ID must be a number", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
# Initialize allocation engine
|
||||
engine = AllocationEngine(db_path)
|
||||
|
||||
# Get allocations based on target type
|
||||
if target_type == 'issue':
|
||||
allocations = engine.get_issue_allocations(target_id)
|
||||
title = f"Cost Allocations for Issue #{target_id}"
|
||||
elif target_type == 'period':
|
||||
allocations = engine.get_period_allocations(target_id)
|
||||
title = f"Cost Allocations for Period {target_id}"
|
||||
else:
|
||||
click.echo("Error: Target type must be 'issue' or 'period'", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
if not allocations:
|
||||
click.echo(f"📝 No allocations found for {target}")
|
||||
return
|
||||
|
||||
# Display results
|
||||
if output_format == 'json':
|
||||
import json
|
||||
click.echo(json.dumps(allocations, indent=2, default=str))
|
||||
else:
|
||||
# Table format
|
||||
from tabulate import tabulate
|
||||
click.echo(f"\n💰 {title}\n")
|
||||
|
||||
if target_type == 'issue':
|
||||
headers = ['ID', 'Period', 'Amount', 'Date', 'Period Range', 'Transaction']
|
||||
rows = []
|
||||
for alloc in allocations:
|
||||
rows.append([
|
||||
alloc['id'],
|
||||
alloc['period_id'],
|
||||
f"€{alloc['allocated_amount']:.2f}",
|
||||
alloc['allocation_date'],
|
||||
f"{alloc['period_start']} to {alloc['period_end']}",
|
||||
alloc['transaction_id'] or 'N/A'
|
||||
])
|
||||
else:
|
||||
headers = ['ID', 'Issue', 'Amount', 'Date', 'Transaction']
|
||||
rows = []
|
||||
for alloc in allocations:
|
||||
rows.append([
|
||||
alloc['id'],
|
||||
f"#{alloc['issue_id']}",
|
||||
f"€{alloc['allocated_amount']:.2f}",
|
||||
alloc['allocation_date'],
|
||||
alloc['transaction_id'] or 'N/A'
|
||||
])
|
||||
|
||||
click.echo(tabulate(rows, headers=headers, tablefmt='grid'))
|
||||
click.echo(f"\n📊 Total: {len(allocations)} allocations")
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"Error showing allocations: {e}", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@cost_allocate.command('reverse')
|
||||
@click.argument('allocation_id', type=int)
|
||||
@click.option('--database', 'db_path', help='Database path (defaults to config)')
|
||||
@click.confirmation_option(prompt='Are you sure you want to reverse this allocation?')
|
||||
def reverse_allocation(allocation_id: int, db_path: Optional[str]):
|
||||
"""Reverse a cost allocation (for corrections)."""
|
||||
try:
|
||||
# Import allocation engine
|
||||
from .allocation_engine import AllocationEngine
|
||||
|
||||
# Get database path
|
||||
if not db_path:
|
||||
config_manager = ConfigurationManager()
|
||||
config = config_manager.get_current_config()
|
||||
db_path = config.get('database_path')
|
||||
|
||||
if not db_path:
|
||||
click.echo("Error: No database path specified.", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
# Initialize allocation engine
|
||||
engine = AllocationEngine(db_path)
|
||||
|
||||
# Perform reversal
|
||||
success = engine.reverse_allocation(allocation_id)
|
||||
|
||||
if success:
|
||||
click.echo(f"✅ Successfully reversed allocation #{allocation_id}")
|
||||
click.echo("A reversal transaction has been created in the audit trail.")
|
||||
else:
|
||||
click.echo(f"❌ Failed to reverse allocation #{allocation_id}")
|
||||
click.echo("Allocation may not exist or may already be reversed.")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"Error reversing allocation: {e}", err=True)
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user