| import logging |
| from typing import Any, Dict, List |
| from accounting.models import Account, AccountType, EntryType, JournalEntry, Transaction |
| from sqlalchemy.orm import Session |
|
|
| logger = logging.getLogger(__name__) |
|
|
| class IntercompanyManager: |
| """ |
| Manager for handling multi-entity transactions and intercompany eliminations. |
| """ |
|
|
| def __init__(self, db: Session): |
| self.db = db |
|
|
| def get_intercompany_transactions(self, workspace_id: str) -> List[Transaction]: |
| """Fetch all transactions involving other workspaces""" |
| return self.db.query(Transaction).filter( |
| Transaction.workspace_id == workspace_id, |
| Transaction.is_intercompany == True |
| ).all() |
|
|
| def find_unmatched_intercompany(self, workspace_id: str) -> List[Dict[str, Any]]: |
| """ |
| Identify intercompany transactions that don't have a matching |
| entry in the counterparty workspace. |
| """ |
| txs = self.get_intercompany_transactions(workspace_id) |
| unmatched = [] |
|
|
| for tx in txs: |
| if not tx.counterparty_workspace_id: |
| continue |
| |
| |
| |
| matching = self.db.query(Transaction).filter( |
| Transaction.workspace_id == tx.counterparty_workspace_id, |
| Transaction.is_intercompany == True, |
| Transaction.counterparty_workspace_id == workspace_id |
| ).first() |
|
|
| if not matching: |
| unmatched.append({ |
| "transaction_id": tx.id, |
| "target_workspace": tx.counterparty_workspace_id, |
| "date": tx.transaction_date, |
| "description": tx.description |
| }) |
| |
| return unmatched |
|
|
| def generate_elimination_report(self, workspace_id: str) -> Dict[str, Any]: |
| """ |
| Calculate total intercompany volume to be eliminated for consolidation. |
| """ |
| txs = self.get_intercompany_transactions(workspace_id) |
| |
| total_volume = 0.0 |
| by_counterparty = {} |
|
|
| for tx in txs: |
| |
| amount = sum(je.amount for je in tx.journal_entries if je.type == EntryType.DEBIT) |
| total_volume += amount |
| |
| cp = tx.counterparty_workspace_id or "Unknown" |
| by_counterparty[cp] = by_counterparty.get(cp, 0.0) + amount |
|
|
| return { |
| "total_elimination_volume": total_volume, |
| "breakdown_by_counterparty": by_counterparty, |
| "transaction_count": len(txs) |
| } |
|
|