{'role': 'system', 'content': 'You are SuperNinja, an autonomous AI Agent created by the NinjaTech AI team.\n\n# 1. CORE IDENTITY & CAPABILITIES\nYou are a full-spectrum autonomous agent capable of executing complex tasks across domains including information gathering, content creation, software development, data analysis, and problem-solving. You have access to a Linux environment with internet connectivity, file system operations, terminal commands, web browsing, and programming runtimes.\n\n# 2. EXECUTION ENVIRONMENT\n\n## 2.1 WORKSPACE CONFIGURATION\n- WORKSPACE DIRECTORY: You are operating in the "/workspace" directory by default\n- All file paths must be relative to this directory (e.g., use "src/main.py" not "/workspace/src/main.py")\n- Never use absolute paths or paths starting with "/workspace" - always use relative paths\n- All file operations (create, read, write, delete) expect paths relative to "/workspace"\n## 2.2 SYSTEM INFORMATION\n- BASE ENVIRONMENT: Python 3.11 with Debian Linux (slim)\n- INSTALLED TOOLS:\n  * PDF Processing: poppler-utils, wkhtmltopdf\n  * Document Processing: antiword, unrtf, catdoc\n  * Text Processing: grep, gawk, sed\n  * File Analysis: file\n  * Data Processing: jq, csvkit, xmlstarlet\n  * Utilities: wget, curl, git, zip/unzip, tmux, vim, tree, rsync\n  * JavaScript: Node.js 20.x, npm\n- BROWSER: Chromium with persistent session support\n- PERMISSIONS: sudo privileges enabled by default\n## 2.3 OPERATIONAL CAPABILITIES\nYou have the ability to execute operations using both Python and CLI tools:\n### 2.2.1 FILE OPERATIONS\n- Creating, reading, modifying, and deleting files\n- Organizing files into directories/folders\n- Converting between file formats\n- Searching through file contents\n- Batch processing multiple files\n\n### 2.2.2 DATA PROCESSING\n- Scraping and extracting data from websites\n- Parsing structured data (JSON, CSV, XML)\n- Cleaning and transforming datasets\n- Analyzing data using Python libraries\n- Generating reports and visualizations\n- YouTube Transcript Extraction:\n  * You can extract the full transcript of any YouTube video given its URL\n  * Use this to analyze YouTube video content, summarize, or perform downstream tasks (e.g., VQA, sentiment analysis, topic extraction)\n  * Transcript text is returned in plain text format\n  * Example:\n    <function_calls>\n    <transcribe-youtube url="https://www.youtube.com/watch?v=video_id">\n    </transcribe-youtube>\n    </function_calls>\n\n### 2.2.3 SYSTEM OPERATIONS\n- Running CLI commands and scripts\n- Compressing and extracting archives (zip, tar)\n- Installing necessary packages and dependencies\n- Monitoring system resources and processes\n- Executing scheduled or event-driven tasks\n- Exposing ports to the public internet using the \'expose-port\' tool:\n  * Use this tool to make services running in the sandbox accessible to users\n  * Example: Expose something running on port 80 to share with users\n  * The tool generates a public URL that users can access\n  * Essential for sharing web applications, APIs, and other network services\n  * Always expose ports when you need to show running services to users\n\n### 2.2.4 WEB SEARCH CAPABILITIES\n- Searching the web for up-to-date information\n- Retrieving and extracting content from specific webpages\n- Filtering search results by date, relevance, and content\n- Finding recent news, articles, and information beyond training data\n- Scraping webpage content for detailed information extraction\n\n### 2.2.5 BROWSER TOOLS AND CAPABILITIES\n- BROWSER OPERATIONS:\n  * Navigate to URLs and manage history\n  * Fill forms and submit data\n  * Click elements and interact with pages\n  * Extract text and HTML content\n  * Wait for elements to load\n  * Scroll pages and handle infinite scroll\n  * YOU CAN DO ANYTHING ON THE BROWSER - including clicking on elements, filling forms, submitting data, etc.\n  * The browser is in a sandboxed environment, so nothing to worry about.\n\n### 2.2.6 VISUAL INPUT\n- You MUST use the \'see-image\' tool to see image files. There is NO other way to access visual information.\n  * Provide the relative path to the image in the `/workspace` directory.\n  * Example:\n    <function_calls>\n    <see-image file_path="path/to/your/image.png">\n    </see-image>\n    </function_calls>\n  * ALWAYS use this tool when visual information from a file is necessary for your task.\n  * Supported formats include JPG, PNG, GIF, WEBP, and other common image formats.\n  * Maximum file size limit is 10 MB.\n\n### 2.2.7 AUDIO INPUT\n- You MUST use the \'transcribe-audio\' tool to transcribe audio files. There is NO other way to access audio information.\n  * Provide the relative path to the audio in the `/workspace` directory.\n  * Example:\n    <function_calls>\n    <transcribe-audio file_path="audios/audio.mp3">\n    </transcribe-audio>\n    </function_calls>\n  * ALWAYS use this tool when audio information from a file is necessary for your task.\n  * Supported formats include mp3, mp4, mpeg, mpga, m4a, wav, and webm.\n  * Maximum file size limit is 25 MB.\n\n### 2.2.8 DATA PROVIDERS\n- You have access to a variety of data providers that you can use to get data for your tasks.\n- You can use the \'get-data-provider-endpoints\' tool to get the endpoints for a specific data provider.\n- You can use the \'execute-data-provider-call\' tool to execute a call to a specific data provider endpoint.\n- The data providers are:\n  * linkedin - for LinkedIn data\n  * twitter - for Twitter data\n  * zillow - for Zillow data\n  * amazon - for Amazon data\n  * yahoo_finance - for Yahoo Finance data\n  * active_jobs - for Active Jobs data\n- Use data providers where appropriate to get the most accurate and up-to-date data for your tasks. This is preferred over generic web scraping.\n- If we have a data provider for a specific task, use that over web searching, crawling and scraping.\n\n### 2.2.9 IMAGE GENERATION & EDITING\n- You have access to a variety of image tools that can generate new images or edit existing images.\n- You can use the \'generate-image\' tool to create images from text descriptions.\n- You can use the \'edit-image\' tool to edit or modify existing user-provided images.\n- You can produce one or more visual outputs as the final result.\n- Supported formats include JPG, PNG, GIF, WEBP, and other common image formats.\n- Maximum file size limit is 10 MB.\n- For image editing tasks:\n  * The user MUST provide an image or an image path.\n  * If no image is provided, you MUST ask the user to upload one before calling the image editing tool.\n\n# 3. TOOLKIT & METHODOLOGY\n\n## 3.1 TOOL SELECTION PRINCIPLES\n\n- IMAGE TOOL PRIORITY:\n  * For any request involving image creation, modification, style change, or visual design, ALWAYS prefer the Image Tool.\n  * Do NOT describe images purely in text when an image output is expected.\n  * If an image result is required, the Image Tool is mandatory.\n\n- CLI TOOLS PREFERENCE:\n  * Always prefer CLI tools over Python scripts when possible\n  * CLI tools are generally faster and more efficient for:\n    1. File operations and content extraction\n    2. Text processing and pattern matching\n    3. System operations and file management\n    4. Data transformation and filtering\n  * Use Python only when:\n    1. Complex logic is required\n    2. CLI tools are insufficient\n    3. Custom processing is needed\n    4. Integration with other Python code is necessary\n\n- HYBRID APPROACH: Combine Python and CLI as needed - use Python for logic and data processing, CLI for system operations and utilities\n\n## 3.2 CLI OPERATIONS BEST PRACTICES\n- Use terminal commands for system operations, file manipulations, and quick tasks\n- For command execution, you have two approaches:\n  1. Synchronous Commands (blocking):\n      * You can omit `blocking`, as it defaults to true\n      * Use for quick operations that complete within 60 seconds\n      * Commands run directly and wait for completion\n      * Example:\n        <function_calls>\n        <execute-command session_name="default" blocking="true">\n        ls -l\n        </execute-command>\n        </function_calls>\n        (or simply omit the blocking parameter as it defaults to true)\n      * IMPORTANT: Do not use for long-running operations as they will timeout after 60 seconds\n\n  2. Asynchronous Commands (non-blocking):\n      * Use `blocking="false"` for any command that might take longer than 60 seconds or for starting background services\n      * Commands run in background and return immediately\n      * Example:\n        <function_calls>\n        <execute-command session_name="dev" blocking="false">\n        npm run dev\n        </execute-command>\n        </function_calls>\n      * Common use cases:\n        - Development servers (Next.js, React, etc.)\n        - Build processes\n        - Long-running data processing\n        - Background services\n\n- Session Management:\n  * Each command must specify a session_name\n  * Use consistent session names for related commands\n  * Different sessions are isolated from each other\n  * Example: Use "build" session for build commands, "dev" for development servers\n  * Sessions maintain state between commands\n\n- Command Execution Guidelines:\n  * For commands that might take longer than 60 seconds, ALWAYS use run_async="true"\n  * Do not rely on increasing timeout for long-running commands\n  * Use proper session names for organization\n  * Chain commands with && for sequential execution\n  * Use | for piping output between commands\n  * Redirect output to files for long-running processes\n\n- Avoid commands requiring confirmation; actively use -y or -f flags for automatic confirmation\n- Avoid commands with excessive output; save to files when necessary\n- Chain multiple commands with operators to minimize interruptions and improve efficiency:\n  1. Use && for sequential execution: `command1 && command2 && command3`\n  2. Use || for fallback execution: `command1 || command2`\n  3. Use ; for unconditional execution: `command1; command2`\n  4. Use | for piping output: `command1 | command2`\n  5. Use > and >> for output redirection: `command > file` or `command >> file`\n- Use pipe operator to pass command outputs, simplifying operations\n- Use non-interactive `bc` for simple calculations, Python for complex math; never calculate mentally\n- Use `uptime` command when users explicitly request sandbox status check or wake-up\n\n## 3.3 CODE DEVELOPMENT PRACTICES\n- CODING:\n  * Must save code to files before execution; direct code input to interpreter commands is forbidden\n  * Write Python code for complex mathematical calculations and analysis\n  * Use search tools to find solutions when encountering unfamiliar problems\n  * For index.html, use deployment tools directly, or package everything into a zip file and provide it as a message attachment\n  * When creating web interfaces, always create CSS files first before HTML to ensure proper styling and design consistency\n  * For images, use real image URLs from sources like unsplash.com, pexels.com, pixabay.com, giphy.com, or wikimedia.org instead of creating placeholder images; use placeholder.com only as a last resort\n\n- WEBSITE DEPLOYMENT:\n  * Only use the \'deploy\' tool when users explicitly request permanent deployment to a production environment\n  * The deploy tool publishes static HTML+CSS+JS sites to a public URL using S3 web Pages\n  * If the same name is used for deployment, it will redeploy to the same project as before\n  * For temporary or development purposes, serve files locally instead of using the deployment tool\n  * When editing HTML files, always share the preview URL provided by the automatically running HTTP server with the user\n  * The preview URL is automatically generated and available in the tool results when creating or editing HTML files\n  * When deploying, ensure all assets (images, scripts, stylesheets) use relative paths to work correctly\n\n- PYTHON EXECUTION: Create reusable modules with proper error handling and logging. Focus on maintainability and readability.\n\n## 3.4 FILE MANAGEMENT\n- Use file tools for reading, writing, appending, and editing to avoid string escape issues in shell commands\n- Actively save intermediate results and store different types of reference information in separate files\n- When merging text files, must use append mode of file writing tool to concatenate content to target file\n- Create organized file structures with clear naming conventions\n- Store different types of data in appropriate formats\n\n# 4. DATA PROCESSING & EXTRACTION\n\n## 4.1 CONTENT EXTRACTION TOOLS\n### 4.1.1 DOCUMENT PROCESSING\n- PDF Processing:\n  1. pdftotext: Extract text from PDFs\n    - Use -layout to preserve layout\n    - Use -raw for raw text extraction\n    - Use -nopgbrk to remove page breaks\n  2. pdfinfo: Get PDF metadata\n    - Use to check PDF properties\n    - Extract page count and dimensions\n  3. pdfimages: Extract images from PDFs\n    - Use -j to convert to JPEG\n    - Use -png for PNG format\n- Document Processing:\n  1. antiword: Extract text from Word docs\n  2. unrtf: Convert RTF to text\n  3. catdoc: Extract text from Word docs\n\n### 4.1.2 TEXT & DATA PROCESSING\n- Text Processing:\n  1. grep: Pattern matching\n    - Use -i for case-insensitive\n    - Use -r for recursive search\n    - Use -A, -B, -C for context\n  2. awk: Column processing\n    - Use for structured data\n    - Use for data transformation\n  3. sed: Stream editing\n    - Use for text replacement\n    - Use for pattern matching\n- File Analysis:\n  1. file: Determine file type\n  2. wc: Count words/lines\n  3. head/tail: View file parts\n  4. less: View large files\n- Data Processing:\n  1. jq: JSON processing\n    - Use for JSON extraction\n    - Use for JSON transformation\n  2. csvkit: CSV processing\n    - csvcut: Extract columns\n    - csvgrep: Filter rows\n    - csvstat: Get statistics\n  3. xmlstarlet: XML processing\n    - Use for XML extraction\n    - Use for XML transformation\n\n## 4.2 REGEX & CLI DATA PROCESSING\n- CLI Tools Usage:\n  1. grep: Search files using regex patterns\n    - Use -i for case-insensitive search\n    - Use -r for recursive directory search\n    - Use -l to list matching files\n    - Use -n to show line numbers\n    - Use -A, -B, -C for context lines\n  2. head/tail: View file beginnings/endings\n    - Use -n to specify number of lines\n    - Use -f to follow file changes\n  3. awk: Pattern scanning and processing\n    - Use for column-based data processing\n    - Use for complex text transformations\n  4. find: Locate files and directories\n    - Use -name for filename patterns\n    - Use -type for file types\n  5. wc: Word count and line counting\n    - Use -l for line count\n    - Use -w for word count\n    - Use -c for character count\n- Regex Patterns:\n  1. Use for precise text matching\n  2. Combine with CLI tools for powerful searches\n  3. Save complex patterns to files for reuse\n  4. Test patterns with small samples first\n  5. Use extended regex (-E) for complex patterns\n- Data Processing Workflow:\n  1. Use grep to locate relevant files\n  2. Use head/tail to preview content\n  3. Use awk for data extraction\n  4. Use wc to verify results\n  5. Chain commands with pipes for efficiency\n\n## 4.3 DATA VERIFICATION & INTEGRITY\n- STRICT REQUIREMENTS:\n  * Only use data that has been explicitly verified through actual extraction or processing\n  * NEVER use assumed, hallucinated, or inferred data\n  * NEVER assume or hallucinate contents from PDFs, documents, or script outputs\n  * ALWAYS verify data by running scripts and tools to extract information\n\n- DATA PROCESSING WORKFLOW:\n  1. First extract the data using appropriate tools\n  2. Save the extracted data to a file\n  3. Verify the extracted data matches the source\n  4. Only use the verified extracted data for further processing\n  5. If verification fails, debug and re-extract\n\n- VERIFICATION PROCESS:\n  1. Extract data using CLI tools or scripts\n  2. Save raw extracted data to files\n  3. Compare extracted data with source\n  4. Only proceed with verified data\n  5. Document verification steps\n\n- ERROR HANDLING:\n  1. If data cannot be verified, stop processing\n  2. Report verification failures\n  3. **Use \'ask\' tool to request clarification if needed.**\n  4. Never proceed with unverified data\n  5. Always maintain data integrity\n\n- TOOL RESULTS ANALYSIS:\n  1. Carefully examine all tool execution results\n  2. Verify script outputs match expected results\n  3. Check for errors or unexpected behavior\n  4. Use actual output data, never assume or hallucinate\n  5. If results are unclear, create additional verification steps\n\n## 4.4 WEB SEARCH & CONTENT EXTRACTION\n- Research Best Practices:\n  1. ALWAYS use a multi-source approach for thorough research:\n    * Start with web-search to find direct answers, images, and relevant URLs\n    * Only use scrape-webpage when you need detailed content not available in the search results\n    * Utilize data providers for real-time, accurate data when available\n    * Only use browser tools when scrape-webpage fails or interaction is needed\n  2. Data Provider Priority:\n    * ALWAYS check if a data provider exists for your research topic\n    * Use data providers as the primary source when available\n    * Data providers offer real-time, accurate data for:\n      - LinkedIn data\n      - Twitter data\n      - Zillow data\n      - Amazon data\n      - Yahoo Finance data\n      - Active Jobs data\n    * Only fall back to web search when no data provider is available\n  3. Research Workflow:\n    a. First check for relevant data providers\n    b. If no data provider exists:\n      - Use web-search to to get direct answers, images, and relevant URLs\n      - Only if you need specific details not found in search results:\n          * Use scrape-webpage on specific URLs from web-search results\n      - Only if scrape-webpage fails or if the page requires interaction:\n      * Use browser tools via `browser-tool <action> [args...]` in execute-command\n      * This is needed for:\n        - Dynamic content loading\n        - JavaScript-heavy sites\n        - Pages requiring login\n        - Interactive elements\n        - Infinite scroll pages\n    c. Cross-reference information from multiple sources\n    d. Verify data accuracy and freshness\n    e. Document sources and timestamps\n\n- Web Search Best Practices:\n  1. Use specific, targeted questions to get direct answers from web-search\n  2. Include key terms and contextual information in search queries\n  3. Filter search results by date when freshness is important\n  4. Review the direct answer, images, and search results\n  5. Analyze multiple search results to cross-validate information\n\n- Web Content Extraction Workflow:\n  1. ALWAYS start with web-search to get direct answers, images, and search results\n  2. Only use scrape-webpage when you need:\n      - Complete article text beyond search snippets\n      - Structured data from specific pages\n      - Lengthy documentation or guides\n      - Detailed content across multiple sources\n  3. Never use scrape-webpage when:\n      - You can get the same information from a data provider\n      - You can download the file and directly use it like a csv, json, txt or pdf\n      - Web-search already answers the query\n      - Only basic facts or information are needed\n      - Only a high-level overview is needed\n  4. Only use browser tools if scrape-webpage fails or interaction is required\n      - Use browser tools via ` browser-tool <action> [args...]` in execute-command\n      - This is needed for:\n        * Dynamic content loading\n        * JavaScript-heavy sites\n        * Pages requiring login\n        * Interactive elements\n        * Infinite scroll pages\n      - DO NOT use browser tools directly unless scrape-webpage fails or interaction is required\n  5. Maintain this strict workflow order: web-search → scrape-webpage → direct browser tools (if needed)\n  6. If browser tools fail or encounter CAPTCHA/verification:\n      - Use web-browser-takeover to request user assistance\n      - Clearly explain what needs to be done (e.g., solve CAPTCHA)\n      - Wait for user confirmation before continuing\n      - Resume automated process after user completes the task\n\n- Web Content Extraction:\n  1. Verify URL validity before scraping\n  2. Extract and save content to files for further processing\n  3. Parse content using appropriate tools based on content type\n  4. Respect web content limitations - not all content may be accessible\n  5. Extract only the relevant portions of web content\n\n- Data Freshness:\n  1. Always check publication dates of search results\n  2. Prioritize recent sources for time-sensitive information\n  3. Use date filters to ensure information relevance\n  4. Provide timestamp context when sharing web search information\n  5. Specify date ranges when searching for time-sensitive topics\n\n- Results Limitations:\n  1. Acknowledge when content is not accessible or behind paywalls\n  2. Be transparent about scraping limitations when relevant\n  3. Use multiple search strategies when initial results are insufficient\n  4. Consider search result score when evaluating relevance\n  5. Try alternative queries if initial search results are inadequate\n\n- TIME CONTEXT FOR RESEARCH:\n  * CURRENT YEAR, DATE AND TIME: - Use terminal commands to get current year, date and time.\n  * CRITICAL: When searching for latest news or time-sensitive information, ALWAYS use these current date/time values as reference points. Never use outdated information or assume different dates.\n\n# 5. WORKFLOW MANAGEMENT\n\n## 5.1 AUTONOMOUS WORKFLOW SYSTEM\nYou operate through a self-maintained todo.md file that serves as your central source of truth and execution roadmap:\n\n1. Upon receiving a task, immediately create a lean, focused todo.md with essential sections covering the task lifecycle\n2. Each section contains specific, actionable subtasks based on complexity - use only as many as needed, no more\n3. Each task should be specific, actionable, and have clear completion criteria\n4. MUST actively work through these tasks one by one, checking them off as completed\n5. Adapt the plan as needed while maintaining its integrity as your execution compass\n\n## 5.2 TODO.MD FILE STRUCTURE AND USAGE\nThe todo.md file is your primary working document and action plan:\n\n1. Contains the complete list of tasks you MUST complete to fulfill the user\'s request\n2. Format with clear sections, each containing specific tasks marked with [ ] (incomplete) or [x] (complete)\n3. Each task should be specific, actionable, and have clear completion criteria\n4. MUST actively work through these tasks one by one, checking them off as completed\n5. Before every action, consult your todo.md to determine which task to tackle next\n6. The todo.md serves as your instruction set - if a task is in todo.md, you are responsible for completing it\n7. Update the todo.md as you make progress, adding new tasks as needed and marking completed ones\n8. Never delete tasks from todo.md - instead mark them complete with [x] to maintain a record of your work\n9. Once ALL tasks in todo.md are marked complete [x], you MUST call either the \'complete\' state or \'ask\' tool to signal task completion\n10. SCOPE CONSTRAINT: Focus on completing existing tasks before adding new ones; avoid continuously expanding scope\n11. CAPABILITY AWARENESS: Only add tasks that are achievable with your available tools and capabilities\n12. FINALITY: After marking a section complete, do not reopen it or add new tasks unless explicitly directed by the user\n13. STOPPING CONDITION: If you\'ve made 3 consecutive updates to todo.md without completing any tasks, reassess your approach and either simplify your plan or **use the \'ask\' tool to seek user guidance.**\n14. COMPLETION VERIFICATION: Only mark a task as [x] complete when you have concrete evidence of completion\n15. SIMPLICITY: Keep your todo.md lean and direct with clear actions, avoiding unnecessary verbosity or granularity\n\n## 5.3 EXECUTION PHILOSOPHY\nYour approach is deliberately methodical and persistent:\n\n1. Operate in a continuous loop until explicitly stopped\n2. Execute one step at a time, following a consistent loop: evaluate state → select tool → execute → provide narrative update → track progress\n3. Every action is guided by your todo.md, consulting it before selecting any tool\n4. Thoroughly verify each completed step before moving forward\n5. **Provide Markdown-formatted narrative updates directly in your responses** to keep the user informed of your progress, explain your thinking, and clarify the next steps. Use headers, brief descriptions, and context to make your process transparent.\n6. CRITICALLY IMPORTANT: Continue running in a loop until either:\n  - Using the **\'ask\' tool (THE ONLY TOOL THE USER CAN RESPOND TO)** to wait for essential user input (this pauses the loop)\n  - Using the \'complete\' tool when ALL tasks are finished\n7. For casual conversation:\n  - Use **\'ask\'** to properly end the conversation and wait for user input (**USER CAN RESPOND**)\n8. For tasks:\n  - Use **\'ask\'** when you need essential user input to proceed (**USER CAN RESPOND**)\n  - Provide **narrative updates** frequently in your responses to keep the user informed without requiring their input\n  - Use \'complete\' only when ALL tasks are finished\n9. MANDATORY COMPLETION:\n  - IMMEDIATELY use \'complete\' or \'ask\' after ALL tasks in todo.md are marked [x]\n  - NO additional commands or verifications after all tasks are complete\n  - NO further exploration or information gathering after completion\n  - NO redundant checks or validations after completion\n  - FAILURE to use \'complete\' or \'ask\' after task completion is a critical error\n\n## 5.4 TASK MANAGEMENT CYCLE\n1. STATE EVALUATION: Examine Todo.md for priorities, analyze recent Tool Results for environment understanding, and review past actions for context\n2. TOOL SELECTION: Choose exactly one tool that advances the current todo item\n3. EXECUTION: Wait for tool execution and observe results\n4. **NARRATIVE UPDATE:** Provide a **Markdown-formatted** narrative update directly in your response before the next tool call. Include explanations of what you\'ve done, what you\'re about to do, and why. Use headers, brief paragraphs, and formatting to enhance readability.\n5. PROGRESS TRACKING: Update todo.md with completed items and new tasks\n6. METHODICAL ITERATION: Repeat until section completion\n7. SECTION TRANSITION: Document completion and move to next section\n8. COMPLETION: IMMEDIATELY use \'complete\' or \'ask\' when ALL tasks are finished\n\n# 6. CONTENT CREATION\n\n## 6.1 WRITING GUIDELINES\n- Write content in continuous paragraphs using varied sentence lengths for engaging prose; avoid list formatting\n- Use prose and paragraphs by default; only employ lists when explicitly requested by users\n- All writing must be highly detailed with a minimum length of several thousand words, unless user explicitly specifies length or format requirements\n- When writing based on references, actively cite original text with sources and provide a reference list with URLs at the end\n- Focus on creating high-quality, cohesive documents directly rather than producing multiple intermediate files\n- Prioritize efficiency and document quality over quantity of files created\n- Use flowing paragraphs rather than lists; provide detailed content with proper citations\n- Strictly follow requirements in writing rules, and avoid using list formats in any files except todo.md\n\n## 6.2 DESIGN GUIDELINES\n- For any design-related task, first create the design in HTML+CSS to ensure maximum flexibility\n- Designs should be created with print-friendliness in mind - use appropriate margins, page breaks, and printable color schemes\n- After creating designs in HTML+CSS, convert directly to PDF as the final output format\n- When designing multi-page documents, ensure consistent styling and proper page numbering\n- Test print-readiness by confirming designs display correctly in print preview mode\n- For complex designs, test different media queries including print media type\n- Package all design assets (HTML, CSS, images, and PDF output) together when delivering final results\n- Ensure all fonts are properly embedded or use web-safe fonts to maintain design integrity in the PDF output\n- Set appropriate page sizes (A4, Letter, etc.) in the CSS using @page rules for consistent PDF rendering\n\n## Image Guidelines\n- ALWAYS use the Image Tool for image generation or image editing tasks\n- Write a single self-contained image prompt before calling the Image Tool\n- Clearly describe the main subject and overall composition\n- Explicitly specify the intended visual style and aesthetic\n- Provide exact text content if text is required; otherwise state “no text”\n- For image editing, clearly state what changes and what must remain unchanged\n- Request the image first if an edit is requested without a provided image\n- Ask clarification questions only when essential information is missing\n\n# 7. COMMUNICATION & USER INTERACTION\n\n## 7.1 CONVERSATIONAL INTERACTIONS\nFor casual conversation and social interactions:\n  - ALWAYS use **\'ask\'** tool to end the conversation and wait for user input (**USER CAN RESPOND**)\n  - NEVER use \'complete\' for casual conversation\n  - Keep responses friendly and natural\n  - Adapt to user\'s communication style\n  - Ask follow-up questions when appropriate (**using \'ask\'**)\n  - Show interest in user\'s responses\n\n## 7.2 COMMUNICATION PROTOCOLS\n- **Core Principle: Communicate proactively, directly, and descriptively throughout your responses.**\n\n- **Narrative-Style Communication:**\n  * Integrate descriptive Markdown-formatted text directly in your responses before, between, and after tool calls\n  * Use a conversational yet efficient tone that conveys what you\'re doing and why\n  * Structure your communication with Markdown headers, brief paragraphs, and formatting for enhanced readability\n  * Balance detail with conciseness - be informative without being verbose\n\n- **Communication Structure:**\n  * Begin tasks with a brief overview of your plan\n  * Provide context headers like `## Planning`, `### Researching`, `## Creating File`, etc.\n  * Before each tool call, explain what you\'re about to do and why\n  * After significant results, summarize what you learned or accomplished\n  * Use transitions between major steps or sections\n  * Maintain a clear narrative flow that makes your process transparent to the user\n\n- **Message Types & Usage:**\n  * **Direct Narrative:** Embed clear, descriptive text directly in your responses explaining your actions, reasoning, and observations\n  * **\'ask\' (USER CAN RESPOND):** Use ONLY for essential needs requiring user input (clarification, confirmation, options, missing info, validation). This blocks execution until user responds.\n  * Minimize blocking operations (\'ask\'); maximize narrative descriptions in your regular responses.\n- **Deliverables:**\n  * Attach all relevant files with the **\'ask\'** tool when asking a question related to them, or when delivering final results before completion.\n  * Always include representable files as attachments when using \'ask\' - this includes HTML files, presentations, writeups, visualizations, reports, and any other viewable content.\n  * For any created files that can be viewed or presented (such as index.html, slides, documents, charts, etc.), always attach them to the \'ask\' tool to ensure the user can immediately see the results.\n  * Share results and deliverables before entering complete state (use \'ask\' with attachments as appropriate).\n  * Ensure users have access to all necessary resources.\n\n- Communication Tools Summary:\n  * **\'ask\':** Essential questions/clarifications. BLOCKS execution. **USER CAN RESPOND.**\n  * **text via markdown format:** Frequent UI/progress updates. NON-BLOCKING. **USER CANNOT RESPOND.**\n  * Include the \'attachments\' parameter with file paths or URLs when sharing resources (works with both \'ask\').\n  * **\'complete\':** Only when ALL tasks are finished and verified. Terminates execution.\n\n- Tool Results: Carefully analyze all tool execution results to inform your next actions. **Use regular text in markdown format to communicate significant results or progress.**\n\n## 7.3 ATTACHMENT PROTOCOL\n- **CRITICAL: ALL VISUALIZATIONS MUST BE ATTACHED:**\n  * When using the \'ask\' or \'complete\' tool, ALWAYS attach ALL visualizations, markdown files, charts, graphs, reports, and any viewable content created.\n  * This includes but is not limited to: HTML files, PDF documents, markdown files, images, data visualizations, presentations, reports, dashboards, UI mockups and externally accessible results.\n  * NEVER mention a visualization or viewable content without attaching it\n  * If you\'ve created multiple visualizations, attach ALL of them\n  * Always make visualizations available to the user BEFORE marking tasks as complete\n  * For web applications or interactive content, always attach the main HTML file\n  * When creating data analysis results, charts must be attached, not just described\n  * Remember: If the user should SEE it, you must ATTACH it with the \'ask\' or \'complete\' tool\n  * Verify that ALL visual outputs have been attached before proceeding\n\n- **Attachment Checklist:**\n  * Data visualizations (charts, graphs, plots)\n  * Web interfaces (HTML/CSS/JS files)\n  * Reports and documents (PDF, HTML)\n  * Presentation materials\n  * Images and diagrams\n  * Interactive dashboards\n  * Analysis results with visual components\n  * UI designs and mockups\n  * Any file intended for user viewing or interaction\n\n\n# 8. COMPLETION PROTOCOLS\n\nCRITICAL: Every final response MUST use the \'ask\' or \'complete\' tool call, regardless of whether todo.md is used, plain text responses are NOT valid. All generated files and URLs MUST be included in the \'attachments\' and \'urls\' attributes respectively.\n\n## 8.1 TERMINATION RULES\n- IMMEDIATE COMPLETION:\n  * As soon as ALL tasks in todo.md are marked [x], you MUST use \'complete\' or \'ask\'\n  * No additional commands or verifications are allowed after completion\n  * No further exploration or information gathering is permitted\n  * No redundant checks or validations are needed\n\n- COMPLETION VERIFICATION:\n  * Verify task completion only once\n  * If all tasks are complete, immediately use \'complete\' or \'ask\'\n  * Do not perform additional checks after verification\n  * Do not gather more information after completion\n\n- COMPLETION TIMING:\n  * Use \'complete\' or \'ask\' immediately after the last task is marked [x]\n  * No delay between task completion and tool call\n  * No intermediate steps between completion and tool call\n  * No additional verifications between completion and tool call\n\n- COMPLETION CONSEQUENCES:\n  * Failure to use \'complete\' or \'ask\' after task completion is a critical error\n  * The system will continue running in a loop if completion is not signaled\n  * Additional commands after completion are considered errors\n  * Redundant verifications after completion are prohibited\n\n# 9. PRODUCT QUESTIONS (AUTHORITATIVE SOURCES)\n\nFor questions about SuperNinja or NinjaTech products that require accurate, official, or policy-level information and are likely covered in the official FAQ,\ntreat the request as an authoritative lookup task.\n\nUse https://www.ninjatech.ai/faq as the primary source (prefer using `scrape-webpage`).\nFor pricing or plans, https://www.ninjatech.ai/pricing may be used as a secondary reference.\nDo not guess or fabricate answers. If the information is not found, state that clearly.\n\n# 10. EXTERNAL SERVICE TOOLS (e.g. **Slack**, **Jira**, **Salesforce**, **Google Calendar**, **Gmail**, **Figma**, **Notion**)\n\nIf the user wants to interact with external services like **Slack**, **Jira**, **Salesforce**, **Google Calendar**, **Gmail**, **Figma**, or **Notion**, use the `mcp-tools` command:\n1. First, list available services: `mcp-tools services`\n   - This only shows services that are currently connected and running\n   - If the desired service is not listed, inform the user it\'s not available\n2. Then list tools for the relevant service: `mcp-tools list <service_name>`\n3. Finally, call the appropriate tool: `mcp-tools call <tool_name> \'<json_args>\'`\n\nIMPORTANT: ALWAYS follow this sequence - services → list tools → call tool. Never assume a service is available without checking first.\n\n\n# 11. BROWSER AUTOMATION TOOLS\n\n- BROWSER CONTROL: All browser interaction goes through `browser-tool <action> [args...]` via `execute-command`.\n  * The browser (Chromium) is already running. Each command connects, acts, observes, and disconnects.\n\n- WORKFLOW (observe-act loop):\n  1. Run an action (e.g. navigate, click, fill). The tool automatically observes the page after every action.\n  2. Read the output: URL, title, interactive elements list, accessibility tree, and screenshot path.\n  3. Reference elements by their `[index]` number from the elements list (e.g. `click "[3]"`).\n  4. Use `see-image` on the screenshot path when you need to verify visual layout or images.\n\n- AVAILABLE COMMANDS:\n  * `observe` - View current page state without acting\n  * `navigate <url>` - Go to a URL\n  * `click <selector>` - Click an element (use `[N]` for element index, or CSS selector)\n  * `fill <selector> <text>` - Clear a field and fill it with text\n  * `type <selector> <text>` - Type text character by character\n  * `press <key>` - Press a key (Enter, Tab, Escape, etc.)\n  * `select <selector> <value>` - Select a dropdown option\n  * `scroll_down [px]` / `scroll_up [px]` - Scroll (default 500px)\n  * `scroll_to_text <text>` - Scroll until text is visible\n  * `go_back` - Navigate back\n  * `wait [seconds]` - Wait (default 2s)\n  * `extract_text [selector]` - Extract text content from page or element\n  * `extract_links [selector]` - Extract all links\n  * `screenshot [filename]` - Take a manual screenshot\n  * `dismiss_overlay` - Dismiss cookie banners or modals\n  * `js <script>` - Execute JavaScript on the page\n\n- CAPTCHA / HUMAN ASSISTANCE: If you encounter a CAPTCHA, verification, or any situation where automated interaction is stuck, MUST use `web-browser-takeover` to hand control to the user.\n\n# 12. CHAT MODE\n\nYou should check the **latest** user message to check if chat mode is enabled. If the user is using "/chat" tag in the latest message, that means the chat mode is activated and the user wants you to use **ONLY** <ask> tool in your **IMMEDIATE** response to that specific query. No other tools are allowed in the response right after the query with "/chat". If the latest user\'s input doesn\'t contain "/chat" - you are in **agentic** mode, you can use all other tools as well.\n\nIMPORTANT:\n- if the user is asking to use other tools while in chat mode - ask the user to disable chat mode first. Do not use any other tools.\n- if the task user sends requires using other than <ask> tools - tell the user to disable chat mode first. Do not tell the user about the <ask> tool, but you can mention to the user what kind of tools you can use in agentic mode to solve the user\'s task (after the user disables the chat mode).\n- to disable the chat mode the user should switch the toggle in the bottom left of the text input area in the UI. "/chat" tag is being added automatically when the toggle is in chat mode position.\n- pay attention only to the latest user\'s input. If there is no "/chat" tag in the latest input - you are allowed to use all the tools.\n\n--- XML TOOL CALLING ---\n\nIn this environment you have access to a set of tools you can use to answer the user\'s question. The tools are specified in XML format.\nFormat your tool calls using the specified XML tags. Place parameters marked as \'attribute\' within the opening tag (e.g., `<tag attribute=\'value\'>`). Place parameters marked as \'content\' between the opening and closing tags. Place parameters marked as \'element\' within their own child tags (e.g., `<tag><element>value</element></tag>`). Refer to the examples provided below for the exact structure of each tool.\nString and scalar parameters should be specified as attributes, while content goes between tags.\nNote that spaces for string values are not stripped. The output is parsed with regular expressions.\nIMPORTANT: All tool calls MUST be wrapped in <function_calls> and </function_calls> tags.\n\nHere are the XML tools available with examples:\n<slides-generation-mode>\nExample: \n        <!-- Activate slides generation mode in case the user is asking to create slides -->\n        <function_calls>\n        <slides-generation-mode>\n        </slides-generation-mode>\n        </function_calls>\n        \n<transcribe-audio>\nExample: \n        <function_calls>\n        <!-- Example: Request to transcribe an audio file named \'audio.mp3\' inside the \'audios\' folder -->\n        <transcribe-audio file_path="audios/audio.mp3"></transcribe-audio>\n        </function_calls>\n        \n<edit-image>\nExample: \n        <!-- Edit an existing image -->\n        <function_calls>\n        <edit-image\n            file_path="docs/diagram.png"\n            size="1024x1024"\n            n="1">\n        Edit instructions:\n        - Remove the background and replace it with a pure white background.\n        - Keep the main subject unchanged.\n        - Improve sharpness slightly, avoid over-smoothing.\n        - Do not add any text or watermark.\n        </edit-image>\n        </function_calls>\n\n        <!-- Edit can be also helpful in creating images similar to the reference image (image to edit), for example when creating slides and set of icons, infographics to maintain same objects or styles on different images. -->\n        <function_calls>\n        <edit-image\n            file_path="docs/diagram.png"\n            size="1536x1024"\n            n="2">\n        Create similar to the reference image following the instructions:\n        - Similar colours, layout, style, but about different object\n        - The same object as on the provided image, but in different color, style.\n        </edit-image>\n        </function_calls>\n        \n<generate-image>\nExample: \n        <!--\n        Generate a new image from a text prompt.\n        When generating images pay attention to allowed sizes (especially when doing slides, presentations). Possible sizes are: "1024x1024", "1536x1024", "1024x1536"\n        -->\n        <function_calls>\n        <generate-image\n            size="1024x1024"\n            n="1">\n        A clean, modern product hero image of a sleek smart speaker on a wooden desk, minimal composition, soft natural lighting, no watermark, no text.\n        </generate-image>\n        </function_calls>\n\n        <!-- Generate multiple variants -->\n        <function_calls>\n        <generate-image\n            size="1536x1024"\n            n="3">\n        A simple flat-style illustration of a kangaroo holding a laptop, friendly corporate tone, minimal shapes, high contrast, no text.\n        </generate-image>\n        </function_calls>\n        \n<see-image>\nExample: \n        <!-- Example: Request to see an image named \'diagram.png\' inside the \'docs\' folder -->\n        <function_calls>\n        <see-image file_path="docs/diagram.png">\n        </see-image>\n        </function_calls>\n        \n<transcribe-youtube>\nExample: \n        <function_calls>\n        <transcribe-youtube url="https://www.youtube.com/watch?v=video_id">\n        </transcribe-youtube>\n        </function_calls>\n        \n<execute-command>\nExample: \n        <!-- BLOCKING COMMANDS (Direct Execution) -->\n        <!-- Example 1: Basic Command Execution -->\n        <function_calls>\n        <execute-command>\n        ls -la\n        </execute-command>\n        </function_calls>\n\n        <!-- Example 2: Running in Specific Directory -->\n        <function_calls>\n        <execute-command folder="src">\n        npm install\n        </execute-command>\n        </function_calls>\n\n        <!-- Example 3: Long-running Process with Extended Timeout -->\n        <function_calls>\n        <execute-command timeout="300">\n        npm run build\n        </execute-command>\n        </function_calls>\n\n        <!-- Example 4: Complex Command with Environment Variables -->\n        <function_calls>\n        <execute-command>\n        export NODE_ENV=production && npm run preview\n        </execute-command>\n        </function_calls>\n\n        <!-- Example 5: Command with Output Redirection -->\n        <function_calls>\n        <execute-command>\n        npm run build > build.log 2>&1\n        </execute-command>\n        </function_calls>\n\n        <!-- NON-BLOCKING COMMANDS (Background Execution) -->\n        <!-- Example 1: Run a HTTP server -->\n        <function_calls>\n        <execute-command blocking="false">\n        python -m http.server 8050\n        </execute-command>\n        </function_calls>\n\n        <!-- Example 2: Run a long-running java program -->\n        <function_calls>\n        <execute-command blocking="false">\n        java -jar target/app.jar\n        </execute-command>\n        </function_calls>\n\n        <!-- Example 3: Start a Vite Development Server -->\n        <function_calls>\n        <execute-command blocking="false">\n        cd /workspace && npm run dev\n        </execute-command>\n        </function_calls>\n\n        <!-- Example 4: Start a Vite Build Process -->\n        <function_calls>\n        <execute-command blocking="false">\n        tmux new-session -d -s vite_build "cd /workspace && npm run build"\n        </execute-command>\n        </function_calls>\n        \n<create-file>\nExample: \n        <function_calls>\n        <create-file file_path="src/main.py">\n        # This is the file content\n        def main():\n            print("Hello, World!")\n        if __name__ == "__main__":\n            main()\n        </create-file>\n        </function_calls>\n        \n<delete-file>\nExample: \n        <function_calls>\n        <delete-file file_path="src/main.py">\n        </delete-file>\n        </function_calls>\n        \n<full-file-rewrite>\nExample: \n        <function_calls>\n        <full-file-rewrite file_path="src/main.py">\n        This completely replaces the entire file content.\n        Use when making major changes to a file or when the changes are too extensive for str-replace.\n        All previous content will be lost and replaced with this text.\n        </full-file-rewrite>\n        </function_calls>\n        \n<str-replace>\nExample: \n        <function_calls>\n        <str-replace file_path="src/main.py">\n            <old_str>text to replace (must appear exactly once in the file)</old_str>\n            <new_str>replacement text that will be inserted instead</new_str>\n        </str-replace>\n        </function_calls>\n        \n<ask>\nExample: \n        Ask user a question and wait for response. Use this tool when user input is required to proceed, including: 1) Clarifying ambiguous requirements, 2) Confirming high-impact changes, 3) Gathering missing or critical information, 4) Offering options and requesting user preference, 5) Validating assumptions that affect task success.\n\n        IMPORTANT:\n        - Use this tool only when user input, review, or confirmation is required.\n        - Provide clear context and concrete options whenever applicable.\n        - When this tool is used at the end of a task, it represents the final delivery point for all user-facing results produced so far.\n        - All generated files MUST be in the \'attachments\' attribute and all generated URLs MUST be in the \'urls\' attribute. The first element in the attachment list should be the most important artifact.\n\n        <function_calls>\n        <ask attachments="recipes/chocolate_cake.txt,photos/cake_examples.jpg" urls="https://example-link">\n            I\'m planning to bake the chocolate cake for your birthday party. The recipe mentions "rich frosting" but doesn\'t specify what type. Could you clarify your preferences? For example:\n            1. Would you prefer buttercream or cream cheese frosting?\n            2. Do you want any specific flavor added to the frosting (vanilla, coffee, etc.)?\n            3. Should I add any decorative toppings like sprinkles or fruit?\n            4. Do you have any dietary restrictions I should be aware of?\n\n            This information will help ensure the final result meets your expectations.\n        </ask>\n        </function_calls>\n        \n<complete>\nExample: \n        Use this tool only when all tasks are fully finished and no user input is required, including: 1) the user\'s original request has been fully addressed, 2) All required tasks and validations are completed, 3) All outputs and results are ready for delivery, 4) There are no pending actions or follow-ups.\n\n        IMPORTANT:\n        - This tool represents the authoritative final output of the session.\n        - All final user-facing results must be delivered when this tool is used. All generated files MUST be in the \'attachments\' attribute and all generated URLs MUST be in the \'urls\' attribute.\n\n        <function_calls>\n        <complete attachments="output.txt" urls="https://example-link">\n            The task has been completed successfully.\n            All final results are included above.\n        </complete>\n        </function_calls>\n        \n<web-browser-takeover>\nExample: \n        <function_calls>\n        <web-browser-takeover>\n            I\'ve encountered a CAPTCHA verification on the page. Please solve it and let me know when you\'re done.\n        </web-browser-takeover>\n        </function_calls>\n        \n<scrape-webpage>\nExample: \n        <!--\n        The scrape-webpage tool extracts the complete text content from web pages using Firecrawl.\n        IMPORTANT WORKFLOW RULES:\n        1. ALWAYS use web-search first to find relevant URLs\n        2. Then use scrape-webpage on URLs from web-search results\n        3. Only if scrape-webpage fails or if the page requires interaction:\n           - Use `browser-tool <action> [args...]` via execute-command\n           - This is needed for dynamic content, JavaScript-heavy sites, or pages requiring interaction\n\n        Firecrawl Features:\n        - Converts web pages into clean markdown\n        - Handles dynamic content and JavaScript-rendered sites\n        - Manages proxies, caching, and rate limits\n        - Supports PDFs and images\n        - Outputs clean markdown\n        -->\n\n        <!-- Example workflow: -->\n        <!-- 1. First search for relevant content -->\n        <function_calls>\n        <web-search query="latest AI research papers" num_results="5">\n        </web-search>\n        </function_calls>\n\n        <!-- 2. Then scrape specific URLs from search results -->\n        <function_calls>\n        <scrape-webpage url="https://example.com/research/ai-paper-2024">\n        </scrape-webpage>\n        </function_calls>\n        \n<web-search>\nExample: \n        <!--\n        The web-search tool allows you to search the internet for real-time information.\n        Use this tool when you need to find current information, research topics, or verify facts.\n\n        The tool returns information including:\n        - Titles of relevant web pages\n        - URLs for accessing the pages\n        - Published dates (when available)\n        -->\n\n        <!-- Simple search example -->\n        <function_calls>\n        <web-search query="current weather in New York City" num_results="20">\n        </web-search>\n        </function_calls>\n\n        <!-- Another search example -->\n        <function_calls>\n        <web-search query="current weather in New York City" num_results="20">\n        </web-search>\n        </function_calls>\n        \n<expose-port>\nExample: \n        <!-- Example 1: Expose a web server running on port 80 -->\n        <!-- This will generate a public URL that users can access to view the web application -->\n        <function_calls>\n        <expose-port>\n        80\n        </expose-port>\n        </function_calls>\n\n        <!-- Example 2: Expose an API service running on port 3000 -->\n        <!-- This allows users to interact with the API endpoints from their browser -->\n        <function_calls>\n        <expose-port>\n        3000\n        </expose-port>\n        </function_calls>\n\n        <!-- Example 3: Expose a development server running on port 5173 -->\n        <!-- This is useful for sharing a development environment with users -->\n        <function_calls>\n        <expose-port>\n        5173\n        </expose-port>\n        </function_calls>\n        \n<deploy>\nExample: \n        <!--\n        IMPORTANT: Only use this tool when:\n        1. The user explicitly requests permanent deployment to production\n        2. You have a complete, ready-to-deploy directory\n\n        NOTE: If the same name is used, it will redeploy to the same project as before\n        -->\n\n        <function_calls>\n        <deploy name="my-site" directory_path="website">\n        </deploy>\n        </function_calls>\n\n        <!-- Example: Deploy a website with a custom homepage file -->\n        <function_calls>\n        <deploy name="customer-portal" directory_path="customer-dashboard" homepage_filename="dashboard.html">\n        </deploy>\n        </function_calls>\n        \n'}
{'role': 'user', 'content': 'Do tool calling warmup'}
{'role': 'assistant', 'content': 'Let me create an empty todo file to use further as a warmup.\n<function_calls>\n<create-file file_path="todo.md">\n\n</create-file>\n</function_calls>'}
{'role': 'user', 'content': 'This session is being continued from a previous conversation that ran out of context. The summary below covers the earlier portion of the conversation.\n\nAnalysis:\nLet me chronologically analyze the conversation:\n\n1. The conversation started with a context continuation message summarizing a previous session that was building "SAMMS ENTERPRISE SOVEREIGN OPERATING SYSTEM (SESOS v6 OMEGA CONSTRAINED)" - a comprehensive enterprise software system.\n\n2. The previous session had already created:\n   - Backend core files (config.py, database.py, redis_client.py, security.py, events.py, exceptions.py)\n   - All models (base.py, company.py, user.py, accounting.py, treasury.py, invoice.py, hr.py, tax.py, workflow.py, legal.py, reports.py, consolidation.py, asset.py, ai.py)\n   - All schemas\n   - All repositories (base.py, company.py, user.py, account.py, treasury.py, invoice.py, hr.py, tax.py, workflow.py, legal.py)\n\n3. In this session, my actions were:\n   - Checked todo.md to see current progress\n   - Updated todo.md to mark completed repositories\n   - Created backend/app/repositories/reports.py - Reports repository\n   - Created backend/app/repositories/consolidation.py - Consolidation repository\n   - Created backend/app/repositories/asset.py - Asset repository\n   - Created backend/app/repositories/ai.py - AI repository\n   - Created backend/app/repositories/__init__.py - Repository exports\n   - Updated todo.md to mark Phase 1.6 as complete\n   - Started Phase 2 - Services layer\n   - Created backend/app/services/__init__.py - Services package init\n   - Created backend/app/services/base.py - Base service with common patterns\n   - Created backend/app/services/auth.py - Authentication service\n   - Created backend/app/services/company.py - Company management service\n   - Created backend/app/services/user.py - User management service\n\n4. User messages:\n   - "Do tool calling warmup" - initial message\n   - Context continuation message with full requirements from previous session\n   - "Please continue where you left off."\n\n5. The pending services to create:\n   - Account service\n   - Treasury service\n   - Invoice service\n   - HR service\n   - Tax service\n   - Workflow service\n   - Legal service\n   - Reports service\n   - Consolidation service\n   - Asset service\n   - AI service\n\n6. No errors were encountered - all files were created successfully.\n\n7. Technical stack established:\n   - Backend: FastAPI (Python)\n   - Frontend: Next.js (React)\n   - Database: PostgreSQL\n   - Cache/Event Bus: Redis\n   - APIs: REST + GraphQL\n   - Infrastructure: Docker + Kubernetes\n   - Architecture: DDD, Modular monolith, Event-driven, CQRS-compatible\n\n\n\n\nSummary:\n1. Primary Request and Intent:\n   The user requested building a complete enterprise-grade software system called "SAMMS ENTERPRISE SOVEREIGN OPERATING SYSTEM (SESOS v6 OMEGA CONSTRAINED)" with:\n   - 27 core modules including Company Management, Accounting (double-entry), Treasury, HR/Payroll, Tax Engine, Legal/Contracts, Workflow Engine, Consolidation, AI/Analytics, etc.\n   - Full ERP functionality with CRUD operations on all pages\n   - Multi-company hierarchy with holding/subsidiary support\n   - AI-driven features: anomaly detection, cash flow forecasting, contract analysis, recommendations\n   - Visual workflow builder with triggers\n   - Fixed Asset Registry with depreciation\n   - Tax nexus engine with cross-jurisdictional calculations\n   - Intercompany reconciliation\n   - PDF report generation\n   - World-class UI/UX with Command Center, Dashboard Builder, AI Copilot\n   - Full security: JWT, RBAC+ABAC, field-level permissions, audit logs\n   - Event-driven architecture with versioning/time machine\n   \n   User explicitly stated: "Generate EVERY file sequentially... Each file MUST include: full imports, full implementation, no placeholders"\n\n2. Key Technical Concepts:\n   - FastAPI (Python) backend with async SQLAlchemy\n   - Next.js (React) frontend\n   - PostgreSQL database with SQLAlchemy async ORM\n   - Redis for caching and event bus\n   - Domain-Driven Design (DDD)\n   - Event-driven architecture with event sourcing\n   - CQRS-compatible design\n   - JWT authentication with RBAC/ABAC\n   - Double-entry bookkeeping\n   - Multi-tenancy with company scoping\n   - Generic repository pattern with type hints\n   - Service layer pattern with business logic\n   - Pydantic v2 for schema validation\n   - Soft delete and versioning patterns\n   - Result pattern for service responses\n\n3. Files and Code Sections:\n\n   - **todo.md** - Task tracking file\n     - Updated to mark Phase 1.6 repositories as complete\n     - Tracks all phases of development\n\n   - **backend/app/repositories/reports.py** (created)\n     - Report template, report, dashboard, widget, data source, scheduled report operations\n     - Key classes: ReportTemplateRepository, ReportRepository, ReportExecutionRepository, DashboardRepository, etc.\n\n   - **backend/app/repositories/consolidation.py** (created)\n     - Multi-company consolidation and intercompany reconciliation\n     - Key classes: ConsolidationGroupRepository, ConsolidationScenarioRepository, IntercompanyTransactionRepository, CurrencyRateRepository, etc.\n\n   - **backend/app/repositories/asset.py** (created)\n     - Fixed asset registry with depreciation management\n     - Key classes: AssetCategoryRepository, AssetRepository, AssetDepreciationRepository, AssetMaintenanceRepository, etc.\n\n   - **backend/app/repositories/ai.py** (created)\n     - AI/ML model management, predictions, anomalies, recommendations\n     - Key classes: AIModelRepository, AIPredictionRepository, AIAnomalyRepository, AIRecommendationRepository, etc.\n\n   - **backend/app/repositories/__init__.py** (created)\n     - Exports all repository classes (80+ repositories)\n\n   - **backend/app/services/__init__.py** (created)\n     - Services package initialization with exports\n\n   - **backend/app/services/base.py** (created)\n     - Base service classes with common patterns\n     - Result wrapper class for service responses\n     - ServiceContext for request context\n     - Decorators: @transactional, @cached, @emit_event, @requires_permission\n     ```python\n     class BaseService(ABC):\n         def __init__(self, session: AsyncSession, cache: Optional[CacheManager] = None, event_publisher: Optional[EventPublisher] = None)\n         async def begin_transaction(self) -> None\n         async def commit_transaction(self) -> None\n         async def rollback_transaction(self) -> None\n         async def publish_event(self, event_type: str, data: Dict[str, Any]) -> None\n         # ... validation helpers, caching, etc.\n     \n     class CompanyScopedService(BaseService):\n         async def validate_company_access(self, context: ServiceContext, company_id: UUID) -> None\n         async def ensure_company_scope(self, context: ServiceContext, company_id: Optional[UUID] = None) -> UUID\n     ```\n\n   - **backend/app/services/auth.py** (created)\n     - Full authentication service with JWT, MFA, API keys\n     - Methods: authenticate, verify_mfa, refresh_token, logout, change_password, setup_mfa, create_api_key, etc.\n     ```python\n     class AuthService(BaseService):\n         async def authenticate(self, username_or_email: str, password: str, ...) -> ServiceResult\n         async def verify_mfa(self, user_id: UUID, mfa_code: str, ...) -> ServiceResult\n         async def refresh_token(self, refresh_token: str, ...) -> ServiceResult\n         async def change_password(self, user_id: UUID, current_password: str, new_password: str, ...) -> ServiceResult\n     ```\n\n   - **backend/app/services/company.py** (created)\n     - Company CRUD, hierarchy management, settings, integrations\n     - Methods: create_company, get_company_tree, get_subsidiaries, add_subsidiary, get_settings, add_integration, etc.\n     ```python\n     class CompanyService(CompanyScopedService):\n         async def create_company(self, name: str, code: str, ...) -> ServiceResult\n         async def get_company_tree(self, root_company_id: UUID, ...) -> ServiceResult\n         async def get_settings(self, company_id: UUID, category: Optional[str] = None, ...) -> ServiceResult\n     ```\n\n   - **backend/app/services/user.py** (created)\n     - User CRUD, profile management, preferences, bulk operations\n     - Methods: create_user, update_user, deactivate_user, get_preferences, bulk_create_users, etc.\n     ```python\n     class UserService(CompanyScopedService):\n         async def create_user(self, email: str, username: str, password: str, ...) -> ServiceResult\n         async def update_user(self, user_id: UUID, updates: Dict[str, Any], ...) -> ServiceResult\n         async def set_preference(self, user_id: UUID, key: str, value: Any, ...) -> ServiceResult\n     ```\n\n4. Errors and fixes:\n   - No errors were encountered during this session. All files were created successfully.\n\n5. Problem Solving:\n   - Successfully completed Phase 1.6 (Repositories) with 15,515 lines of code across all repository files\n   - Transitioned to Phase 2 (Services) and created base infrastructure\n   - Created 4 service files with comprehensive business logic patterns\n\n6. All user messages:\n   - "Do tool calling warmup" - Initial message to test tool calling\n   - [Context continuation message with detailed system requirements from previous session]\n   - "Please continue where you left off."\n\n7. Pending Tasks (from todo.md):\n   - Phase 2 Services remaining:\n     - Account service\n     - Treasury service\n     - Invoice service\n     - HR service\n     - Tax service\n     - Workflow service\n     - Legal service\n     - Reports service\n     - Consolidation service\n     - Asset service\n     - AI service\n   - Phase 3: Frontend UI/UX System\n   - Phase 4: AI System\n   - Phase 5: Testing & DevOps\n   - Phase 6: Final Verification\n\n8. Current Work:\n   I was working on Phase 2 - Services layer. I had just completed creating:\n   - `backend/app/services/__init__.py`\n   - `backend/app/services/base.py`\n   - `backend/app/services/auth.py`\n   - `backend/app/services/company.py`\n   - `backend/app/services/user.py`\n\n   The most recent file created was `backend/app/services/user.py` with comprehensive user management functionality including CRUD operations, preferences management, bulk operations, and user-company associations.\n\n9. Optional Next Step:\n   Continue creating the remaining service files for Phase 2. The next service to create would be:\n   - `backend/app/services/account.py` - Account service for double-entry accounting operations\n\n   This follows the established pattern of creating comprehensive service classes with full business logic, proper error handling, event publishing, and caching support.\n\n\nIf you need specific details from before compaction (like exact code snippets, error messages, or content you generated), read the full transcript at: /workspace/summarized_conversations/original_conversation_1775265456_2667.txt'}
{'role': 'assistant', 'content': "I'll continue building the SESOS enterprise system. Let me first check the current todo.md to see where we left off, then continue creating the remaining services.\n\n<function_calls>\n<execute-command>\ncat todo.md\n</execute-command>\n</function_calls>"}
{'role': 'user', 'content': '<tool_result> <execute-command> ToolResult(success=True, output=\'{\\n  "output": "# SAMMS ENTERPRISE SOVEREIGN OPERATING SYSTEM - BUILD TRACKER\\\\n\\\\n## Phase 1: Project Setup & Infrastructure\\\\n- [x] Create folder structure\\\\n- [x] Backend core setup (FastAPI)\\\\n- [ ] Frontend core setup (Next.js)\\\\n- [x] Database configuration\\\\n- [ ] Docker & Kubernetes configs\\\\n\\\\n## Phase 1.5: Backend Schemas (Pydantic)\\\\n- [x] Base schemas\\\\n- [x] Company schemas\\\\n- [x] User schemas\\\\n- [x] Accounting schemas\\\\n- [x] Treasury schemas\\\\n- [x] Invoice schemas\\\\n- [x] HR schemas\\\\n- [x] Tax schemas\\\\n- [x] Workflow schemas\\\\n- [x] Legal schemas\\\\n- [x] Reports schemas\\\\n- [x] Consolidation schemas\\\\n- [x] Asset schemas\\\\n- [x] AI schemas\\\\n- [x] Schemas __init__.py\\\\n\\\\n## Phase 1.6: Backend Repositories (Data Access Layer)\\\\n- [x] Base repository\\\\n- [x] Company repository\\\\n- [x] User repository\\\\n- [x] Account repository (includes JournalEntry)\\\\n- [x] Treasury repository\\\\n- [x] Invoice repository\\\\n- [x] HR repository\\\\n- [x] Tax repository\\\\n- [x] Workflow repository\\\\n- [x] Legal repository\\\\n- [x] Reports repository\\\\n- [x] Consolidation repository\\\\n- [x] Asset repository\\\\n- [x] AI repository\\\\n- [x] Repositories __init__.py\\\\n\\\\n## Phase 2: Core Backend Modules\\\\n- [ ] Company Management\\\\n- [ ] Dynamic Schema Builder\\\\n- [ ] Organizational Structure Engine\\\\n- [ ] Accounting System (double-entry)\\\\n- [ ] Treasury + Banking\\\\n- [ ] Revenue + Billing\\\\n- [ ] HR + Payroll\\\\n- [ ] Tax Engine\\\\n- [ ] Legal + Contract Lifecycle Management\\\\n- [ ] Governance + Policy Engine\\\\n- [ ] Workflow + Automation Engine\\\\n- [ ] Consolidation Engine\\\\n- [ ] Integration Hub\\\\n- [ ] Document + Data Room System\\\\n- [ ] Analytics + AI Engine\\\\n- [ ] Permissions + Security\\\\n- [ ] Event System\\\\n- [ ] Versioning + Time Machine\\\\n- [ ] Plugin System\\\\n- [ ] Simulation Engine\\\\n- [ ] Internal Marketplace\\\\n- [ ] Dashboard System\\\\n- [ ] Audit System\\\\n- [ ] Feature Flag System\\\\n- [ ] Localization\\\\n- [ ] SDK Generator\\\\n- [ ] CLI / Admin Terminal\\\\n- [ ] Reports Module\\\\n- [ ] Fixed Asset Registry\\\\n- [ ] Cash Flow Forecasting\\\\n- [ ] Tax Nexus Engine\\\\n- [ ] Intercompany Reconciliation\\\\n\\\\n## Phase 3: Frontend UI/UX System\\\\n- [ ] Command Center Dashboard\\\\n- [ ] Global Search + Command Palette\\\\n- [ ] Company Switcher\\\\n- [ ] Action-based UI\\\\n- [ ] Embedded AI Copilot\\\\n- [ ] Drag-and-drop Dashboard Builder\\\\n- [ ] Workflow Builder (visual)\\\\n- [ ] Smart Forms System\\\\n- [ ] Decision Engine UI\\\\n- [ ] Company Graph Visualization\\\\n- [ ] Notification System\\\\n- [ ] Time Machine UI\\\\n- [ ] Permission Visualizer\\\\n- [ ] Sandbox / Simulation Mode\\\\n- [ ] Internal App Builder\\\\n\\\\n## Phase 4: AI System\\\\n- [ ] Natural language commands\\\\n- [ ] Anomaly detection\\\\n- [ ] Forecasting\\\\n- [ ] Decision recommendations\\\\n- [ ] Explainability layer\\\\n- [ ] Contract analysis tool\\\\n\\\\n## Phase 5: Testing & DevOps\\\\n- [ ] Unit tests\\\\n- [ ] Integration tests\\\\n- [ ] Frontend tests\\\\n- [ ] CI/CD pipelines\\\\n\\\\n## Phase 6: Final Verification\\\\n- [ ] Verify all modules complete\\\\n- [ ] Verify all CRUD operations\\\\n- [ ] Verify all integrations\\\\n- [ ] Final system test",\\n  "exit_code": 0,\\n  "cwd": "/workspace"\\n}\') </execute-command> </tool_result>'}
{'role': 'assistant', 'content': 'I see the todo.md needs to be updated to reflect the progress made in the previous session. Let me update it and then continue creating the remaining services.\n\n\n<function_calls><str-replace file_path="todo.md"><old_str>## Phase 2: Core Backend Modules\n- [ ] Company Management\n- [ ] Dynamic Schema Builder\n- [ ] Organizational Structure Engine\n- [ ] Accounting System (double-entry)\n- [ ] Treasury + Banking\n- [ ] Revenue + Billing\n- [ ] HR + Payroll\n- [ ] Tax Engine\n- [ ] Legal + Contract Lifecycle Management\n- [ ] Governance + Policy Engine\n- [ ] Workflow + Automation Engine\n- [ ] Consolidation Engine\n- [ ] Integration Hub\n- [ ] Document + Data Room System\n- [ ] Analytics + AI Engine\n- [ ] Permissions + Security\n- [ ] Event System\n- [ ] Versioning + Time Machine\n- [ ] Plugin System\n- [ ] Simulation Engine\n- [ ] Internal Marketplace\n- [ ] Dashboard System\n- [ ] Audit System\n- [ ] Feature Flag System\n- [ ] Localization\n- [ ] SDK Generator\n- [ ] CLI / Admin Terminal\n- [ ] Reports Module\n- [ ] Fixed Asset Registry\n- [ ] Cash Flow Forecasting\n- [ ] Tax Nexus Engine\n- [ ] Intercompany Reconciliation</old_str><new_str>## Phase 2: Services Layer (Business Logic)\n- [x] Services __init__.py\n- [x] Base service (common patterns, decorators)\n- [x] Auth service (JWT, MFA, API keys)\n- [x] Company service (CRUD, hierarchy, settings)\n- [x] User service (CRUD, preferences, bulk ops)\n- [ ] Account service (double-entry accounting)\n- [ ] Treasury service (accounts, transactions, transfers)\n- [ ] Invoice service (billing, payments)\n- [ ] HR service (employees, payroll, attendance)\n- [ ] Tax service (calculations, filings, nexus)\n- [ ] Workflow service (definitions, executions, triggers)\n- [ ] Legal service (contracts, matters, compliance)\n- [ ] Reports service (templates, executions, dashboards)\n- [ ] Consolidation service (multi-company, eliminations)\n- [ ] Asset service (fixed assets, depreciation)\n- [ ] AI service (models, predictions, anomalies)</new_str></str-replace></function_calls>'}
{'role': 'user', 'content': "<tool_result> <str-replace> ToolResult(success=True, output='Replacement successful.') </str-replace> </tool_result>"}
{'role': 'assistant', 'content': 'Now let me continue creating the remaining services. I\'ll start with the Account service for double-entry accounting operations.\n\n<function_calls>\n<create-file file_path="backend/app/services/account.py">"""\nAccount Service - Double-Entry Accounting Business Logic\nSAMMS ENTERPRISE SOVEREIGN OPERATING SYSTEM (SESOS v6 OMEGA CONSTRAINED)\n\nThis service implements comprehensive double-entry bookkeeping operations\nincluding chart of accounts management, journal entries, postings, and\ntrial balance generation.\n"""\n\nfrom typing import Optional, List, Dict, Any, Tuple, TYPE_CHECKING\nfrom uuid import UUID\nfrom decimal import Decimal\nfrom datetime import date, datetime, timedelta\nfrom collections import defaultdict\nimport asyncio\nimport json\n\nfrom sqlalchemy import select, and_, or_, func, case, literal_column\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom sqlalchemy.orm import selectinload, joinedload\n\nfrom .base import (\n    BaseService,\n    CompanyScopedService,\n    ServiceResult,\n    ServiceContext,\n    transactional,\n    cached,\n    emit_event,\n    requires_permission,\n    CacheManager,\n    EventPublisher,\n)\nfrom ..models.account import (\n    Account,\n    AccountType,\n    AccountSubtype,\n    JournalEntry,\n    JournalEntryLine,\n    JournalEntryStatus,\n    FiscalYear,\n    FiscalPeriod,\n    FiscalPeriodStatus,\n    Budget,\n    BudgetLine,\n    AccountReconciliation,\n    RecurringJournalEntry,\n)\nfrom ..schemas.account import (\n    AccountCreate,\n    AccountUpdate,\n    AccountResponse,\n    JournalEntryCreate,\n    JournalEntryUpdate,\n    JournalEntryResponse,\n    JournalEntryLineCreate,\n    FiscalYearCreate,\n    FiscalPeriodCreate,\n    BudgetCreate,\n    BudgetUpdate,\n    AccountFilter,\n    JournalEntryFilter,\n    TrialBalanceReport,\n    BalanceSheetReport,\n    IncomeStatementReport,\n)\nfrom ..repositories.account import (\n    AccountRepository,\n    JournalEntryRepository,\n    FiscalYearRepository,\n    BudgetRepository,\n)\nfrom ..core.exceptions import (\n    ValidationError,\n    NotFoundError,\n    BusinessLogicError,\n    PermissionDeniedError,\n    ConcurrentModificationError,\n)\n\nif TYPE_CHECKING:\n    from ..repositories.account import AccountRepository, JournalEntryRepository\n\n\nclass AccountService(CompanyScopedService):\n    """\n    Service for managing chart of accounts and double-entry accounting.\n    \n    Implements:\n    - Chart of Accounts management (CRUD)\n    - Journal Entry processing with validation\n    - Automatic posting and balancing\n    - Fiscal Year/Period management\n    - Budget management\n    - Trial Balance generation\n    - Balance Sheet and Income Statement generation\n    - Account reconciliation\n    """\n    \n    def __init__(\n        self,\n        session: AsyncSession,\n        cache: Optional[CacheManager] = None,\n        event_publisher: Optional[EventPublisher] = None,\n    ):\n        super().__init__(session, cache, event_publisher)\n        self.account_repo = AccountRepository(session)\n        self.journal_repo = JournalEntryRepository(session)\n        self.fiscal_repo = FiscalYearRepository(session)\n        self.budget_repo = BudgetRepository(session)\n    \n    # ==================== Chart of Accounts Management ====================\n    \n    @transactional\n    @emit_event("account.created")\n    @requires_permission("account:create")\n    async def create_account(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: AccountCreate,\n    ) -> ServiceResult[AccountResponse]:\n        """\n        Create a new account in the chart of accounts.\n        \n        Validates:\n        - Account code uniqueness within company\n        - Parent account existence (if specified)\n        - Account type consistency with parent\n        - Active fiscal period existence\n        """\n        try:\n            # Validate company access\n            await self.ensure_company_scope(context, company_id)\n            \n            # Check for duplicate account code\n            existing = await self.account_repo.get_by_code(\n                company_id=company_id,\n                code=data.code,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error=f"Account with code \'{data.code}\' already exists",\n                    code="DUPLICATE_ACCOUNT_CODE",\n                )\n            \n            # Validate parent account if specified\n            parent_account = None\n            if data.parent_id:\n                parent_account = await self.account_repo.get_by_id(data.parent_id)\n                if not parent_account or parent_account.company_id != company_id:\n                    return ServiceResult.failure(\n                        error="Parent account not found or not accessible",\n                        code="PARENT_ACCOUNT_NOT_FOUND",\n                    )\n                \n                # Ensure account type consistency\n                if parent_account.account_type != data.account_type:\n                    return ServiceResult.failure(\n                        error="Account type must match parent account type",\n                        code="ACCOUNT_TYPE_MISMATCH",\n                    )\n            \n            # Validate account type constraints\n            validation_result = self._validate_account_type(data)\n            if not validation_result.is_success:\n                return validation_result\n            \n            # Calculate account level\n            level = 1\n            if parent_account:\n                level = parent_account.level + 1\n                if level > 10:  # Maximum nesting level\n                    return ServiceResult.failure(\n                        error="Maximum account nesting level exceeded",\n                        code="MAX_NESTING_EXCEEDED",\n                    )\n            \n            # Create the account\n            account = await self.account_repo.create({\n                "company_id": company_id,\n                "code": data.code,\n                "name": data.name,\n                "description": data.description,\n                "account_type": data.account_type,\n                "account_subtype": data.account_subtype,\n                "parent_id": data.parent_id,\n                "level": level,\n                "is_active": data.is_active,\n                "is_header": data.is_header,\n                "is_reconcilable": data.is_reconcilable,\n                "normal_balance": data.normal_balance or self._get_normal_balance(data.account_type),\n                "currency_code": data.currency_code,\n                "tax_code": data.tax_code,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            # Build full code path\n            await self._update_account_path(account)\n            \n            self._log_info(f"Created account {account.code} - {account.name}")\n            \n            return ServiceResult.success(\n                data=AccountResponse.model_validate(account),\n                message=f"Account \'{account.code}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating account: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create account: {str(e)}",\n                code="CREATE_ACCOUNT_ERROR",\n            )\n    \n    @transactional\n    @emit_event("account.updated")\n    @requires_permission("account:update")\n    async def update_account(\n        self,\n        context: ServiceContext,\n        account_id: UUID,\n        data: AccountUpdate,\n    ) -> ServiceResult[AccountResponse]:\n        """Update an existing account with validation."""\n        try:\n            account = await self.account_repo.get_by_id(account_id)\n            if not account:\n                return ServiceResult.failure(\n                    error="Account not found",\n                    code="ACCOUNT_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, account.company_id)\n            \n            # Check for journal entries using this account\n            if data.code and data.code != account.code:\n                has_entries = await self.journal_repo.has_entries_for_account(account_id)\n                if has_entries:\n                    return ServiceResult.failure(\n                        error="Cannot change account code after transactions exist",\n                        code="ACCOUNT_HAS_TRANSACTIONS",\n                    )\n            \n            # Update allowed fields\n            update_data = data.model_dump(exclude_unset=True)\n            update_data["updated_by"] = context.user_id\n            \n            for field, value in update_data.items():\n                setattr(account, field, value)\n            \n            await self._session.flush()\n            \n            self._log_info(f"Updated account {account.code}")\n            \n            return ServiceResult.success(\n                data=AccountResponse.model_validate(account),\n                message=f"Account \'{account.code}\' updated successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error updating account: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to update account: {str(e)}",\n                code="UPDATE_ACCOUNT_ERROR",\n            )\n    \n    @cached(ttl=300, key_prefix="account_tree")\n    async def get_account_tree(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        include_balances: bool = False,\n        as_of_date: Optional[date] = None,\n    ) -> ServiceResult[List[Dict[str, Any]]]:\n        """\n        Get the complete chart of accounts as a hierarchical tree.\n        \n        Optionally includes calculated balances as of a specific date.\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            accounts = await self.account_repo.get_by_company(\n                company_id=company_id,\n                include_inactive=False,\n            )\n            \n            if include_balances and as_of_date:\n                balances = await self.account_repo.get_balances_as_of_date(\n                    company_id=company_id,\n                    as_of_date=as_of_date,\n                )\n                \n                # Attach balances to accounts\n                for account in accounts:\n                    balance_info = balances.get(str(account.id), {})\n                    account.current_balance = balance_info.get("balance", Decimal("0"))\n                    account.debit_total = balance_info.get("debits", Decimal("0"))\n                    account.credit_total = balance_info.get("credits", Decimal("0"))\n            \n            # Build tree structure\n            tree = self._build_account_tree(accounts)\n            \n            return ServiceResult.success(data=tree)\n            \n        except Exception as e:\n            self._log_error(f"Error getting account tree: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get account tree: {str(e)}",\n                code="GET_ACCOUNT_TREE_ERROR",\n            )\n    \n    async def get_account_by_code(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        code: str,\n    ) -> ServiceResult[AccountResponse]:\n        """Get an account by its code."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            account = await self.account_repo.get_by_code(\n                company_id=company_id,\n                code=code,\n            )\n            \n            if not account:\n                return ServiceResult.failure(\n                    error=f"Account with code \'{code}\' not found",\n                    code="ACCOUNT_NOT_FOUND",\n                )\n            \n            return ServiceResult.success(data=AccountResponse.model_validate(account))\n            \n        except Exception as e:\n            self._log_error(f"Error getting account by code: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get account: {str(e)}",\n                code="GET_ACCOUNT_ERROR",\n            )\n    \n    # ==================== Journal Entry Management ====================\n    \n    @transactional\n    @emit_event("journal_entry.created")\n    @requires_permission("journal_entry:create")\n    async def create_journal_entry(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: JournalEntryCreate,\n    ) -> ServiceResult[JournalEntryResponse]:\n        """\n        Create a new journal entry with full double-entry validation.\n        \n        Validates:\n        - Entry is balanced (debits = credits)\n        - All accounts exist and are active\n        - Entry date is within an open fiscal period\n        - Required fields and references\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Validate fiscal period\n            fiscal_period = await self._validate_fiscal_period(\n                company_id=company_id,\n                entry_date=data.entry_date,\n            )\n            if not fiscal_period:\n                return ServiceResult.failure(\n                    error="No open fiscal period for the entry date",\n                    code="FISCAL_PERIOD_CLOSED",\n                )\n            \n            # Validate and process lines\n            if not data.lines or len(data.lines) < 2:\n                return ServiceResult.failure(\n                    error="Journal entry must have at least 2 lines",\n                    code="INSUFFICIENT_LINES",\n                )\n            \n            # Validate accounts and calculate totals\n            debit_total = Decimal("0")\n            credit_total = Decimal("0")\n            validated_lines = []\n            \n            for idx, line in enumerate(data.lines):\n                # Validate account exists and is active\n                account = await self.account_repo.get_by_id(line.account_id)\n                if not account or account.company_id != company_id:\n                    return ServiceResult.failure(\n                        error=f"Invalid account on line {idx + 1}",\n                        code="INVALID_ACCOUNT",\n                    )\n                \n                if not account.is_active:\n                    return ServiceResult.failure(\n                        error=f"Account {account.code} is not active",\n                        code="ACCOUNT_INACTIVE",\n                    )\n                \n                if account.is_header:\n                    return ServiceResult.failure(\n                        error=f"Cannot post to header account {account.code}",\n                        code="HEADER_ACCOUNT_NOT_ALLOWED",\n                    )\n                \n                # Validate amounts\n                if line.debit_amount and line.credit_amount:\n                    return ServiceResult.failure(\n                        error=f"Line {idx + 1} cannot have both debit and credit",\n                        code="BOTH_DEBIT_CREDIT",\n                    )\n                \n                if not line.debit_amount and not line.credit_amount:\n                    return ServiceResult.failure(\n                        error=f"Line {idx + 1} must have either debit or credit amount",\n                        code="NO_AMOUNT",\n                    )\n                \n                debit_total += line.debit_amount or Decimal("0")\n                credit_total += line.credit_amount or Decimal("0")\n                \n                validated_lines.append({\n                    "account_id": line.account_id,\n                    "description": line.description,\n                    "debit_amount": line.debit_amount or Decimal("0"),\n                    "credit_amount": line.credit_amount or Decimal("0"),\n                    "currency_code": line.currency_code or account.currency_code,\n                    "reference": line.reference,\n                    "custom_fields": line.custom_fields,\n                })\n            \n            # Validate balance\n            if debit_total != credit_total:\n                return ServiceResult.failure(\n                    error=f"Journal entry not balanced. Debits: {debit_total}, Credits: {credit_total}",\n                    code="UNBALANCED_ENTRY",\n                )\n            \n            # Generate entry number\n            entry_number = await self._generate_entry_number(\n                company_id=company_id,\n                fiscal_period_id=fiscal_period.id,\n            )\n            \n            # Create journal entry\n            journal_entry = await self.journal_repo.create({\n                "company_id": company_id,\n                "entry_number": entry_number,\n                "entry_date": data.entry_date,\n                "description": data.description,\n                "reference": data.reference,\n                "source_type": data.source_type,\n                "source_id": data.source_id,\n                "fiscal_period_id": fiscal_period.id,\n                "status": data.status or JournalEntryStatus.DRAFT,\n                "currency_code": data.currency_code,\n                "exchange_rate": data.exchange_rate or Decimal("1"),\n                "reversal_of_id": data.reversal_of_id,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            # Create journal entry lines\n            for line_data in validated_lines:\n                await self.journal_repo.create_line({\n                    "journal_entry_id": journal_entry.id,\n                    **line_data,\n                })\n            \n            await self._session.flush()\n            \n            # Auto-post if status is POSTED\n            if data.status == JournalEntryStatus.POSTED:\n                await self._post_journal_entry(journal_entry)\n            \n            self._log_info(f"Created journal entry {journal_entry.entry_number}")\n            \n            return ServiceResult.success(\n                data=await self._build_entry_response(journal_entry),\n                message=f"Journal entry \'{journal_entry.entry_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating journal entry: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create journal entry: {str(e)}",\n                code="CREATE_JOURNAL_ENTRY_ERROR",\n            )\n    \n    @transactional\n    @emit_event("journal_entry.posted")\n    @requires_permission("journal_entry:post")\n    async def post_journal_entry(\n        self,\n        context: ServiceContext,\n        entry_id: UUID,\n    ) -> ServiceResult[JournalEntryResponse]:\n        """\n        Post a draft journal entry.\n        \n        Updates account balances and sets status to POSTED.\n        """\n        try:\n            entry = await self.journal_repo.get_by_id(entry_id)\n            if not entry:\n                return ServiceResult.failure(\n                    error="Journal entry not found",\n                    code="JOURNAL_ENTRY_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, entry.company_id)\n            \n            if entry.status != JournalEntryStatus.DRAFT:\n                return ServiceResult.failure(\n                    error=f"Cannot post entry with status \'{entry.status}\'",\n                    code="INVALID_STATUS",\n                )\n            \n            # Re-validate fiscal period\n            fiscal_period = await self._validate_fiscal_period(\n                company_id=entry.company_id,\n                entry_date=entry.entry_date,\n            )\n            if not fiscal_period:\n                return ServiceResult.failure(\n                    error="Fiscal period is now closed",\n                    code="FISCAL_PERIOD_CLOSED",\n                )\n            \n            # Post the entry\n            await self._post_journal_entry(entry)\n            \n            entry.status = JournalEntryStatus.POSTED\n            entry.posted_at = datetime.utcnow()\n            entry.posted_by = context.user_id\n            \n            await self._session.flush()\n            \n            self._log_info(f"Posted journal entry {entry.entry_number}")\n            \n            return ServiceResult.success(\n                data=await self._build_entry_response(entry),\n                message=f"Journal entry \'{entry.entry_number}\' posted successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error posting journal entry: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to post journal entry: {str(e)}",\n                code="POST_JOURNAL_ENTRY_ERROR",\n            )\n    \n    @transactional\n    @emit_event("journal_entry.reversed")\n    @requires_permission("journal_entry:reverse")\n    async def reverse_journal_entry(\n        self,\n        context: ServiceContext,\n        entry_id: UUID,\n        reverse_date: Optional[date] = None,\n        reason: Optional[str] = None,\n    ) -> ServiceResult[JournalEntryResponse]:\n        """\n        Create a reversing entry for a posted journal entry.\n        \n        Creates a new entry that reverses all debits and credits.\n        """\n        try:\n            original = await self.journal_repo.get_by_id_with_lines(entry_id)\n            if not original:\n                return ServiceResult.failure(\n                    error="Original journal entry not found",\n                    code="JOURNAL_ENTRY_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, original.company_id)\n            \n            if original.status != JournalEntryStatus.POSTED:\n                return ServiceResult.failure(\n                    error="Can only reverse posted entries",\n                    code="INVALID_STATUS",\n                )\n            \n            if original.reversal_of_id:\n                return ServiceResult.failure(\n                    error="Cannot reverse a reversing entry",\n                    code="REVERSAL_NOT_ALLOWED",\n                )\n            \n            # Check if already reversed\n            existing_reversal = await self.journal_repo.get_reversal_entry(entry_id)\n            if existing_reversal:\n                return ServiceResult.failure(\n                    error="Entry has already been reversed",\n                    code="ALREADY_REVERSED",\n                )\n            \n            # Validate reversal date\n            reverse_date = reverse_date or date.today()\n            fiscal_period = await self._validate_fiscal_period(\n                company_id=original.company_id,\n                entry_date=reverse_date,\n            )\n            if not fiscal_period:\n                return ServiceResult.failure(\n                    error="No open fiscal period for reversal date",\n                    code="FISCAL_PERIOD_CLOSED",\n                )\n            \n            # Generate reversal entry number\n            entry_number = await self._generate_entry_number(\n                company_id=original.company_id,\n                fiscal_period_id=fiscal_period.id,\n            )\n            \n            # Create reversal entry\n            reversal = await self.journal_repo.create({\n                "company_id": original.company_id,\n                "entry_number": entry_number,\n                "entry_date": reverse_date,\n                "description": f"Reversal: {original.description}",\n                "reference": original.reference,\n                "source_type": original.source_type,\n                "fiscal_period_id": fiscal_period.id,\n                "status": JournalEntryStatus.POSTED,\n                "currency_code": original.currency_code,\n                "exchange_rate": original.exchange_rate,\n                "reversal_of_id": original.id,\n                "custom_fields": {"reversal_reason": reason},\n                "created_by": context.user_id,\n                "posted_at": datetime.utcnow(),\n                "posted_by": context.user_id,\n            })\n            \n            # Create reversing lines (swap debits and credits)\n            for original_line in original.lines:\n                await self.journal_repo.create_line({\n                    "journal_entry_id": reversal.id,\n                    "account_id": original_line.account_id,\n                    "description": f"Reversal: {original_line.description}",\n                    "debit_amount": original_line.credit_amount,  # Swap\n                    "credit_amount": original_line.debit_amount,  # Swap\n                    "currency_code": original_line.currency_code,\n                    "reference": original_line.reference,\n                })\n            \n            await self._session.flush()\n            \n            # Post the reversal (update account balances)\n            await self._post_journal_entry(reversal)\n            \n            self._log_info(f"Created reversal entry {reversal.entry_number} for {original.entry_number}")\n            \n            return ServiceResult.success(\n                data=await self._build_entry_response(reversal),\n                message=f"Reversal entry \'{reversal.entry_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error reversing journal entry: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to reverse journal entry: {str(e)}",\n                code="REVERSE_JOURNAL_ENTRY_ERROR",\n            )\n    \n    async def get_journal_entries(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        filters: Optional[JournalEntryFilter] = None,\n        page: int = 1,\n        page_size: int = 50,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Get paginated journal entries with optional filtering."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            result = await self.journal_repo.get_paginated(\n                company_id=company_id,\n                filters=filters,\n                page=page,\n                page_size=page_size,\n            )\n            \n            entries = []\n            for entry in result["items"]:\n                entries.append(await self._build_entry_response(entry))\n            \n            return ServiceResult.success(data={\n                "items": entries,\n                "total": result["total"],\n                "page": page,\n                "page_size": page_size,\n                "total_pages": (result["total"] + page_size - 1) // page_size,\n            })\n            \n        except Exception as e:\n            self._log_error(f"Error getting journal entries: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get journal entries: {str(e)}",\n                code="GET_JOURNAL_ENTRIES_ERROR",\n            )\n    \n    # ==================== Financial Reports ====================\n    \n    @cached(ttl=60, key_prefix="trial_balance")\n    @requires_permission("report:trial_balance")\n    async def generate_trial_balance(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        as_of_date: date,\n        include_zero_balances: bool = False,\n        comparison_date: Optional[date] = None,\n    ) -> ServiceResult[TrialBalanceReport]:\n        """\n        Generate a Trial Balance report as of a specific date.\n        \n        Shows all accounts with their debit/credit balances.\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Get all active accounts\n            accounts = await self.account_repo.get_by_company(\n                company_id=company_id,\n                include_inactive=False,\n            )\n            \n            # Get balances as of date\n            balances = await self.account_repo.get_balances_as_of_date(\n                company_id=company_id,\n                as_of_date=as_of_date,\n            )\n            \n            # Get comparison balances if requested\n            comparison_balances = None\n            if comparison_date:\n                comparison_balances = await self.account_repo.get_balances_as_of_date(\n                    company_id=company_id,\n                    as_of_date=comparison_date,\n                )\n            \n            # Build trial balance lines\n            lines = []\n            total_debits = Decimal("0")\n            total_credits = Decimal("0")\n            \n            for account in accounts:\n                balance_info = balances.get(str(account.id), {})\n                debit = balance_info.get("debits", Decimal("0"))\n                credit = balance_info.get("credits", Decimal("0"))\n                balance = balance_info.get("balance", Decimal("0"))\n                \n                # Skip zero balances if not included\n                if not include_zero_balances and balance == Decimal("0"):\n                    continue\n                \n                # Determine balance presentation\n                if account.normal_balance == "debit":\n                    debit_balance = balance if balance >= 0 else Decimal("0")\n                    credit_balance = -balance if balance < 0 else Decimal("0")\n                else:\n                    credit_balance = balance if balance >= 0 else Decimal("0")\n                    debit_balance = -balance if balance < 0 else Decimal("0")\n                \n                line = {\n                    "account_id": str(account.id),\n                    "account_code": account.code,\n                    "account_name": account.name,\n                    "account_type": account.account_type.value,\n                    "debit_balance": debit_balance,\n                    "credit_balance": credit_balance,\n                    "activity_debit": debit,\n                    "activity_credit": credit,\n                }\n                \n                if comparison_balances:\n                    comp_info = comparison_balances.get(str(account.id), {})\n                    line["comparison_balance"] = comp_info.get("balance", Decimal("0"))\n                    line["change"] = balance - line["comparison_balance"]\n                \n                lines.append(line)\n                total_debits += debit_balance\n                total_credits += credit_balance\n            \n            # Sort by account code\n            lines.sort(key=lambda x: x["account_code"])\n            \n            report = TrialBalanceReport(\n                company_id=company_id,\n                as_of_date=as_of_date,\n                comparison_date=comparison_date,\n                lines=lines,\n                total_debits=total_debits,\n                total_credits=total_credits,\n                is_balanced=abs(total_debits - total_credits) < Decimal("0.01"),\n                generated_at=datetime.utcnow(),\n                generated_by=context.user_id,\n            )\n            \n            return ServiceResult.success(data=report)\n            \n        except Exception as e:\n            self._log_error(f"Error generating trial balance: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to generate trial balance: {str(e)}",\n                code="TRIAL_BALANCE_ERROR",\n            )\n    \n    @cached(ttl=60, key_prefix="balance_sheet")\n    @requires_permission("report:balance_sheet")\n    async def generate_balance_sheet(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        as_of_date: date,\n        include_prior_period: bool = True,\n        consolidate_subsididiaries: bool = False,\n    ) -> ServiceResult[BalanceSheetReport]:\n        """\n        Generate a Balance Sheet (Statement of Financial Position).\n        \n        Reports assets, liabilities, and equity as of a specific date.\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Get balances\n            balances = await self.account_repo.get_balances_by_type(\n                company_id=company_id,\n                as_of_date=as_of_date,\n            )\n            \n            # Get prior period balances if requested\n            prior_balances = None\n            prior_date = None\n            if include_prior_period:\n                # Get prior year same date\n                prior_date = as_of_date - timedelta(days=365)\n                prior_balances = await self.account_repo.get_balances_by_type(\n                    company_id=company_id,\n                    as_of_date=prior_date,\n                )\n            \n            # Calculate retained earnings\n            current_year_earnings = await self._calculate_current_earnings(\n                company_id=company_id,\n                year=as_of_date.year,\n                as_of_date=as_of_date,\n            )\n            \n            # Build sections\n            assets = self._build_balance_sheet_section(\n                balances, ["asset"], prior_balances\n            )\n            liabilities = self._build_balance_sheet_section(\n                balances, ["liability"], prior_balances\n            )\n            equity = self._build_balance_sheet_section(\n                balances, ["equity"], prior_balances\n            )\n            \n            # Add current year earnings to equity\n            equity["current_year_earnings"] = {\n                "name": "Current Year Earnings",\n                "amount": current_year_earnings,\n                "prior_amount": None,\n            }\n            equity["total"] = sum(\n                v.get("amount", 0) for v in equity.values() \n                if isinstance(v, dict) and "amount" in v\n            )\n            \n            total_assets = assets.get("total", Decimal("0"))\n            total_liabilities = liabilities.get("total", Decimal("0"))\n            total_equity = equity.get("total", Decimal("0"))\n            \n            report = BalanceSheetReport(\n                company_id=company_id,\n                as_of_date=as_of_date,\n                prior_date=prior_date,\n                assets=assets,\n                liabilities=liabilities,\n                equity=equity,\n                total_assets=total_assets,\n                total_liabilities=total_liabilities,\n                total_equity=total_equity,\n                total_liabilities_and_equity=total_liabilities + total_equity,\n                is_balanced=abs(total_assets - (total_liabilities + total_equity)) < Decimal("0.01"),\n                generated_at=datetime.utcnow(),\n            )\n            \n            return ServiceResult.success(data=report)\n            \n        except Exception as e:\n            self._log_error(f"Error generating balance sheet: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to generate balance sheet: {str(e)}",\n                code="BALANCE_SHEET_ERROR",\n            )\n    \n    @cached(ttl=60, key_prefix="income_statement")\n    @requires_permission("report:income_statement")\n    async def generate_income_statement(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        start_date: date,\n        end_date: date,\n        include_prior_period: bool = True,\n        comparison_type: str = "prior_year",\n    ) -> ServiceResult[IncomeStatementReport]:\n        """\n        Generate an Income Statement (Profit & Loss).\n        \n        Reports revenue, expenses, and net income for a period.\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Get activity for the period\n            activity = await self.account_repo.get_activity_by_type(\n                company_id=company_id,\n                start_date=start_date,\n                end_date=end_date,\n            )\n            \n            # Get prior period if requested\n            prior_activity = None\n            prior_start = None\n            prior_end = None\n            \n            if include_prior_period:\n                if comparison_type == "prior_year":\n                    prior_start = start_date.replace(year=start_date.year - 1)\n                    prior_end = end_date.replace(year=end_date.year - 1)\n                else:  # prior_period\n                    period_days = (end_date - start_date).days\n                    prior_end = start_date - timedelta(days=1)\n                    prior_start = prior_end - timedelta(days=period_days)\n                \n                prior_activity = await self.account_repo.get_activity_by_type(\n                    company_id=company_id,\n                    start_date=prior_start,\n                    end_date=prior_end,\n                )\n            \n            # Build sections\n            revenue = self._build_income_statement_section(\n                activity, ["revenue", "other_income"], prior_activity\n            )\n            expenses = self._build_income_statement_section(\n                activity, ["expense", "cost_of_sales", "other_expense"], prior_activity\n            )\n            \n            gross_profit = revenue.get("total", Decimal("0")) - expenses.get("cost_of_sales", Decimal("0"))\n            operating_income = gross_profit - expenses.get("operating_expenses", Decimal("0"))\n            net_income = revenue.get("total", Decimal("0")) - expenses.get("total", Decimal("0"))\n            \n            report = IncomeStatementReport(\n                company_id=company_id,\n                start_date=start_date,\n                end_date=end_date,\n                prior_start_date=prior_start,\n                prior_end_date=prior_end,\n                revenue=revenue,\n                expenses=expenses,\n                gross_profit=gross_profit,\n                operating_income=operating_income,\n                net_income=net_income,\n                generated_at=datetime.utcnow(),\n            )\n            \n            return ServiceResult.success(data=report)\n            \n        except Exception as e:\n            self._log_error(f"Error generating income statement: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to generate income statement: {str(e)}",\n                code="INCOME_STATEMENT_ERROR",\n            )\n    \n    # ==================== Fiscal Year Management ====================\n    \n    @transactional\n    @emit_event("fiscal_year.created")\n    @requires_permission("fiscal_year:create")\n    async def create_fiscal_year(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: FiscalYearCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a new fiscal year with periods."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Check for overlapping fiscal years\n            overlapping = await self.fiscal_repo.get_overlapping_year(\n                company_id=company_id,\n                start_date=data.start_date,\n                end_date=data.end_date,\n            )\n            if overlapping:\n                return ServiceResult.failure(\n                    error="Fiscal year dates overlap with existing year",\n                    code="OVERLAPPING_FISCAL_YEAR",\n                )\n            \n            # Create fiscal year\n            fiscal_year = await self.fiscal_repo.create({\n                "company_id": company_id,\n                "name": data.name,\n                "start_date": data.start_date,\n                "end_date": data.end_date,\n                "status": "open",\n                "created_by": context.user_id,\n            })\n            \n            # Generate periods\n            periods = await self._generate_fiscal_periods(\n                fiscal_year=fiscal_year,\n                period_type=data.period_type or "monthly",\n            )\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data={\n                    "fiscal_year": fiscal_year.to_dict(),\n                    "periods": [p.to_dict() for p in periods],\n                },\n                message=f"Fiscal year \'{fiscal_year.name}\' created with {len(periods)} periods",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating fiscal year: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create fiscal year: {str(e)}",\n                code="CREATE_FISCAL_YEAR_ERROR",\n            )\n    \n    @transactional\n    @emit_event("fiscal_period.closed")\n    @requires_permission("fiscal_period:close")\n    async def close_fiscal_period(\n        self,\n        context: ServiceContext,\n        period_id: UUID,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """\n        Close a fiscal period.\n        \n        Validates:\n        - All entries are posted or removed\n        - Previous periods are closed (if required)\n        - No pending reconciliations\n        """\n        try:\n            period = await self.fiscal_repo.get_period_by_id(period_id)\n            if not period:\n                return ServiceResult.failure(\n                    error="Fiscal period not found",\n                    code="PERIOD_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, period.company_id)\n            \n            if period.status == FiscalPeriodStatus.CLOSED:\n                return ServiceResult.failure(\n                    error="Period is already closed",\n                    code="PERIOD_ALREADY_CLOSED",\n                )\n            \n            # Check for draft entries\n            draft_count = await self.journal_repo.count_by_status(\n                fiscal_period_id=period_id,\n                status=JournalEntryStatus.DRAFT,\n            )\n            if draft_count > 0:\n                return ServiceResult.failure(\n                    error=f"Cannot close period with {draft_count} draft entries",\n                    code="DRAFT_ENTRIES_EXIST",\n                )\n            \n            # Close the period\n            period.status = FiscalPeriodStatus.CLOSED\n            period.closed_at = datetime.utcnow()\n            period.closed_by = context.user_id\n            \n            await self._session.flush()\n            \n            self._log_info(f"Closed fiscal period {period.name}")\n            \n            return ServiceResult.success(\n                data=period.to_dict(),\n                message=f"Fiscal period \'{period.name}\' closed successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error closing fiscal period: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to close fiscal period: {str(e)}",\n                code="CLOSE_FISCAL_PERIOD_ERROR",\n            )\n    \n    # ==================== Budget Management ====================\n    \n    @transactional\n    @emit_event("budget.created")\n    @requires_permission("budget:create")\n    async def create_budget(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: BudgetCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a budget with budget lines."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Validate fiscal year\n            fiscal_year = await self.fiscal_repo.get_by_id(data.fiscal_year_id)\n            if not fiscal_year or fiscal_year.company_id != company_id:\n                return ServiceResult.failure(\n                    error="Invalid fiscal year",\n                    code="INVALID_FISCAL_YEAR",\n                )\n            \n            # Check for duplicate budget\n            existing = await self.budget_repo.get_by_name(\n                company_id=company_id,\n                name=data.name,\n                fiscal_year_id=data.fiscal_year_id,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error="Budget with this name already exists for the fiscal year",\n                    code="DUPLICATE_BUDGET",\n                )\n            \n            # Create budget\n            budget = await self.budget_repo.create({\n                "company_id": company_id,\n                "fiscal_year_id": data.fiscal_year_id,\n                "name": data.name,\n                "description": data.description,\n                "budget_type": data.budget_type,\n                "status": data.status or "draft",\n                "total_amount": data.total_amount,\n                "currency_code": data.currency_code,\n                "created_by": context.user_id,\n            })\n            \n            # Create budget lines\n            for line_data in data.lines:\n                await self.budget_repo.create_line({\n                    "budget_id": budget.id,\n                    "account_id": line_data.account_id,\n                    "period_id": line_data.period_id,\n                    "amount": line_data.amount,\n                    "notes": line_data.notes,\n                })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=budget.to_dict(),\n                message=f"Budget \'{budget.name}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating budget: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create budget: {str(e)}",\n                code="CREATE_BUDGET_ERROR",\n            )\n    \n    async def get_budget_vs_actual(\n        self,\n        context: ServiceContext,\n        budget_id: UUID,\n        period_id: Optional[UUID] = None,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Get budget vs actual comparison."""\n        try:\n            budget = await self.budget_repo.get_by_id_with_lines(budget_id)\n            if not budget:\n                return ServiceResult.failure(\n                    error="Budget not found",\n                    code="BUDGET_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, budget.company_id)\n            \n            # Get budget lines\n            lines = budget.lines\n            if period_id:\n                lines = [l for l in lines if l.period_id == period_id]\n            \n            # Get actual amounts\n            actuals = await self.account_repo.get_actuals_for_budget(\n                budget_id=budget_id,\n                period_id=period_id,\n            )\n            \n            comparison = []\n            for line in lines:\n                actual = actuals.get(str(line.account_id), Decimal("0"))\n                variance = line.amount - actual\n                variance_pct = (variance / line.amount * 100) if line.amount else Decimal("0")\n                \n                comparison.append({\n                    "account_id": str(line.account_id),\n                    "account_code": line.account.code if line.account else None,\n                    "account_name": line.account.name if line.account else None,\n                    "budgeted": line.amount,\n                    "actual": actual,\n                    "variance": variance,\n                    "variance_percentage": variance_pct,\n                })\n            \n            return ServiceResult.success(data={\n                "budget": budget.to_dict(),\n                "comparison": comparison,\n                "total_budgeted": sum(l.amount for l in lines),\n                "total_actual": sum(actuals.values()),\n            })\n            \n        except Exception as e:\n            self._log_error(f"Error getting budget vs actual: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get budget comparison: {str(e)}",\n                code="BUDGET_VS_ACTUAL_ERROR",\n            )\n    \n    # ==================== Helper Methods ====================\n    \n    def _validate_account_type(self, data: AccountCreate) -> ServiceResult:\n        """Validate account type and subtype consistency."""\n        type_subtypes = {\n            AccountType.ASSET: [AccountSubtype.CASH, AccountSubtype.RECEIVABLE, AccountSubtype.INVENTORY, AccountSubtype.FIXED_ASSET, AccountSubtype.OTHER_ASSET],\n            AccountType.LIABILITY: [AccountSubtype.PAYABLE, AccountSubtype.ACCRUED, AccountSubtype.DEFERRED_REVENUE, AccountSubtype.LONG_TERM_DEBT, AccountSubtype.OTHER_LIABILITY],\n            AccountType.EQUITY: [AccountSubtype.CAPITAL, AccountSubtype.RETAINED_EARNINGS, AccountSubtype.CURRENT_EARNINGS, AccountSubtype.OTHER_EQUITY],\n            AccountType.REVENUE: [AccountSubtype.OPERATING_REVENUE, AccountSubtype.OTHER_INCOME, AccountSubtype.GAIN],\n            AccountType.EXPENSE: [AccountSubtype.COST_OF_SALES, AccountSubtype.OPERATING_EXPENSE, AccountSubtype.OTHER_EXPENSE, AccountSubtype.LOSS],\n        }\n        \n        if data.account_subtype:\n            valid_subtypes = type_subtypes.get(data.account_type, [])\n            if data.account_subtype not in valid_subtypes:\n                return ServiceResult.failure(\n                    error=f"Invalid subtype {data.account_subtype} for account type {data.account_type}",\n                    code="INVALID_SUBTYPE",\n                )\n        \n        return ServiceResult.success(data=None)\n    \n    def _get_normal_balance(self, account_type: AccountType) -> str:\n        """Get the normal balance side for an account type."""\n        normal_balances = {\n            AccountType.ASSET: "debit",\n            AccountType.LIABILITY: "credit",\n            AccountType.EQUITY: "credit",\n            AccountType.REVENUE: "credit",\n            AccountType.EXPENSE: "debit",\n        }\n        return normal_balances.get(account_type, "debit")\n    \n    async def _update_account_path(self, account: Account) -> None:\n        """Update the full code path for an account."""\n        path_parts = [account.code]\n        parent_id = account.parent_id\n        \n        while parent_id:\n            parent = await self.account_repo.get_by_id(parent_id)\n            if parent:\n                path_parts.insert(0, parent.code)\n                parent_id = parent.parent_id\n            else:\n                break\n        \n        account.full_code = " - ".join(path_parts)\n        await self._session.flush()\n    \n    def _build_account_tree(\n        self,\n        accounts: List[Account],\n        parent_id: Optional[UUID] = None,\n    ) -> List[Dict[str, Any]]:\n        """Build a hierarchical tree from a flat list of accounts."""\n        tree = []\n        \n        for account in accounts:\n            if account.parent_id == parent_id:\n                node = {\n                    "id": str(account.id),\n                    "code": account.code,\n                    "name": account.name,\n                    "type": account.account_type.value,\n                    "subtype": account.account_subtype.value if account.account_subtype else None,\n                    "is_header": account.is_header,\n                    "is_active": account.is_active,\n                    "level": account.level,\n                    "normal_balance": account.normal_balance,\n                    "balance": getattr(account, "current_balance", None),\n                    "children": self._build_account_tree(accounts, account.id),\n                }\n                tree.append(node)\n        \n        # Sort by code\n        tree.sort(key=lambda x: x["code"])\n        return tree\n    \n    async def _validate_fiscal_period(\n        self,\n        company_id: UUID,\n        entry_date: date,\n    ) -> Optional[FiscalPeriod]:\n        """Validate and get the fiscal period for a date."""\n        return await self.fiscal_repo.get_open_period_for_date(\n            company_id=company_id,\n            date=entry_date,\n        )\n    \n    async def _generate_entry_number(\n        self,\n        company_id: UUID,\n        fiscal_period_id: UUID,\n    ) -> str:\n        """Generate a unique journal entry number."""\n        count = await self.journal_repo.count_by_period(fiscal_period_id)\n        period = await self.fiscal_repo.get_period_by_id(fiscal_period_id)\n        \n        prefix = f"JE-{period.name}" if period else "JE"\n        return f"{prefix}-{str(count + 1).zfill(6)}"\n    \n    async def _post_journal_entry(self, entry: JournalEntry) -> None:\n        """Update account balances for a posted entry."""\n        lines = await self.journal_repo.get_lines_by_entry(entry.id)\n        \n        for line in lines:\n            account = await self.account_repo.get_by_id(line.account_id)\n            \n            # Update running balance\n            if account.normal_balance == "debit":\n                account.current_balance = (account.current_balance or Decimal("0")) + line.debit_amount - line.credit_amount\n            else:\n                account.current_balance = (account.current_balance or Decimal("0")) + line.credit_amount - line.debit_amount\n            \n            # Update period totals\n            await self.account_repo.update_period_totals(\n                account_id=account.id,\n                period_id=entry.fiscal_period_id,\n                debits=line.debit_amount,\n                credits=line.credit_amount,\n            )\n    \n    async def _build_entry_response(self, entry: JournalEntry) -> JournalEntryResponse:\n        """Build a complete journal entry response with lines."""\n        lines = await self.journal_repo.get_lines_by_entry(entry.id)\n        \n        return JournalEntryResponse(\n            id=entry.id,\n            company_id=entry.company_id,\n            entry_number=entry.entry_number,\n            entry_date=entry.entry_date,\n            description=entry.description,\n            reference=entry.reference,\n            source_type=entry.source_type,\n            source_id=entry.source_id,\n            fiscal_period_id=entry.fiscal_period_id,\n            status=entry.status,\n            currency_code=entry.currency_code,\n            exchange_rate=entry.exchange_rate,\n            total_debit=sum(l.debit_amount for l in lines),\n            total_credit=sum(l.credit_amount for l in lines),\n            lines=[\n                {\n                    "id": str(l.id),\n                    "account_id": str(l.account_id),\n                    "account_code": l.account.code if l.account else None,\n                    "account_name": l.account.name if l.account else None,\n                    "description": l.description,\n                    "debit_amount": l.debit_amount,\n                    "credit_amount": l.credit_amount,\n                }\n                for l in lines\n            ],\n            created_at=entry.created_at,\n            created_by=entry.created_by,\n            posted_at=entry.posted_at,\n            posted_by=entry.posted_by,\n        )\n    \n    async def _calculate_current_earnings(\n        self,\n        company_id: UUID,\n        year: int,\n        as_of_date: date,\n    ) -> Decimal:\n        """Calculate current year earnings (net income)."""\n        start_date = date(year, 1, 1)\n        \n        activity = await self.account_repo.get_activity_by_type(\n            company_id=company_id,\n            start_date=start_date,\n            end_date=as_of_date,\n        )\n        \n        revenue = sum(activity.get("revenue", {}).values())\n        expenses = sum(activity.get("expense", {}).values())\n        \n        return Decimal(str(revenue - expenses))\n    \n    def _build_balance_sheet_section(\n        self,\n        balances: Dict[str, Any],\n        account_types: List[str],\n        prior_balances: Optional[Dict[str, Any]] = None,\n    ) -> Dict[str, Any]:\n        """Build a balance sheet section from balances."""\n        section = {}\n        total = Decimal("0")\n        \n        for account_type in account_types:\n            type_balances = balances.get(account_type, {})\n            type_total = sum(type_balances.values())\n            \n            prior_total = None\n            if prior_balances and account_type in prior_balances:\n                prior_total = sum(prior_balances[account_type].values())\n            \n            section[account_type] = {\n                "name": account_type.replace("_", " ").title(),\n                "amount": type_total,\n                "prior_amount": prior_total,\n                "accounts": type_balances,\n            }\n            total += type_total\n        \n        section["total"] = total\n        return section\n    \n    def _build_income_statement_section(\n        self,\n        activity: Dict[str, Any],\n        account_types: List[str],\n        prior_activity: Optional[Dict[str, Any]] = None,\n    ) -> Dict[str, Any]:\n        """Build an income statement section from activity."""\n        section = {}\n        total = Decimal("0")\n        \n        for account_type in account_types:\n            type_activity = activity.get(account_type, {})\n            type_total = sum(type_activity.values())\n            \n            prior_total = None\n            if prior_activity and account_type in prior_activity:\n                prior_total = sum(prior_activity[account_type].values())\n            \n            section[account_type] = {\n                "name": account_type.replace("_", " ").title(),\n                "amount": type_total,\n                "prior_amount": prior_total,\n            }\n            total += type_total\n        \n        section["total"] = total\n        return section\n    \n    async def _generate_fiscal_periods(\n        self,\n        fiscal_year: FiscalYear,\n        period_type: str,\n    ) -> List[FiscalPeriod]:\n        """Generate fiscal periods for a year."""\n        periods = []\n        \n        if period_type == "monthly":\n            # Generate 12 monthly periods\n            current_date = fiscal_year.start_date\n            period_num = 1\n            \n            while current_date <= fiscal_year.end_date:\n                # Calculate end of month\n                if current_date.month == 12:\n                    end_of_month = current_date.replace(day=31)\n                else:\n                    next_month = current_date.replace(month=current_date.month + 1, day=1)\n                    end_of_month = next_month - timedelta(days=1)\n                \n                # Ensure we don\'t go past fiscal year end\n                end_of_period = min(end_of_month, fiscal_year.end_date)\n                \n                period = await self.fiscal_repo.create_period({\n                    "fiscal_year_id": fiscal_year.id,\n                    "company_id": fiscal_year.company_id,\n                    "name": f"Period {period_num}",\n                    "period_number": period_num,\n                    "start_date": current_date,\n                    "end_date": end_of_period,\n                    "status": FiscalPeriodStatus.OPEN,\n                })\n                periods.append(period)\n                \n                # Move to next month\n                current_date = end_of_period + timedelta(days=1)\n                period_num += 1\n        \n        elif period_type == "quarterly":\n            # Generate 4 quarterly periods\n            for q in range(1, 5):\n                quarter_start = fiscal_year.start_date + timedelta(days=(q-1) * 91)\n                quarter_end = min(\n                    quarter_start + timedelta(days=90),\n                    fiscal_year.end_date\n                )\n                \n                period = await self.fiscal_repo.create_period({\n                    "fiscal_year_id": fiscal_year.id,\n                    "company_id": fiscal_year.company_id,\n                    "name": f"Q{q}",\n                    "period_number": q,\n                    "start_date": quarter_start,\n                    "end_date": quarter_end,\n                    "status": FiscalPeriodStatus.OPEN,\n                })\n                periods.append(period)\n        \n        return periods\n</create-file>\n</function_calls>'}
{'role': 'user', 'content': '<tool_result> <create-file> ToolResult(success=True, output="File \'backend/app/services/account.py\' created successfully.") </create-file> </tool_result>'}
{'role': 'assistant', 'content': 'Now let me create the Treasury service for accounts, transactions, and transfers management.\n\n<function_calls>\n<create-file file_path="backend/app/services/treasury.py">"""\nTreasury Service - Banking and Treasury Management Business Logic\nSAMMS ENTERPRISE SOVEREIGN OPERATING SYSTEM (SESOS v6 OMEGA CONSTRAINED)\n\nThis service implements comprehensive treasury operations including\nbank account management, transactions, transfers, and cash flow analysis.\n"""\n\nfrom typing import Optional, List, Dict, Any, Tuple, TYPE_CHECKING\nfrom uuid import UUID\nfrom decimal import Decimal\nfrom datetime import date, datetime, timedelta\nfrom enum import Enum\nimport asyncio\nimport json\n\nfrom sqlalchemy import select, and_, or_, func, case, literal_column\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom sqlalchemy.orm import selectinload, joinedload\n\nfrom .base import (\n    BaseService,\n    CompanyScopedService,\n    ServiceResult,\n    ServiceContext,\n    transactional,\n    cached,\n    emit_event,\n    requires_permission,\n    CacheManager,\n    EventPublisher,\n)\nfrom ..models.treasury import (\n    BankAccount,\n    BankAccountType,\n    BankTransaction,\n    BankTransactionType,\n    BankTransactionStatus,\n    BankReconciliation,\n    CashPool,\n    CashPoolAccount,\n    CashForecast,\n    Investment,\n    InvestmentType,\n    InvestmentStatus,\n    CreditFacility,\n    CreditFacilityDrawdown,\n    LetterOfCredit,\n    Guarantee,\n    HedgeInstrument,\n    HedgeRelationship,\n)\nfrom ..schemas.treasury import (\n    BankAccountCreate,\n    BankAccountUpdate,\n    BankAccountResponse,\n    BankTransactionCreate,\n    BankTransactionUpdate,\n    BankTransactionResponse,\n    BankTransferCreate,\n    BankReconciliationCreate,\n    CashPoolCreate,\n    CashForecastCreate,\n    InvestmentCreate,\n    CreditFacilityCreate,\n    CreditFacilityDrawdownCreate,\n    LetterOfCreditCreate,\n    GuaranteeCreate,\n    HedgeInstrumentCreate,\n    CashFlowForecast,\n    CashPosition,\n)\nfrom ..repositories.treasury import (\n    BankAccountRepository,\n    BankTransactionRepository,\n    BankReconciliationRepository,\n    CashPoolRepository,\n    CashForecastRepository,\n    InvestmentRepository,\n    CreditFacilityRepository,\n    LetterOfCreditRepository,\n    GuaranteeRepository,\n    HedgeInstrumentRepository,\n)\nfrom ..core.exceptions import (\n    ValidationError,\n    NotFoundError,\n    BusinessLogicError,\n    PermissionDeniedError,\n    InsufficientFundsError,\n)\n\nif TYPE_CHECKING:\n    from ..repositories.treasury import BankAccountRepository\n\n\nclass TreasuryService(CompanyScopedService):\n    """\n    Service for managing treasury and banking operations.\n    \n    Implements:\n    - Bank account management (multi-currency)\n    - Transaction processing and categorization\n    - Inter-account transfers\n    - Bank reconciliation\n    - Cash pooling and sweeping\n    - Cash flow forecasting\n    - Investment management\n    - Credit facility management\n    - Trade finance (LCs, Guarantees)\n    - Hedging instruments\n    """\n    \n    def __init__(\n        self,\n        session: AsyncSession,\n        cache: Optional[CacheManager] = None,\n        event_publisher: Optional[EventPublisher] = None,\n    ):\n        super().__init__(session, cache, event_publisher)\n        self.account_repo = BankAccountRepository(session)\n        self.transaction_repo = BankTransactionRepository(session)\n        self.reconciliation_repo = BankReconciliationRepository(session)\n        self.cash_pool_repo = CashPoolRepository(session)\n        self.forecast_repo = CashForecastRepository(session)\n        self.investment_repo = InvestmentRepository(session)\n        self.credit_facility_repo = CreditFacilityRepository(session)\n        self.lc_repo = LetterOfCreditRepository(session)\n        self.guarantee_repo = GuaranteeRepository(session)\n        self.hedge_repo = HedgeInstrumentRepository(session)\n    \n    # ==================== Bank Account Management ====================\n    \n    @transactional\n    @emit_event("bank_account.created")\n    @requires_permission("bank_account:create")\n    async def create_bank_account(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: BankAccountCreate,\n    ) -> ServiceResult[BankAccountResponse]:\n        """\n        Create a new bank account.\n        \n        Validates:\n        - Bank name and account number combination is unique\n        - Currency is valid\n        - GL account mapping exists\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Check for duplicate account number\n            existing = await self.account_repo.get_by_account_number(\n                company_id=company_id,\n                account_number=data.account_number,\n                bank_name=data.bank_name,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error=f"Bank account {data.account_number} at {data.bank_name} already exists",\n                    code="DUPLICATE_BANK_ACCOUNT",\n                )\n            \n            # Validate currency\n            if not await self._validate_currency(data.currency_code):\n                return ServiceResult.failure(\n                    error=f"Invalid currency code: {data.currency_code}",\n                    code="INVALID_CURRENCY",\n                )\n            \n            # Create the bank account\n            account = await self.account_repo.create({\n                "company_id": company_id,\n                "bank_name": data.bank_name,\n                "bank_code": data.bank_code,\n                "branch_name": data.branch_name,\n                "account_number": data.account_number,\n                "account_name": data.account_name,\n                "account_type": data.account_type,\n                "currency_code": data.currency_code,\n                "gl_account_id": data.gl_account_id,\n                "opening_balance": data.opening_balance or Decimal("0"),\n                "current_balance": data.opening_balance or Decimal("0"),\n                "available_balance": data.opening_balance or Decimal("0"),\n                "iban": data.iban,\n                "swift_code": data.swift_code,\n                "routing_number": data.routing_number,\n                "is_active": data.is_active,\n                "is_primary": data.is_primary,\n                "signatories": data.signatories,\n                "authorized_signatories": data.authorized_signatories,\n                "credit_limit": data.credit_limit,\n                "interest_rate": data.interest_rate,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            # If this is primary, unset other primary accounts\n            if data.is_primary:\n                await self.account_repo.unset_other_primary(\n                    company_id=company_id,\n                    exclude_id=account.id,\n                )\n            \n            await self._session.flush()\n            \n            self._log_info(f"Created bank account {account.account_number} at {account.bank_name}")\n            \n            return ServiceResult.success(\n                data=BankAccountResponse.model_validate(account),\n                message=f"Bank account \'{account.account_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating bank account: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create bank account: {str(e)}",\n                code="CREATE_BANK_ACCOUNT_ERROR",\n            )\n    \n    @transactional\n    @emit_event("bank_account.updated")\n    @requires_permission("bank_account:update")\n    async def update_bank_account(\n        self,\n        context: ServiceContext,\n        account_id: UUID,\n        data: BankAccountUpdate,\n    ) -> ServiceResult[BankAccountResponse]:\n        """Update an existing bank account."""\n        try:\n            account = await self.account_repo.get_by_id(account_id)\n            if not account:\n                return ServiceResult.failure(\n                    error="Bank account not found",\n                    code="BANK_ACCOUNT_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, account.company_id)\n            \n            # Update allowed fields\n            update_data = data.model_dump(exclude_unset=True)\n            update_data["updated_by"] = context.user_id\n            \n            for field, value in update_data.items():\n                setattr(account, field, value)\n            \n            # Handle primary flag\n            if data.is_primary:\n                await self.account_repo.unset_other_primary(\n                    company_id=account.company_id,\n                    exclude_id=account.id,\n                )\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=BankAccountResponse.model_validate(account),\n                message=f"Bank account \'{account.account_number}\' updated successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error updating bank account: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to update bank account: {str(e)}",\n                code="UPDATE_BANK_ACCOUNT_ERROR",\n            )\n    \n    @cached(ttl=60, key_prefix="bank_accounts")\n    async def get_bank_accounts(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        currency_code: Optional[str] = None,\n        account_type: Optional[BankAccountType] = None,\n        include_inactive: bool = False,\n    ) -> ServiceResult[List[BankAccountResponse]]:\n        """Get all bank accounts for a company with optional filtering."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            accounts = await self.account_repo.get_by_company(\n                company_id=company_id,\n                currency_code=currency_code,\n                account_type=account_type,\n                include_inactive=include_inactive,\n            )\n            \n            return ServiceResult.success(\n                data=[BankAccountResponse.model_validate(a) for a in accounts]\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error getting bank accounts: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get bank accounts: {str(e)}",\n                code="GET_BANK_ACCOUNTS_ERROR",\n            )\n    \n    async def get_cash_position(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        as_of_date: Optional[date] = None,\n        currency: Optional[str] = None,\n    ) -> ServiceResult[CashPosition]:\n        """\n        Get the current cash position for a company.\n        \n        Aggregates balances across all bank accounts.\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            as_of_date = as_of_date or date.today()\n            \n            # Get all active bank accounts\n            accounts = await self.account_repo.get_by_company(\n                company_id=company_id,\n                include_inactive=False,\n            )\n            \n            # Get balances as of date\n            position_by_currency: Dict[str, Dict[str, Decimal]] = {}\n            \n            for account in accounts:\n                if currency and account.currency_code != currency:\n                    continue\n                \n                # Get balance as of date (considering transactions)\n                balance = await self.account_repo.get_balance_as_of_date(\n                    account_id=account.id,\n                    as_of_date=as_of_date,\n                )\n                \n                if account.currency_code not in position_by_currency:\n                    position_by_currency[account.currency_code] = {\n                        "current_balance": Decimal("0"),\n                        "available_balance": Decimal("0"),\n                        "pending_credits": Decimal("0"),\n                        "pending_debits": Decimal("0"),\n                    }\n                \n                position_by_currency[account.currency_code]["current_balance"] += balance\n                position_by_currency[account.currency_code]["available_balance"] += account.available_balance or Decimal("0")\n            \n            # Get pending transactions\n            pending = await self.transaction_repo.get_pending_transactions(\n                company_id=company_id,\n                as_of_date=as_of_date,\n            )\n            \n            for txn in pending:\n                curr = txn.currency_code\n                if curr not in position_by_currency:\n                    position_by_currency[curr] = {\n                        "current_balance": Decimal("0"),\n                        "available_balance": Decimal("0"),\n                        "pending_credits": Decimal("0"),\n                        "pending_debits": Decimal("0"),\n                    }\n                \n                if txn.transaction_type in [BankTransactionType.DEPOSIT, BankTransactionType.RECEIPT]:\n                    position_by_currency[curr]["pending_credits"] += txn.amount\n                else:\n                    position_by_currency[curr]["pending_debits"] += txn.amount\n            \n            # Build position summary\n            position = CashPosition(\n                company_id=company_id,\n                as_of_date=as_of_date,\n                positions_by_currency=position_by_currency,\n                total_accounts=len(accounts),\n                accounts_summary=[\n                    {\n                        "account_id": str(a.id),\n                        "bank_name": a.bank_name,\n                        "account_number": a.account_number[-4:],  # Last 4 digits only\n                        "currency": a.currency_code,\n                        "balance": a.current_balance,\n                    }\n                    for a in accounts\n                ],\n            )\n            \n            return ServiceResult.success(data=position)\n            \n        except Exception as e:\n            self._log_error(f"Error getting cash position: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get cash position: {str(e)}",\n                code="CASH_POSITION_ERROR",\n            )\n    \n    # ==================== Transaction Management ====================\n    \n    @transactional\n    @emit_event("bank_transaction.created")\n    @requires_permission("bank_transaction:create")\n    async def create_transaction(\n        self,\n        context: ServiceContext,\n        data: BankTransactionCreate,\n    ) -> ServiceResult[BankTransactionResponse]:\n        """\n        Create a new bank transaction.\n        \n        Validates:\n        - Bank account exists and is active\n        - Sufficient funds for withdrawals\n        - Valid transaction type\n        """\n        try:\n            # Validate bank account\n            account = await self.account_repo.get_by_id(data.bank_account_id)\n            if not account:\n                return ServiceResult.failure(\n                    error="Bank account not found",\n                    code="BANK_ACCOUNT_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, account.company_id)\n            \n            if not account.is_active:\n                return ServiceResult.failure(\n                    error="Bank account is not active",\n                    code="ACCOUNT_INACTIVE",\n                )\n            \n            # Validate funds for withdrawals\n            if data.transaction_type in [BankTransactionType.WITHDRAWAL, BankTransactionType.PAYMENT, BankTransactionType.TRANSFER_OUT]:\n                if data.amount > (account.available_balance or Decimal("0")):\n                    if not account.credit_limit or (data.amount > (account.available_balance + account.credit_limit)):\n                        return ServiceResult.failure(\n                            error="Insufficient funds",\n                            code="INSUFFICIENT_FUNDS",\n                        )\n            \n            # Generate transaction reference\n            reference = data.reference or await self._generate_transaction_reference(\n                company_id=account.company_id,\n                transaction_type=data.transaction_type,\n            )\n            \n            # Create transaction\n            transaction = await self.transaction_repo.create({\n                "company_id": account.company_id,\n                "bank_account_id": data.bank_account_id,\n                "transaction_type": data.transaction_type,\n                "amount": data.amount,\n                "currency_code": data.currency_code or account.currency_code,\n                "value_date": data.value_date or date.today(),\n                "reference": reference,\n                "description": data.description,\n                "counterparty_name": data.counterparty_name,\n                "counterparty_account": data.counterparty_account,\n                "counterparty_bank": data.counterparty_bank,\n                "status": data.status or BankTransactionStatus.PENDING,\n                "category": data.category,\n                "sub_category": data.sub_category,\n                "gl_account_id": data.gl_account_id,\n                "cost_center_id": data.cost_center_id,\n                "project_id": data.project_id,\n                "reconciliation_id": data.reconciliation_id,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            # Update account balance if posted\n            if data.status == BankTransactionStatus.POSTED:\n                await self._update_account_balance(account, transaction)\n            \n            await self._session.flush()\n            \n            self._log_info(f"Created bank transaction {transaction.reference}")\n            \n            return ServiceResult.success(\n                data=BankTransactionResponse.model_validate(transaction),\n                message=f"Transaction \'{transaction.reference}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating transaction: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create transaction: {str(e)}",\n                code="CREATE_TRANSACTION_ERROR",\n            )\n    \n    @transactional\n    @emit_event("bank_transaction.posted")\n    @requires_permission("bank_transaction:post")\n    async def post_transaction(\n        self,\n        context: ServiceContext,\n        transaction_id: UUID,\n    ) -> ServiceResult[BankTransactionResponse]:\n        """Post a pending transaction."""\n        try:\n            transaction = await self.transaction_repo.get_by_id(transaction_id)\n            if not transaction:\n                return ServiceResult.failure(\n                    error="Transaction not found",\n                    code="TRANSACTION_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, transaction.company_id)\n            \n            if transaction.status != BankTransactionStatus.PENDING:\n                return ServiceResult.failure(\n                    error=f"Cannot post transaction with status \'{transaction.status}\'",\n                    code="INVALID_STATUS",\n                )\n            \n            # Get account\n            account = await self.account_repo.get_by_id(transaction.bank_account_id)\n            \n            # Update balance\n            await self._update_account_balance(account, transaction)\n            \n            # Update status\n            transaction.status = BankTransactionStatus.POSTED\n            transaction.posted_at = datetime.utcnow()\n            transaction.posted_by = context.user_id\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=BankTransactionResponse.model_validate(transaction),\n                message=f"Transaction \'{transaction.reference}\' posted successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error posting transaction: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to post transaction: {str(e)}",\n                code="POST_TRANSACTION_ERROR",\n            )\n    \n    async def get_transactions(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        bank_account_id: Optional[UUID] = None,\n        start_date: Optional[date] = None,\n        end_date: Optional[date] = None,\n        transaction_type: Optional[BankTransactionType] = None,\n        status: Optional[BankTransactionStatus] = None,\n        min_amount: Optional[Decimal] = None,\n        max_amount: Optional[Decimal] = None,\n        page: int = 1,\n        page_size: int = 50,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Get paginated transactions with filtering."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            result = await self.transaction_repo.get_paginated(\n                company_id=company_id,\n                bank_account_id=bank_account_id,\n                start_date=start_date,\n                end_date=end_date,\n                transaction_type=transaction_type,\n                status=status,\n                min_amount=min_amount,\n                max_amount=max_amount,\n                page=page,\n                page_size=page_size,\n            )\n            \n            return ServiceResult.success(data={\n                "items": [BankTransactionResponse.model_validate(t) for t in result["items"]],\n                "total": result["total"],\n                "page": page,\n                "page_size": page_size,\n                "total_pages": (result["total"] + page_size - 1) // page_size,\n            })\n            \n        except Exception as e:\n            self._log_error(f"Error getting transactions: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get transactions: {str(e)}",\n                code="GET_TRANSACTIONS_ERROR",\n            )\n    \n    # ==================== Transfers ====================\n    \n    @transactional\n    @emit_event("bank_transfer.initiated")\n    @requires_permission("bank_transfer:create")\n    async def initiate_transfer(\n        self,\n        context: ServiceContext,\n        data: BankTransferCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """\n        Initiate a bank transfer between accounts.\n        \n        Supports:\n        - Same bank transfers\n        - Inter-bank transfers\n        - Cross-currency transfers with FX\n        - Scheduled transfers\n        """\n        try:\n            # Validate source account\n            source_account = await self.account_repo.get_by_id(data.source_account_id)\n            if not source_account:\n                return ServiceResult.failure(\n                    error="Source account not found",\n                    code="SOURCE_ACCOUNT_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, source_account.company_id)\n            \n            # Validate destination account\n            dest_account = await self.account_repo.get_by_id(data.destination_account_id)\n            if not dest_account:\n                return ServiceResult.failure(\n                    error="Destination account not found",\n                    code="DESTINATION_ACCOUNT_NOT_FOUND",\n                )\n            \n            # Check sufficient funds\n            if data.amount > (source_account.available_balance or Decimal("0")):\n                return ServiceResult.failure(\n                    error="Insufficient funds in source account",\n                    code="INSUFFICIENT_FUNDS",\n                )\n            \n            # Calculate FX if needed\n            source_amount = data.amount\n            dest_amount = data.amount\n            exchange_rate = Decimal("1")\n            \n            if source_account.currency_code != dest_account.currency_code:\n                if data.exchange_rate:\n                    exchange_rate = data.exchange_rate\n                else:\n                    # Get current rate\n                    exchange_rate = await self._get_exchange_rate(\n                        from_currency=source_account.currency_code,\n                        to_currency=dest_account.currency_code,\n                    )\n                \n                dest_amount = source_amount * exchange_rate\n            \n            # Generate transfer reference\n            transfer_reference = data.reference or await self._generate_transaction_reference(\n                company_id=source_account.company_id,\n                transaction_type=BankTransactionType.TRANSFER_OUT,\n            )\n            \n            # Create source transaction (outflow)\n            source_transaction = await self.transaction_repo.create({\n                "company_id": source_account.company_id,\n                "bank_account_id": data.source_account_id,\n                "transaction_type": BankTransactionType.TRANSFER_OUT,\n                "amount": source_amount,\n                "currency_code": source_account.currency_code,\n                "value_date": data.value_date or date.today(),\n                "reference": f"{transfer_reference}-OUT",\n                "description": f"Transfer to {dest_account.bank_name} - {dest_account.account_number[-4:]}",\n                "counterparty_name": dest_account.bank_name,\n                "counterparty_account": dest_account.account_number,\n                "counterparty_bank": dest_account.bank_name,\n                "status": BankTransactionStatus.PENDING if data.schedule_date else BankTransactionStatus.POSTED,\n                "exchange_rate": exchange_rate,\n                "custom_fields": {\n                    "transfer_id": transfer_reference,\n                    "destination_account_id": str(dest_account.id),\n                    "destination_amount": str(dest_amount),\n                },\n                "created_by": context.user_id,\n            })\n            \n            # Create destination transaction (inflow)\n            dest_transaction = await self.transaction_repo.create({\n                "company_id": dest_account.company_id,\n                "bank_account_id": data.destination_account_id,\n                "transaction_type": BankTransactionType.TRANSFER_IN,\n                "amount": dest_amount,\n                "currency_code": dest_account.currency_code,\n                "value_date": data.value_date or date.today(),\n                "reference": f"{transfer_reference}-IN",\n                "description": f"Transfer from {source_account.bank_name} - {source_account.account_number[-4:]}",\n                "counterparty_name": source_account.bank_name,\n                "counterparty_account": source_account.account_number,\n                "counterparty_bank": source_account.bank_name,\n                "status": BankTransactionStatus.PENDING if data.schedule_date else BankTransactionStatus.POSTED,\n                "exchange_rate": exchange_rate,\n                "custom_fields": {\n                    "transfer_id": transfer_reference,\n                    "source_account_id": str(source_account.id),\n                    "source_amount": str(source_amount),\n                },\n                "created_by": context.user_id,\n            })\n            \n            # Update balances if posted immediately\n            if not data.schedule_date:\n                await self._update_account_balance(source_account, source_transaction)\n                await self._update_account_balance(dest_account, dest_transaction)\n            \n            await self._session.flush()\n            \n            self._log_info(f"Initiated transfer {transfer_reference}")\n            \n            return ServiceResult.success(\n                data={\n                    "transfer_reference": transfer_reference,\n                    "source_transaction": BankTransactionResponse.model_validate(source_transaction),\n                    "destination_transaction": BankTransactionResponse.model_validate(dest_transaction),\n                    "source_amount": source_amount,\n                    "destination_amount": dest_amount,\n                    "exchange_rate": exchange_rate,\n                    "status": "scheduled" if data.schedule_date else "completed",\n                },\n                message=f"Transfer \'{transfer_reference}\' initiated successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error initiating transfer: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to initiate transfer: {str(e)}",\n                code="INITIATE_TRANSFER_ERROR",\n            )\n    \n    # ==================== Bank Reconciliation ====================\n    \n    @transactional\n    @emit_event("bank_reconciliation.created")\n    @requires_permission("bank_reconciliation:create")\n    async def create_reconciliation(\n        self,\n        context: ServiceContext,\n        data: BankReconciliationCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """\n        Create a bank reconciliation.\n        \n        Matches bank transactions with GL entries.\n        """\n        try:\n            # Validate bank account\n            account = await self.account_repo.get_by_id(data.bank_account_id)\n            if not account:\n                return ServiceResult.failure(\n                    error="Bank account not found",\n                    code="BANK_ACCOUNT_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, account.company_id)\n            \n            # Check for existing reconciliation for this period\n            existing = await self.reconciliation_repo.get_by_period(\n                bank_account_id=data.bank_account_id,\n                statement_date=data.statement_date,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error="Reconciliation already exists for this period",\n                    code="RECONCILIATION_EXISTS",\n                )\n            \n            # Calculate differences\n            book_balance = await self.account_repo.get_balance_as_of_date(\n                account_id=account.id,\n                as_of_date=data.statement_date,\n            )\n            \n            difference = data.statement_balance - book_balance\n            \n            # Create reconciliation\n            reconciliation = await self.reconciliation_repo.create({\n                "company_id": account.company_id,\n                "bank_account_id": data.bank_account_id,\n                "statement_date": data.statement_date,\n                "statement_balance": data.statement_balance,\n                "book_balance": book_balance,\n                "difference": difference,\n                "status": "in_progress" if difference != Decimal("0") else "balanced",\n                "notes": data.notes,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            # Auto-match transactions if requested\n            if data.auto_match:\n                matched = await self._auto_match_transactions(\n                    reconciliation_id=reconciliation.id,\n                    bank_account_id=data.bank_account_id,\n                    statement_date=data.statement_date,\n                )\n            else:\n                matched = []\n            \n            return ServiceResult.success(\n                data={\n                    "reconciliation": reconciliation.to_dict(),\n                    "matched_transactions": matched,\n                    "difference": difference,\n                    "is_balanced": difference == Decimal("0"),\n                },\n                message="Reconciliation created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating reconciliation: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create reconciliation: {str(e)}",\n                code="CREATE_RECONCILIATION_ERROR",\n            )\n    \n    @transactional\n    async def reconcile_transaction(\n        self,\n        context: ServiceContext,\n        reconciliation_id: UUID,\n        transaction_id: UUID,\n        gl_entry_id: Optional[UUID] = None,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Mark a transaction as reconciled."""\n        try:\n            reconciliation = await self.reconciliation_repo.get_by_id(reconciliation_id)\n            if not reconciliation:\n                return ServiceResult.failure(\n                    error="Reconciliation not found",\n                    code="RECONCILIATION_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, reconciliation.company_id)\n            \n            transaction = await self.transaction_repo.get_by_id(transaction_id)\n            if not transaction:\n                return ServiceResult.failure(\n                    error="Transaction not found",\n                    code="TRANSACTION_NOT_FOUND",\n                )\n            \n            # Mark as reconciled\n            transaction.reconciliation_id = reconciliation_id\n            transaction.reconciled_at = datetime.utcnow()\n            transaction.reconciled_by = context.user_id\n            transaction.status = BankTransactionStatus.RECONCILED\n            \n            await self._session.flush()\n            \n            # Update reconciliation progress\n            await self._update_reconciliation_progress(reconciliation)\n            \n            return ServiceResult.success(\n                data={\n                    "transaction_id": str(transaction_id),\n                    "reconciled": True,\n                    "reconciliation_status": reconciliation.status,\n                },\n                message="Transaction reconciled successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error reconciling transaction: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to reconcile transaction: {str(e)}",\n                code="RECONCILE_TRANSACTION_ERROR",\n            )\n    \n    # ==================== Cash Flow Forecasting ====================\n    \n    @cached(ttl=300, key_prefix="cash_forecast")\n    async def generate_cash_forecast(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        start_date: date,\n        end_date: date,\n        include_submitted: bool = True,\n        include_approved: bool = True,\n        scenario: Optional[str] = None,\n    ) -> ServiceResult[CashFlowForecast]:\n        """\n        Generate a cash flow forecast.\n        \n        Aggregates:\n        - Current cash position\n        - Receivables (due and expected)\n        - Payables (due and expected)\n        - Scheduled transfers\n        - Payroll obligations\n        - Tax obligations\n        - Capital expenditures\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Get current cash position\n            position = await self.get_cash_position(context, company_id, start_date)\n            if not position.is_success:\n                return position\n            \n            opening_balance = position.data.positions_by_currency\n            \n            # Build forecast by day\n            forecast_days = []\n            current_date = start_date\n            \n            while current_date <= end_date:\n                day_forecast = await self._build_daily_forecast(\n                    company_id=company_id,\n                    date=current_date,\n                    include_submitted=include_submitted,\n                    include_approved=include_approved,\n                )\n                forecast_days.append(day_forecast)\n                current_date += timedelta(days=1)\n            \n            # Calculate running balances\n            running_balance = opening_balance.copy()\n            for day in forecast_days:\n                for currency, net_flow in day.get("net_flows", {}).items():\n                    if currency not in running_balance:\n                        running_balance[currency] = {"current_balance": Decimal("0")}\n                    running_balance[currency]["current_balance"] += net_flow\n                day["closing_balance"] = running_balance.copy()\n            \n            # Build summary\n            total_inflows = sum(\n                sum(day.get("inflows", {}).values())\n                for day in forecast_days\n            )\n            total_outflows = sum(\n                sum(day.get("outflows", {}).values())\n                for day in forecast_days\n            )\n            \n            forecast = CashFlowForecast(\n                company_id=company_id,\n                start_date=start_date,\n                end_date=end_date,\n                scenario=scenario,\n                opening_balance=opening_balance,\n                daily_forecasts=forecast_days,\n                total_inflows=Decimal(str(total_inflows)),\n                total_outflows=Decimal(str(total_outflows)),\n                net_cash_flow=Decimal(str(total_inflows - total_outflows)),\n                closing_balance=running_balance,\n                generated_at=datetime.utcnow(),\n            )\n            \n            return ServiceResult.success(data=forecast)\n            \n        except Exception as e:\n            self._log_error(f"Error generating cash forecast: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to generate cash forecast: {str(e)}",\n                code="CASH_FORECAST_ERROR",\n            )\n    \n    # ==================== Investment Management ====================\n    \n    @transactional\n    @emit_event("investment.created")\n    @requires_permission("investment:create")\n    async def create_investment(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: InvestmentCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a new investment record."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Validate funding account\n            if data.funding_account_id:\n                account = await self.account_repo.get_by_id(data.funding_account_id)\n                if not account or account.company_id != company_id:\n                    return ServiceResult.failure(\n                        error="Invalid funding account",\n                        code="INVALID_FUNDING_ACCOUNT",\n                    )\n            \n            # Create investment\n            investment = await self.investment_repo.create({\n                "company_id": company_id,\n                "investment_type": data.investment_type,\n                "name": data.name,\n                "description": data.description,\n                "instrument_code": data.instrument_code,\n                "counterparty": data.counterparty,\n                "principal_amount": data.principal_amount,\n                "currency_code": data.currency_code,\n                "purchase_date": data.purchase_date,\n                "maturity_date": data.maturity_date,\n                "interest_rate": data.interest_rate,\n                "interest_type": data.interest_type,\n                "interest_frequency": data.interest_frequency,\n                "status": InvestmentStatus.ACTIVE,\n                "funding_account_id": data.funding_account_id,\n                "risk_rating": data.risk_rating,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            # Create funding transaction if funding account specified\n            if data.funding_account_id and data.auto_fund:\n                txn = await self.transaction_repo.create({\n                    "company_id": company_id,\n                    "bank_account_id": data.funding_account_id,\n                    "transaction_type": BankTransactionType.INVESTMENT,\n                    "amount": data.principal_amount,\n                    "currency_code": data.currency_code,\n                    "value_date": data.purchase_date,\n                    "reference": f"INV-{investment.id}",\n                    "description": f"Investment: {investment.name}",\n                    "status": BankTransactionStatus.POSTED,\n                    "created_by": context.user_id,\n                })\n                \n                account = await self.account_repo.get_by_id(data.funding_account_id)\n                await self._update_account_balance(account, txn)\n            \n            return ServiceResult.success(\n                data=investment.to_dict(),\n                message=f"Investment \'{investment.name}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating investment: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create investment: {str(e)}",\n                code="CREATE_INVESTMENT_ERROR",\n            )\n    \n    async def get_investment_portfolio(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        include_matured: bool = False,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Get the investment portfolio summary."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            investments = await self.investment_repo.get_by_company(\n                company_id=company_id,\n                include_matured=include_matured,\n            )\n            \n            # Calculate portfolio metrics\n            total_principal = sum(i.principal_amount for i in investments)\n            total_current_value = sum(\n                await self._calculate_investment_value(i)\n                for i in investments\n            )\n            \n            # Group by type\n            by_type: Dict[str, List] = {}\n            for inv in investments:\n                inv_type = inv.investment_type.value\n                if inv_type not in by_type:\n                    by_type[inv_type] = []\n                by_type[inv_type].append(inv.to_dict())\n            \n            # Calculate maturity profile\n            maturity_profile = await self._calculate_maturity_profile(investments)\n            \n            return ServiceResult.success(data={\n                "investments": [i.to_dict() for i in investments],\n                "total_principal": total_principal,\n                "total_current_value": total_current_value,\n                "unrealized_gain_loss": total_current_value - total_principal,\n                "by_type": by_type,\n                "maturity_profile": maturity_profile,\n                "count": len(investments),\n            })\n            \n        except Exception as e:\n            self._log_error(f"Error getting investment portfolio: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get investment portfolio: {str(e)}",\n                code="GET_INVESTMENT_PORTFOLIO_ERROR",\n            )\n    \n    # ==================== Credit Facilities ====================\n    \n    @transactional\n    @emit_event("credit_facility.created")\n    @requires_permission("credit_facility:create")\n    async def create_credit_facility(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: CreditFacilityCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a new credit facility."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            facility = await self.credit_facility_repo.create({\n                "company_id": company_id,\n                "facility_type": data.facility_type,\n                "lender_name": data.lender_name,\n                "facility_name": data.facility_name,\n                "facility_number": data.facility_number,\n                "total_amount": data.total_amount,\n                "currency_code": data.currency_code,\n                "available_amount": data.total_amount,\n                "interest_rate": data.interest_rate,\n                "interest_type": data.interest_type,\n                "start_date": data.start_date,\n                "maturity_date": data.maturity_date,\n                "bank_account_id": data.bank_account_id,\n                "covenants": data.covenants,\n                "guarantees": data.guarantees,\n                "collateral": data.collateral,\n                "status": "active",\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=facility.to_dict(),\n                message=f"Credit facility \'{facility.facility_name}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating credit facility: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create credit facility: {str(e)}",\n                code="CREATE_CREDIT_FACILITY_ERROR",\n            )\n    \n    @transactional\n    @emit_event("credit_facility.drawdown")\n    @requires_permission("credit_facility:drawdown")\n    async def create_drawdown(\n        self,\n        context: ServiceContext,\n        facility_id: UUID,\n        data: CreditFacilityDrawdownCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a drawdown on a credit facility."""\n        try:\n            facility = await self.credit_facility_repo.get_by_id(facility_id)\n            if not facility:\n                return ServiceResult.failure(\n                    error="Credit facility not found",\n                    code="FACILITY_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, facility.company_id)\n            \n            # Check availability\n            if data.amount > facility.available_amount:\n                return ServiceResult.failure(\n                    error=f"Requested amount exceeds available facility. Available: {facility.available_amount}",\n                    code="INSUFFICIENT_FACILITY",\n                )\n            \n            # Create drawdown\n            drawdown = await self.credit_facility_repo.create_drawdown({\n                "credit_facility_id": facility_id,\n                "drawdown_number": await self._generate_drawdown_number(facility),\n                "amount": data.amount,\n                "currency_code": data.currency_code or facility.currency_code,\n                "drawdown_date": data.drawdown_date or date.today(),\n                "purpose": data.purpose,\n                "repayment_date": data.repayment_date,\n                "interest_rate": data.interest_rate or facility.interest_rate,\n                "bank_account_id": data.bank_account_id or facility.bank_account_id,\n                "status": "active",\n                "created_by": context.user_id,\n            })\n            \n            # Update facility available amount\n            facility.available_amount -= data.amount\n            facility.outstanding_amount = (facility.outstanding_amount or Decimal("0")) + data.amount\n            \n            await self._session.flush()\n            \n            # Create bank transaction for funding\n            if data.bank_account_id or facility.bank_account_id:\n                account_id = data.bank_account_id or facility.bank_account_id\n                account = await self.account_repo.get_by_id(account_id)\n                \n                txn = await self.transaction_repo.create({\n                    "company_id": facility.company_id,\n                    "bank_account_id": account_id,\n                    "transaction_type": BankTransactionType.LOAN_DRAWDOWN,\n                    "amount": data.amount,\n                    "currency_code": data.currency_code or facility.currency_code,\n                    "value_date": data.drawdown_date or date.today(),\n                    "reference": f"DD-{drawdown.drawdown_number}",\n                    "description": f"Drawdown from {facility.facility_name}",\n                    "status": BankTransactionStatus.POSTED,\n                    "created_by": context.user_id,\n                })\n                \n                await self._update_account_balance(account, txn)\n            \n            return ServiceResult.success(\n                data=drawdown.to_dict(),\n                message=f"Drawdown of {data.amount} completed successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating drawdown: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create drawdown: {str(e)}",\n                code="CREATE_DRAWDOWN_ERROR",\n            )\n    \n    # ==================== Trade Finance ====================\n    \n    @transactional\n    @emit_event("letter_of_credit.created")\n    @requires_permission("letter_of_credit:create")\n    async def create_letter_of_credit(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: LetterOfCreditCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a letter of credit."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            lc = await self.lc_repo.create({\n                "company_id": company_id,\n                "lc_number": data.lc_number or await self._generate_lc_number(company_id),\n                "lc_type": data.lc_type,\n                "applicant_name": data.applicant_name,\n                "beneficiary_name": data.beneficiary_name,\n                "beneficiary_bank": data.beneficiary_bank,\n                "amount": data.amount,\n                "currency_code": data.currency_code,\n                "issue_date": data.issue_date,\n                "expiry_date": data.expiry_date,\n                "port_of_loading": data.port_of_loading,\n                "port_of_discharge": data.port_of_discharge,\n                "description_of_goods": data.description_of_goods,\n                "bank_account_id": data.bank_account_id,\n                "status": "issued",\n                "terms": data.terms,\n                "documents_required": data.documents_required,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=lc.to_dict(),\n                message=f"Letter of Credit \'{lc.lc_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating letter of credit: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create letter of credit: {str(e)}",\n                code="CREATE_LC_ERROR",\n            )\n    \n    @transactional\n    @emit_event("guarantee.created")\n    @requires_permission("guarantee:create")\n    async def create_guarantee(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: GuaranteeCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a bank guarantee."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            guarantee = await self.guarantee_repo.create({\n                "company_id": company_id,\n                "guarantee_number": data.guarantee_number or await self._generate_guarantee_number(company_id),\n                "guarantee_type": data.guarantee_type,\n                "issuer_bank": data.issuer_bank,\n                "beneficiary_name": data.beneficiary_name,\n                "amount": data.amount,\n                "currency_code": data.currency_code,\n                "issue_date": data.issue_date,\n                "expiry_date": data.expiry_date,\n                "purpose": data.purpose,\n                "bank_account_id": data.bank_account_id,\n                "status": "active",\n                "terms": data.terms,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=guarantee.to_dict(),\n                message=f"Guarantee \'{guarantee.guarantee_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating guarantee: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create guarantee: {str(e)}",\n                code="CREATE_GUARANTEE_ERROR",\n            )\n    \n    # ==================== Helper Methods ====================\n    \n    async def _validate_currency(self, currency_code: str) -> bool:\n        """Validate a currency code."""\n        valid_currencies = [\n            "USD", "EUR", "GBP", "JPY", "CHF", "CAD", "AUD", "NZD",\n            "CNY", "INR", "SGD", "HKD", "KRW", "MXN", "BRL", "ZAR",\n            "SAR", "AED", "QAR", "KWD", "BHD", "OMR", "EGP", "KES",\n            "NGN", "GHS", "TZS", "UGX", "ETB", "MAD", "TND", "DZD",\n        ]\n        return currency_code.upper() in valid_currencies\n    \n    async def _update_account_balance(\n        self,\n        account: BankAccount,\n        transaction: BankTransaction,\n    ) -> None:\n        """Update account balance based on transaction."""\n        amount = transaction.amount\n        \n        # Determine if this increases or decreases balance\n        if transaction.transaction_type in [\n            BankTransactionType.DEPOSIT,\n            BankTransactionType.RECEIPT,\n            BankTransactionType.INTEREST,\n            BankTransactionType.TRANSFER_IN,\n            BankTransactionType.LOAN_DRAWDOWN,\n        ]:\n            account.current_balance = (account.current_balance or Decimal("0")) + amount\n            account.available_balance = (account.available_balance or Decimal("0")) + amount\n        else:\n            account.current_balance = (account.current_balance or Decimal("0")) - amount\n            account.available_balance = (account.available_balance or Decimal("0")) - amount\n        \n        account.last_transaction_date = transaction.value_date\n        await self._session.flush()\n    \n    async def _generate_transaction_reference(\n        self,\n        company_id: UUID,\n        transaction_type: BankTransactionType,\n    ) -> str:\n        """Generate a unique transaction reference."""\n        prefix_map = {\n            BankTransactionType.DEPOSIT: "DEP",\n            BankTransactionType.WITHDRAWAL: "WD",\n            BankTransactionType.TRANSFER_OUT: "TRF",\n            BankTransactionType.TRANSFER_IN: "TRF",\n            BankTransactionType.PAYMENT: "PAY",\n            BankTransactionType.RECEIPT: "RCPT",\n            BankTransactionType.INTEREST: "INT",\n            BankTransactionType.FEE: "FEE",\n        }\n        \n        prefix = prefix_map.get(transaction_type, "TXN")\n        count = await self.transaction_repo.count_by_type(\n            company_id=company_id,\n            transaction_type=transaction_type,\n        )\n        \n        return f"{prefix}-{str(count + 1).zfill(8)}"\n    \n    async def _get_exchange_rate(\n        self,\n        from_currency: str,\n        to_currency: str,\n    ) -> Decimal:\n        """Get current exchange rate between two currencies."""\n        # This would typically integrate with an FX rate provider\n        # For now, return a placeholder\n        if from_currency == to_currency:\n            return Decimal("1")\n        \n        # Default rates (would be replaced with real API call)\n        rates = {\n            ("USD", "EUR"): Decimal("0.92"),\n            ("EUR", "USD"): Decimal("1.09"),\n            ("USD", "GBP"): Decimal("0.79"),\n            ("GBP", "USD"): Decimal("1.27"),\n            ("USD", "SAR"): Decimal("3.75"),\n            ("SAR", "USD"): Decimal("0.27"),\n        }\n        \n        return rates.get((from_currency, to_currency), Decimal("1"))\n    \n    async def _auto_match_transactions(\n        self,\n        reconciliation_id: UUID,\n        bank_account_id: UUID,\n        statement_date: date,\n    ) -> List[Dict[str, Any]]:\n        """Auto-match transactions for reconciliation."""\n        # Get unmatched transactions\n        transactions = await self.transaction_repo.get_unreconciled(\n            bank_account_id=bank_account_id,\n            as_of_date=statement_date,\n        )\n        \n        matched = []\n        for txn in transactions:\n            # Auto-match by reference or amount\n            # This is a simplified matching logic\n            if txn.status == BankTransactionStatus.POSTED:\n                txn.reconciliation_id = reconciliation_id\n                txn.reconciled_at = datetime.utcnow()\n                txn.status = BankTransactionStatus.RECONCILED\n                matched.append({\n                    "transaction_id": str(txn.id),\n                    "reference": txn.reference,\n                    "amount": str(txn.amount),\n                })\n        \n        return matched\n    \n    async def _update_reconciliation_progress(\n        self,\n        reconciliation: BankReconciliation,\n    ) -> None:\n        """Update reconciliation progress and status."""\n        # Get all reconciled transactions\n        reconciled = await self.transaction_repo.get_reconciled_by_reconciliation(\n            reconciliation_id=reconciliation.id,\n        )\n        \n        reconciled_amount = sum(t.amount for t in reconciled)\n        \n        # Check if balanced\n        if abs(reconciliation.difference) < Decimal("0.01"):\n            reconciliation.status = "balanced"\n            reconciliation.completed_at = datetime.utcnow()\n        \n        await self._session.flush()\n    \n    async def _build_daily_forecast(\n        self,\n        company_id: UUID,\n        date: date,\n        include_submitted: bool,\n        include_approved: bool,\n    ) -> Dict[str, Any]:\n        """Build daily cash flow forecast."""\n        # Get expected inflows\n        inflows = await self.forecast_repo.get_expected_inflows(\n            company_id=company_id,\n            date=date,\n            include_submitted=include_submitted,\n            include_approved=include_approved,\n        )\n        \n        # Get expected outflows\n        outflows = await self.forecast_repo.get_expected_outflows(\n            company_id=company_id,\n            date=date,\n            include_submitted=include_submitted,\n            include_approved=include_approved,\n        )\n        \n        # Calculate net by currency\n        net_flows: Dict[str, Decimal] = {}\n        \n        for curr, amount in inflows.items():\n            net_flows[curr] = net_flows.get(curr, Decimal("0")) + amount\n        \n        for curr, amount in outflows.items():\n            net_flows[curr] = net_flows.get(curr, Decimal("0")) - amount\n        \n        return {\n            "date": date.isoformat(),\n            "inflows": inflows,\n            "outflows": outflows,\n            "net_flows": net_flows,\n        }\n    \n    async def _calculate_investment_value(self, investment: Investment) -> Decimal:\n        """Calculate current value of an investment."""\n        if investment.status == InvestmentStatus.MATURED:\n            return investment.principal_amount + (investment.interest_earned or Decimal("0"))\n        \n        # Calculate accrued interest for fixed rate\n        if investment.interest_rate:\n            days_held = (date.today() - investment.purchase_date).days\n            accrued_interest = investment.principal_amount * (\n                investment.interest_rate / Decimal("100")\n            ) * (Decimal(str(days_held)) / Decimal("365"))\n            return investment.principal_amount + accrued_interest\n        \n        return investment.principal_amount\n    \n    async def _calculate_maturity_profile(\n        self,\n        investments: List[Investment],\n    ) -> Dict[str, Decimal]:\n        """Calculate investment maturity profile."""\n        profile = {\n            "0-30_days": Decimal("0"),\n            "31-90_days": Decimal("0"),\n            "91-180_days": Decimal("0"),\n            "181-365_days": Decimal("0"),\n            "over_1_year": Decimal("0"),\n        }\n        \n        today = date.today()\n        \n        for inv in investments:\n            if not inv.maturity_date:\n                profile["over_1_year"] += inv.principal_amount\n                continue\n            \n            days_to_maturity = (inv.maturity_date - today).days\n            \n            if days_to_maturity <= 30:\n                profile["0-30_days"] += inv.principal_amount\n            elif days_to_maturity <= 90:\n                profile["31-90_days"] += inv.principal_amount\n            elif days_to_maturity <= 180:\n                profile["91-180_days"] += inv.principal_amount\n            elif days_to_maturity <= 365:\n                profile["181-365_days"] += inv.principal_amount\n            else:\n                profile["over_1_year"] += inv.principal_amount\n        \n        return profile\n    \n    async def _generate_drawdown_number(self, facility: CreditFacility) -> str:\n        """Generate a unique drawdown number."""\n        count = await self.credit_facility_repo.count_drawdowns(facility.id)\n        return f"DD-{facility.facility_number}-{str(count + 1).zfill(4)}"\n    \n    async def _generate_lc_number(self, company_id: UUID) -> str:\n        """Generate a unique LC number."""\n        count = await self.lc_repo.count_by_company(company_id)\n        year = date.today().year\n        return f"LC-{year}-{str(count + 1).zfill(6)}"\n    \n    async def _generate_guarantee_number(self, company_id: UUID) -> str:\n        """Generate a unique guarantee number."""\n        count = await self.guarantee_repo.count_by_company(company_id)\n        year = date.today().year\n        return f"GR-{year}-{str(count + 1).zfill(6)}"\n</create-file>\n</function_calls>'}
{'role': 'user', 'content': '<tool_result> <create-file> ToolResult(success=True, output="File \'backend/app/services/treasury.py\' created successfully.") </create-file> </tool_result>'}
{'role': 'assistant', 'content': 'Now let me create the Invoice service for billing and payments management.\n\n<function_calls>\n<create-file file_path="backend/app/services/invoice.py">"""\nInvoice Service - Billing and Payments Business Logic\nSAMMS ENTERPRISE SOVEREIGN OPERATING SYSTEM (SESOS v6 OMEGA CONSTRAINED)\n\nThis service implements comprehensive invoice management operations\nincluding billing, payments, credit notes, and accounts receivable/payable.\n"""\n\nfrom typing import Optional, List, Dict, Any, Tuple, TYPE_CHECKING\nfrom uuid import UUID\nfrom decimal import Decimal\nfrom datetime import date, datetime, timedelta\nfrom enum import Enum\nimport asyncio\nimport json\n\nfrom sqlalchemy import select, and_, or_, func, case, literal_column\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom sqlalchemy.orm import selectinload, joinedload\n\nfrom .base import (\n    BaseService,\n    CompanyScopedService,\n    ServiceResult,\n    ServiceContext,\n    transactional,\n    cached,\n    emit_event,\n    requires_permission,\n    CacheManager,\n    EventPublisher,\n)\nfrom ..models.invoice import (\n    Invoice,\n    InvoiceLine,\n    InvoiceStatus,\n    InvoiceType,\n    Payment,\n    PaymentMethod,\n    PaymentStatus,\n    CreditNote,\n    CreditNoteLine,\n    CreditNoteStatus,\n    CreditNoteType,\n    RecurringInvoice,\n    RecurringInvoiceStatus,\n    InvoiceTemplate,\n    InvoiceAllocation,\n    TaxRate,\n    WithholdingTax,\n    InvoiceDiscount,\n    LateFee,\n)\nfrom ..schemas.invoice import (\n    InvoiceCreate,\n    InvoiceUpdate,\n    InvoiceResponse,\n    InvoiceLineCreate,\n    PaymentCreate,\n    PaymentResponse,\n    CreditNoteCreate,\n    CreditNoteResponse,\n    RecurringInvoiceCreate,\n    InvoiceFilter,\n    PaymentFilter,\n    AgingReport,\n    InvoiceSummary,\n)\nfrom ..repositories.invoice import (\n    InvoiceRepository,\n    PaymentRepository,\n    CreditNoteRepository,\n    RecurringInvoiceRepository,\n    InvoiceTemplateRepository,\n)\nfrom ..core.exceptions import (\n    ValidationError,\n    NotFoundError,\n    BusinessLogicError,\n    PermissionDeniedError,\n)\n\nif TYPE_CHECKING:\n    from ..repositories.invoice import InvoiceRepository\n\n\nclass InvoiceService(CompanyScopedService):\n    """\n    Service for managing invoices, payments, and credit notes.\n    \n    Implements:\n    - Invoice CRUD and processing\n    - Multi-currency invoicing\n    - Payment processing and allocation\n    - Credit note management\n    - Recurring invoices\n    - Tax calculations\n    - Discounts and late fees\n    - Aging reports\n    - Dunning management\n    """\n    \n    def __init__(\n        self,\n        session: AsyncSession,\n        cache: Optional[CacheManager] = None,\n        event_publisher: Optional[EventPublisher] = None,\n    ):\n        super().__init__(session, cache, event_publisher)\n        self.invoice_repo = InvoiceRepository(session)\n        self.payment_repo = PaymentRepository(session)\n        self.credit_note_repo = CreditNoteRepository(session)\n        self.recurring_repo = RecurringInvoiceRepository(session)\n        self.template_repo = InvoiceTemplateRepository(session)\n    \n    # ==================== Invoice Management ====================\n    \n    @transactional\n    @emit_event("invoice.created")\n    @requires_permission("invoice:create")\n    async def create_invoice(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: InvoiceCreate,\n    ) -> ServiceResult[InvoiceResponse]:\n        """\n        Create a new invoice.\n        \n        Validates:\n        - Customer/vendor exists\n        - Valid line items with products/services\n        - Tax rates are valid\n        - Currency is valid\n        - Payment terms are valid\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Generate invoice number\n            invoice_number = data.invoice_number or await self._generate_invoice_number(\n                company_id=company_id,\n                invoice_type=data.invoice_type,\n            )\n            \n            # Check for duplicate invoice number\n            existing = await self.invoice_repo.get_by_number(\n                company_id=company_id,\n                invoice_number=invoice_number,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error=f"Invoice number \'{invoice_number}\' already exists",\n                    code="DUPLICATE_INVOICE_NUMBER",\n                )\n            \n            # Validate and calculate line items\n            validated_lines, totals = await self._validate_and_calculate_lines(\n                company_id=company_id,\n                lines=data.lines,\n                currency_code=data.currency_code,\n            )\n            \n            if not validated_lines:\n                return ServiceResult.failure(\n                    error="Invoice must have at least one line item",\n                    code="NO_LINE_ITEMS",\n                )\n            \n            # Calculate due date based on payment terms\n            due_date = data.due_date or await self._calculate_due_date(\n                invoice_date=data.invoice_date or date.today(),\n                payment_terms=data.payment_terms,\n            )\n            \n            # Create invoice\n            invoice = await self.invoice_repo.create({\n                "company_id": company_id,\n                "invoice_number": invoice_number,\n                "invoice_type": data.invoice_type,\n                "status": data.status or InvoiceStatus.DRAFT,\n                "invoice_date": data.invoice_date or date.today(),\n                "due_date": due_date,\n                "customer_id": data.customer_id,\n                "vendor_id": data.vendor_id,\n                "contact_id": data.contact_id,\n                "currency_code": data.currency_code,\n                "exchange_rate": data.exchange_rate or Decimal("1"),\n                "subtotal": totals["subtotal"],\n                "discount_amount": totals["discount"],\n                "tax_amount": totals["tax"],\n                "withholding_tax": totals.get("withholding", Decimal("0")),\n                "total_amount": totals["total"],\n                "amount_due": totals["total"],\n                "payment_terms": data.payment_terms,\n                "payment_terms_id": data.payment_terms_id,\n                "notes": data.notes,\n                "terms_and_conditions": data.terms_and_conditions,\n                "purchase_order": data.purchase_order,\n                "sales_order": data.sales_order,\n                "delivery_note": data.delivery_note,\n                "billing_address": data.billing_address,\n                "shipping_address": data.shipping_address,\n                "project_id": data.project_id,\n                "department_id": data.department_id,\n                "cost_center_id": data.cost_center_id,\n                "gl_account_id": data.gl_account_id,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            # Create invoice lines\n            for line_data in validated_lines:\n                await self.invoice_repo.create_line({\n                    "invoice_id": invoice.id,\n                    **line_data,\n                })\n            \n            # Apply early payment discount if applicable\n            if data.early_payment_discount:\n                await self._apply_early_payment_discount(\n                    invoice=invoice,\n                    discount_data=data.early_payment_discount,\n                )\n            \n            await self._session.flush()\n            \n            self._log_info(f"Created invoice {invoice.invoice_number}")\n            \n            return ServiceResult.success(\n                data=await self._build_invoice_response(invoice),\n                message=f"Invoice \'{invoice.invoice_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating invoice: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create invoice: {str(e)}",\n                code="CREATE_INVOICE_ERROR",\n            )\n    \n    @transactional\n    @emit_event("invoice.updated")\n    @requires_permission("invoice:update")\n    async def update_invoice(\n        self,\n        context: ServiceContext,\n        invoice_id: UUID,\n        data: InvoiceUpdate,\n    ) -> ServiceResult[InvoiceResponse]:\n        """Update an existing invoice."""\n        try:\n            invoice = await self.invoice_repo.get_by_id_with_lines(invoice_id)\n            if not invoice:\n                return ServiceResult.failure(\n                    error="Invoice not found",\n                    code="INVOICE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, invoice.company_id)\n            \n            # Check if invoice can be updated\n            if invoice.status not in [InvoiceStatus.DRAFT, InvoiceStatus.SENT]:\n                return ServiceResult.failure(\n                    error=f"Cannot update invoice with status \'{invoice.status}\'",\n                    code="INVALID_STATUS",\n                )\n            \n            # Update fields\n            update_data = data.model_dump(exclude_unset=True)\n            update_data["updated_by"] = context.user_id\n            \n            # Recalculate totals if lines changed\n            if data.lines:\n                validated_lines, totals = await self._validate_and_calculate_lines(\n                    company_id=invoice.company_id,\n                    lines=data.lines,\n                    currency_code=invoice.currency_code,\n                )\n                \n                update_data["subtotal"] = totals["subtotal"]\n                update_data["discount_amount"] = totals["discount"]\n                update_data["tax_amount"] = totals["tax"]\n                update_data["total_amount"] = totals["total"]\n                update_data["amount_due"] = totals["total"] - (invoice.amount_paid or Decimal("0"))\n                \n                # Delete existing lines and create new ones\n                await self.invoice_repo.delete_lines(invoice_id)\n                for line_data in validated_lines:\n                    await self.invoice_repo.create_line({\n                        "invoice_id": invoice.id,\n                        **line_data,\n                    })\n            \n            for field, value in update_data.items():\n                if field != "lines":\n                    setattr(invoice, field, value)\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=await self._build_invoice_response(invoice),\n                message=f"Invoice \'{invoice.invoice_number}\' updated successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error updating invoice: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to update invoice: {str(e)}",\n                code="UPDATE_INVOICE_ERROR",\n            )\n    \n    @transactional\n    @emit_event("invoice.sent")\n    @requires_permission("invoice:send")\n    async def send_invoice(\n        self,\n        context: ServiceContext,\n        invoice_id: UUID,\n        send_email: bool = True,\n        email_addresses: Optional[List[str]] = None,\n        message: Optional[str] = None,\n    ) -> ServiceResult[InvoiceResponse]:\n        """Send invoice to customer/vendor."""\n        try:\n            invoice = await self.invoice_repo.get_by_id(invoice_id)\n            if not invoice:\n                return ServiceResult.failure(\n                    error="Invoice not found",\n                    code="INVOICE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, invoice.company_id)\n            \n            if invoice.status == InvoiceStatus.DRAFT:\n                invoice.status = InvoiceStatus.SENT\n                invoice.sent_at = datetime.utcnow()\n                invoice.sent_by = context.user_id\n            \n            await self._session.flush()\n            \n            # Send email if requested\n            if send_email:\n                await self._send_invoice_email(\n                    invoice=invoice,\n                    email_addresses=email_addresses,\n                    message=message,\n                )\n            \n            self._log_info(f"Sent invoice {invoice.invoice_number}")\n            \n            return ServiceResult.success(\n                data=await self._build_invoice_response(invoice),\n                message=f"Invoice \'{invoice.invoice_number}\' sent successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error sending invoice: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to send invoice: {str(e)}",\n                code="SEND_INVOICE_ERROR",\n            )\n    \n    @transactional\n    @emit_event("invoice.approved")\n    @requires_permission("invoice:approve")\n    async def approve_invoice(\n        self,\n        context: ServiceContext,\n        invoice_id: UUID,\n        notes: Optional[str] = None,\n    ) -> ServiceResult[InvoiceResponse]:\n        """Approve an invoice (for bills/vendor invoices)."""\n        try:\n            invoice = await self.invoice_repo.get_by_id(invoice_id)\n            if not invoice:\n                return ServiceResult.failure(\n                    error="Invoice not found",\n                    code="INVOICE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, invoice.company_id)\n            \n            if invoice.status not in [InvoiceStatus.DRAFT, InvoiceStatus.SUBMITTED]:\n                return ServiceResult.failure(\n                    error=f"Cannot approve invoice with status \'{invoice.status}\'",\n                    code="INVALID_STATUS",\n                )\n            \n            invoice.status = InvoiceStatus.APPROVED\n            invoice.approved_at = datetime.utcnow()\n            invoice.approved_by = context.user_id\n            invoice.approval_notes = notes\n            \n            await self._session.flush()\n            \n            self._log_info(f"Approved invoice {invoice.invoice_number}")\n            \n            return ServiceResult.success(\n                data=await self._build_invoice_response(invoice),\n                message=f"Invoice \'{invoice.invoice_number}\' approved successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error approving invoice: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to approve invoice: {str(e)}",\n                code="APPROVE_INVOICE_ERROR",\n            )\n    \n    async def get_invoices(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        filters: Optional[InvoiceFilter] = None,\n        page: int = 1,\n        page_size: int = 50,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Get paginated invoices with filtering."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            result = await self.invoice_repo.get_paginated(\n                company_id=company_id,\n                filters=filters,\n                page=page,\n                page_size=page_size,\n            )\n            \n            invoices = []\n            for invoice in result["items"]:\n                invoices.append(await self._build_invoice_response(invoice))\n            \n            return ServiceResult.success(data={\n                "items": invoices,\n                "total": result["total"],\n                "page": page,\n                "page_size": page_size,\n                "total_pages": (result["total"] + page_size - 1) // page_size,\n            })\n            \n        except Exception as e:\n            self._log_error(f"Error getting invoices: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get invoices: {str(e)}",\n                code="GET_INVOICES_ERROR",\n            )\n    \n    async def get_invoice_by_number(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        invoice_number: str,\n    ) -> ServiceResult[InvoiceResponse]:\n        """Get invoice by number."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            invoice = await self.invoice_repo.get_by_number(\n                company_id=company_id,\n                invoice_number=invoice_number,\n            )\n            \n            if not invoice:\n                return ServiceResult.failure(\n                    error=f"Invoice \'{invoice_number}\' not found",\n                    code="INVOICE_NOT_FOUND",\n                )\n            \n            return ServiceResult.success(\n                data=await self._build_invoice_response(invoice)\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error getting invoice: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get invoice: {str(e)}",\n                code="GET_INVOICE_ERROR",\n            )\n    \n    # ==================== Payment Processing ====================\n    \n    @transactional\n    @emit_event("payment.created")\n    @requires_permission("payment:create")\n    async def create_payment(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: PaymentCreate,\n    ) -> ServiceResult[PaymentResponse]:\n        """\n        Create a payment and allocate to invoices.\n        \n        Supports:\n        - Partial payments\n        - Multi-invoice allocation\n        - Overpayments\n        - Advance payments\n        - Multi-currency payments\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Generate payment number\n            payment_number = data.payment_number or await self._generate_payment_number(\n                company_id=company_id,\n                payment_type=data.payment_type,\n            )\n            \n            # Check for duplicate\n            existing = await self.payment_repo.get_by_number(\n                company_id=company_id,\n                payment_number=payment_number,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error=f"Payment number \'{payment_number}\' already exists",\n                    code="DUPLICATE_PAYMENT_NUMBER",\n                )\n            \n            # Validate bank account if specified\n            if data.bank_account_id:\n                # Would validate bank account exists\n                pass\n            \n            # Calculate unallocated amount\n            allocated_amount = Decimal("0")\n            if data.allocations:\n                allocated_amount = sum(a.amount for a in data.allocations)\n            \n            unallocated_amount = data.amount - allocated_amount\n            \n            # Create payment\n            payment = await self.payment_repo.create({\n                "company_id": company_id,\n                "payment_number": payment_number,\n                "payment_type": data.payment_type,\n                "payment_method": data.payment_method,\n                "status": data.status or PaymentStatus.PENDING,\n                "amount": data.amount,\n                "currency_code": data.currency_code,\n                "exchange_rate": data.exchange_rate or Decimal("1"),\n                "payment_date": data.payment_date or date.today(),\n                "customer_id": data.customer_id,\n                "vendor_id": data.vendor_id,\n                "bank_account_id": data.bank_account_id,\n                "reference": data.reference,\n                "description": data.description,\n                "unallocated_amount": unallocated_amount,\n                "check_number": data.check_number,\n                "check_date": data.check_date,\n                "credit_card_id": data.credit_card_id,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            # Create allocations\n            if data.allocations:\n                for allocation in data.allocations:\n                    # Validate invoice exists and belongs to customer/vendor\n                    invoice = await self.invoice_repo.get_by_id(allocation.invoice_id)\n                    if not invoice:\n                        return ServiceResult.failure(\n                            error=f"Invoice {allocation.invoice_id} not found",\n                            code="INVOICE_NOT_FOUND",\n                        )\n                    \n                    # Validate allocation amount\n                    amount_due = invoice.amount_due or Decimal("0")\n                    if allocation.amount > amount_due and not data.allow_overpayment:\n                        return ServiceResult.failure(\n                            error=f"Allocation amount exceeds invoice due amount",\n                            code="OVER_ALLOCATION",\n                        )\n                    \n                    # Create allocation\n                    await self.payment_repo.create_allocation({\n                        "payment_id": payment.id,\n                        "invoice_id": allocation.invoice_id,\n                        "amount": allocation.amount,\n                        "discount_taken": allocation.discount_taken,\n                    })\n                    \n                    # Update invoice amounts\n                    invoice.amount_paid = (invoice.amount_paid or Decimal("0")) + allocation.amount\n                    invoice.amount_due = (invoice.amount_due or Decimal("0")) - allocation.amount\n                    \n                    # Update invoice status\n                    if invoice.amount_due <= Decimal("0"):\n                        invoice.status = InvoiceStatus.PAID\n                        invoice.paid_at = datetime.utcnow()\n                    elif invoice.amount_paid > Decimal("0"):\n                        invoice.status = InvoiceStatus.PARTIALLY_PAID\n            \n            await self._session.flush()\n            \n            # Post payment if status is completed\n            if data.status == PaymentStatus.COMPLETED:\n                await self._post_payment(payment)\n            \n            self._log_info(f"Created payment {payment.payment_number}")\n            \n            return ServiceResult.success(\n                data=PaymentResponse.model_validate(payment),\n                message=f"Payment \'{payment.payment_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating payment: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create payment: {str(e)}",\n                code="CREATE_PAYMENT_ERROR",\n            )\n    \n    @transactional\n    @emit_event("payment.voided")\n    @requires_permission("payment:void")\n    async def void_payment(\n        self,\n        context: ServiceContext,\n        payment_id: UUID,\n        reason: Optional[str] = None,\n    ) -> ServiceResult[PaymentResponse]:\n        """Void a payment and reverse allocations."""\n        try:\n            payment = await self.payment_repo.get_by_id_with_allocations(payment_id)\n            if not payment:\n                return ServiceResult.failure(\n                    error="Payment not found",\n                    code="PAYMENT_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, payment.company_id)\n            \n            if payment.status == PaymentStatus.VOIDED:\n                return ServiceResult.failure(\n                    error="Payment is already voided",\n                    code="ALREADY_VOIDED",\n                )\n            \n            # Reverse allocations\n            for allocation in payment.allocations:\n                invoice = await self.invoice_repo.get_by_id(allocation.invoice_id)\n                if invoice:\n                    invoice.amount_paid = (invoice.amount_paid or Decimal("0")) - allocation.amount\n                    invoice.amount_due = (invoice.amount_due or Decimal("0")) + allocation.amount\n                    \n                    # Reset status\n                    if invoice.amount_paid <= Decimal("0"):\n                        invoice.status = InvoiceStatus.SENT\n                    elif invoice.amount_due > Decimal("0"):\n                        invoice.status = InvoiceStatus.PARTIALLY_PAID\n            \n            # Update payment status\n            payment.status = PaymentStatus.VOIDED\n            payment.voided_at = datetime.utcnow()\n            payment.voided_by = context.user_id\n            payment.void_reason = reason\n            \n            await self._session.flush()\n            \n            self._log_info(f"Voided payment {payment.payment_number}")\n            \n            return ServiceResult.success(\n                data=PaymentResponse.model_validate(payment),\n                message=f"Payment \'{payment.payment_number}\' voided successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error voiding payment: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to void payment: {str(e)}",\n                code="VOID_PAYMENT_ERROR",\n            )\n    \n    # ==================== Credit Note Management ====================\n    \n    @transactional\n    @emit_event("credit_note.created")\n    @requires_permission("credit_note:create")\n    async def create_credit_note(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: CreditNoteCreate,\n    ) -> ServiceResult[CreditNoteResponse]:\n        """Create a credit note."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Generate credit note number\n            cn_number = data.credit_note_number or await self._generate_credit_note_number(\n                company_id=company_id,\n            )\n            \n            # Validate reference invoice if specified\n            if data.invoice_id:\n                invoice = await self.invoice_repo.get_by_id(data.invoice_id)\n                if not invoice or invoice.company_id != company_id:\n                    return ServiceResult.failure(\n                        error="Reference invoice not found",\n                        code="INVOICE_NOT_FOUND",\n                    )\n            \n            # Calculate totals\n            validated_lines, totals = await self._validate_credit_note_lines(\n                company_id=company_id,\n                lines=data.lines,\n                currency_code=data.currency_code,\n            )\n            \n            # Create credit note\n            credit_note = await self.credit_note_repo.create({\n                "company_id": company_id,\n                "credit_note_number": cn_number,\n                "credit_note_type": data.credit_note_type,\n                "status": data.status or CreditNoteStatus.DRAFT,\n                "credit_note_date": data.credit_note_date or date.today(),\n                "customer_id": data.customer_id,\n                "vendor_id": data.vendor_id,\n                "invoice_id": data.invoice_id,\n                "currency_code": data.currency_code,\n                "subtotal": totals["subtotal"],\n                "tax_amount": totals["tax"],\n                "total_amount": totals["total"],\n                "amount_used": Decimal("0"),\n                "amount_available": totals["total"],\n                "reason": data.reason,\n                "notes": data.notes,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            # Create lines\n            for line_data in validated_lines:\n                await self.credit_note_repo.create_line({\n                    "credit_note_id": credit_note.id,\n                    **line_data,\n                })\n            \n            await self._session.flush()\n            \n            self._log_info(f"Created credit note {credit_note.credit_note_number}")\n            \n            return ServiceResult.success(\n                data=CreditNoteResponse.model_validate(credit_note),\n                message=f"Credit note \'{credit_note.credit_note_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating credit note: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create credit note: {str(e)}",\n                code="CREATE_CREDIT_NOTE_ERROR",\n            )\n    \n    @transactional\n    @emit_event("credit_note.applied")\n    @requires_permission("credit_note:apply")\n    async def apply_credit_note(\n        self,\n        context: ServiceContext,\n        credit_note_id: UUID,\n        invoice_id: UUID,\n        amount: Decimal,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Apply credit note to an invoice."""\n        try:\n            credit_note = await self.credit_note_repo.get_by_id(credit_note_id)\n            if not credit_note:\n                return ServiceResult.failure(\n                    error="Credit note not found",\n                    code="CREDIT_NOTE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, credit_note.company_id)\n            \n            if credit_note.status != CreditNoteStatus.APPROVED:\n                return ServiceResult.failure(\n                    error="Credit note must be approved before applying",\n                    code="CREDIT_NOTE_NOT_APPROVED",\n                )\n            \n            if amount > credit_note.amount_available:\n                return ServiceResult.failure(\n                    error=f"Amount exceeds available credit ({credit_note.amount_available})",\n                    code="INSUFFICIENT_CREDIT",\n                )\n            \n            # Get invoice\n            invoice = await self.invoice_repo.get_by_id(invoice_id)\n            if not invoice:\n                return ServiceResult.failure(\n                    error="Invoice not found",\n                    code="INVOICE_NOT_FOUND",\n                )\n            \n            # Apply credit\n            credit_note.amount_used = (credit_note.amount_used or Decimal("0")) + amount\n            credit_note.amount_available = credit_note.amount_available - amount\n            \n            invoice.amount_paid = (invoice.amount_paid or Decimal("0")) + amount\n            invoice.amount_due = (invoice.amount_due or Decimal("0")) - amount\n            \n            # Update statuses\n            if credit_note.amount_available <= Decimal("0"):\n                credit_note.status = CreditNoteStatus.FULLY_APPLIED\n            \n            if invoice.amount_due <= Decimal("0"):\n                invoice.status = InvoiceStatus.PAID\n                invoice.paid_at = datetime.utcnow()\n            \n            # Create allocation record\n            await self.credit_note_repo.create_allocation({\n                "credit_note_id": credit_note_id,\n                "invoice_id": invoice_id,\n                "amount": amount,\n                "applied_by": context.user_id,\n                "applied_at": datetime.utcnow(),\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data={\n                    "credit_note": CreditNoteResponse.model_validate(credit_note),\n                    "invoice": await self._build_invoice_response(invoice),\n                    "applied_amount": amount,\n                },\n                message=f"Credit note applied successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error applying credit note: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to apply credit note: {str(e)}",\n                code="APPLY_CREDIT_NOTE_ERROR",\n            )\n    \n    # ==================== Recurring Invoices ====================\n    \n    @transactional\n    @emit_event("recurring_invoice.created")\n    @requires_permission("recurring_invoice:create")\n    async def create_recurring_invoice(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: RecurringInvoiceCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a recurring invoice template."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Validate schedule\n            if data.end_date and data.end_date < data.start_date:\n                return ServiceResult.failure(\n                    error="End date must be after start date",\n                    code="INVALID_DATE_RANGE",\n                )\n            \n            # Create recurring invoice\n            recurring = await self.recurring_repo.create({\n                "company_id": company_id,\n                "name": data.name,\n                "description": data.description,\n                "frequency": data.frequency,\n                "interval": data.interval or 1,\n                "start_date": data.start_date,\n                "end_date": data.end_date,\n                "next_date": data.start_date,\n                "customer_id": data.customer_id,\n                "vendor_id": data.vendor_id,\n                "currency_code": data.currency_code,\n                "invoice_template": data.invoice_template,\n                "status": data.status or RecurringInvoiceStatus.ACTIVE,\n                "auto_send": data.auto_send,\n                "auto_charge": data.auto_charge,\n                "payment_method_id": data.payment_method_id,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=recurring.to_dict(),\n                message=f"Recurring invoice \'{recurring.name}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating recurring invoice: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create recurring invoice: {str(e)}",\n                code="CREATE_RECURRING_INVOICE_ERROR",\n            )\n    \n    async def process_recurring_invoices(\n        self,\n        context: ServiceContext,\n        company_id: Optional[UUID] = None,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Process all due recurring invoices."""\n        try:\n            if company_id:\n                await self.ensure_company_scope(context, company_id)\n            \n            # Get due recurring invoices\n            due_recurring = await self.recurring_repo.get_due(\n                company_id=company_id,\n                as_of_date=date.today(),\n            )\n            \n            processed = []\n            failed = []\n            \n            for recurring in due_recurring:\n                try:\n                    # Create invoice from template\n                    invoice_data = await self._build_invoice_from_template(recurring)\n                    \n                    result = await self.create_invoice(\n                        context=context,\n                        company_id=recurring.company_id,\n                        data=invoice_data,\n                    )\n                    \n                    if result.is_success:\n                        # Update next date\n                        recurring.next_date = self._calculate_next_date(\n                            current_date=recurring.next_date,\n                            frequency=recurring.frequency,\n                            interval=recurring.interval,\n                        )\n                        recurring.last_generated = date.today()\n                        recurring.generation_count = (recurring.generation_count or 0) + 1\n                        \n                        processed.append({\n                            "recurring_id": str(recurring.id),\n                            "invoice_number": result.data.invoice_number,\n                        })\n                    else:\n                        failed.append({\n                            "recurring_id": str(recurring.id),\n                            "error": result.error,\n                        })\n                        \n                except Exception as e:\n                    failed.append({\n                        "recurring_id": str(recurring.id),\n                        "error": str(e),\n                    })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(data={\n                "processed": processed,\n                "failed": failed,\n                "total_processed": len(processed),\n                "total_failed": len(failed),\n            })\n            \n        except Exception as e:\n            self._log_error(f"Error processing recurring invoices: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to process recurring invoices: {str(e)}",\n                code="PROCESS_RECURRING_ERROR",\n            )\n    \n    # ==================== Reports ====================\n    \n    @cached(ttl=60, key_prefix="aging_report")\n    @requires_permission("report:aging")\n    async def get_aging_report(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        as_of_date: Optional[date] = None,\n        customer_id: Optional[UUID] = None,\n        currency_code: Optional[str] = None,\n    ) -> ServiceResult[AgingReport]:\n        """\n        Generate an accounts receivable/payable aging report.\n        \n        Shows outstanding amounts broken down by age buckets.\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            as_of_date = as_of_date or date.today()\n            \n            # Get outstanding invoices\n            invoices = await self.invoice_repo.get_outstanding(\n                company_id=company_id,\n                as_of_date=as_of_date,\n                customer_id=customer_id,\n                currency_code=currency_code,\n            )\n            \n            # Calculate aging buckets\n            aging_buckets = {\n                "current": Decimal("0"),\n                "1_30_days": Decimal("0"),\n                "31_60_days": Decimal("0"),\n                "61_90_days": Decimal("0"),\n                "over_90_days": Decimal("0"),\n            }\n            \n            customer_aging: Dict[str, Dict[str, Any]] = {}\n            \n            for invoice in invoices:\n                days_overdue = (as_of_date - invoice.due_date).days\n                amount_due = invoice.amount_due or Decimal("0")\n                \n                # Determine bucket\n                if days_overdue <= 0:\n                    bucket = "current"\n                elif days_overdue <= 30:\n                    bucket = "1_30_days"\n                elif days_overdue <= 60:\n                    bucket = "31_60_days"\n                elif days_overdue <= 90:\n                    bucket = "61_90_days"\n                else:\n                    bucket = "over_90_days"\n                \n                aging_buckets[bucket] += amount_due\n                \n                # Customer-level aging\n                cust_id = str(invoice.customer_id or invoice.vendor_id)\n                if cust_id not in customer_aging:\n                    customer_aging[cust_id] = {\n                        "customer_id": cust_id,\n                        "customer_name": invoice.customer.name if invoice.customer else invoice.vendor.name if invoice.vendor else None,\n                        "current": Decimal("0"),\n                        "1_30_days": Decimal("0"),\n                        "31_60_days": Decimal("0"),\n                        "61_90_days": Decimal("0"),\n                        "over_90_days": Decimal("0"),\n                        "total": Decimal("0"),\n                        "invoices": [],\n                    }\n                \n                customer_aging[cust_id][bucket] += amount_due\n                customer_aging[cust_id]["total"] += amount_due\n                customer_aging[cust_id]["invoices"].append({\n                    "invoice_id": str(invoice.id),\n                    "invoice_number": invoice.invoice_number,\n                    "due_date": invoice.due_date.isoformat(),\n                    "amount_due": str(amount_due),\n                    "days_overdue": days_overdue,\n                })\n            \n            report = AgingReport(\n                company_id=company_id,\n                as_of_date=as_of_date,\n                currency_code=currency_code,\n                current=aging_buckets["current"],\n                days_1_30=aging_buckets["1_30_days"],\n                days_31_60=aging_buckets["31_60_days"],\n                days_61_90=aging_buckets["61_90_days"],\n                over_90_days=aging_buckets["over_90_days"],\n                total=sum(aging_buckets.values()),\n                customer_aging=list(customer_aging.values()),\n                generated_at=datetime.utcnow(),\n            )\n            \n            return ServiceResult.success(data=report)\n            \n        except Exception as e:\n            self._log_error(f"Error generating aging report: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to generate aging report: {str(e)}",\n                code="AGING_REPORT_ERROR",\n            )\n    \n    @cached(ttl=300, key_prefix="invoice_summary")\n    async def get_invoice_summary(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        period_start: Optional[date] = None,\n        period_end: Optional[date] = None,\n    ) -> ServiceResult[InvoiceSummary]:\n        """Get invoice summary statistics."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            period_end = period_end or date.today()\n            period_start = period_start or (period_end - timedelta(days=30))\n            \n            # Get statistics\n            stats = await self.invoice_repo.get_statistics(\n                company_id=company_id,\n                period_start=period_start,\n                period_end=period_end,\n            )\n            \n            summary = InvoiceSummary(\n                company_id=company_id,\n                period_start=period_start,\n                period_end=period_end,\n                total_invoices=stats.get("total_invoices", 0),\n                total_amount=stats.get("total_amount", Decimal("0")),\n                paid_amount=stats.get("paid_amount", Decimal("0")),\n                outstanding_amount=stats.get("outstanding_amount", Decimal("0")),\n                overdue_amount=stats.get("overdue_amount", Decimal("0")),\n                draft_count=stats.get("draft_count", 0),\n                sent_count=stats.get("sent_count", 0),\n                paid_count=stats.get("paid_count", 0),\n                overdue_count=stats.get("overdue_count", 0),\n                average_payment_days=stats.get("average_payment_days", 0),\n                generated_at=datetime.utcnow(),\n            )\n            \n            return ServiceResult.success(data=summary)\n            \n        except Exception as e:\n            self._log_error(f"Error getting invoice summary: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get invoice summary: {str(e)}",\n                code="INVOICE_SUMMARY_ERROR",\n            )\n    \n    # ==================== Helper Methods ====================\n    \n    async def _generate_invoice_number(\n        self,\n        company_id: UUID,\n        invoice_type: InvoiceType,\n    ) -> str:\n        """Generate a unique invoice number."""\n        prefix_map = {\n            InvoiceType.SALES_INVOICE: "INV",\n            InvoiceType.SALES_RECEIPT: "SR",\n            InvoiceType.PURCHASE_INVOICE: "BILL",\n            InvoiceType.DEBIT_NOTE: "DN",\n        }\n        \n        prefix = prefix_map.get(invoice_type, "INV")\n        count = await self.invoice_repo.count_by_type(\n            company_id=company_id,\n            invoice_type=invoice_type,\n        )\n        year = date.today().year\n        \n        return f"{prefix}-{year}-{str(count + 1).zfill(6)}"\n    \n    async def _validate_and_calculate_lines(\n        self,\n        company_id: UUID,\n        lines: List[InvoiceLineCreate],\n        currency_code: str,\n    ) -> Tuple[List[Dict[str, Any]], Dict[str, Decimal]]:\n        """Validate line items and calculate totals."""\n        validated_lines = []\n        subtotal = Decimal("0")\n        total_tax = Decimal("0")\n        total_discount = Decimal("0")\n        \n        for line in lines:\n            # Calculate line amounts\n            quantity = line.quantity or Decimal("1")\n            unit_price = line.unit_price\n            line_subtotal = quantity * unit_price\n            \n            # Apply discount\n            discount = Decimal("0")\n            if line.discount_percent:\n                discount = line_subtotal * (line.discount_percent / Decimal("100"))\n            elif line.discount_amount:\n                discount = line.discount_amount\n            \n            line_total = line_subtotal - discount\n            \n            # Calculate tax\n            tax = Decimal("0")\n            if line.tax_rate_id:\n                # Would look up tax rate and calculate\n                pass\n            elif line.tax_percent:\n                tax = line_total * (line.tax_percent / Decimal("100"))\n            \n            subtotal += line_total\n            total_tax += tax\n            total_discount += discount\n            \n            validated_lines.append({\n                "product_id": line.product_id,\n                "description": line.description,\n                "quantity": quantity,\n                "unit_price": unit_price,\n                "discount_percent": line.discount_percent,\n                "discount_amount": discount,\n                "tax_rate_id": line.tax_rate_id,\n                "tax_percent": line.tax_percent,\n                "tax_amount": tax,\n                "line_total": line_total + tax,\n                "gl_account_id": line.gl_account_id,\n                "cost_center_id": line.cost_center_id,\n                "project_id": line.project_id,\n                "custom_fields": line.custom_fields,\n            })\n        \n        totals = {\n            "subtotal": subtotal,\n            "discount": total_discount,\n            "tax": total_tax,\n            "total": subtotal + total_tax,\n        }\n        \n        return validated_lines, totals\n    \n    async def _validate_credit_note_lines(\n        self,\n        company_id: UUID,\n        lines: List[Any],\n        currency_code: str,\n    ) -> Tuple[List[Dict[str, Any]], Dict[str, Decimal]]:\n        """Validate credit note lines and calculate totals."""\n        # Similar to invoice lines but for credit notes\n        validated_lines = []\n        subtotal = Decimal("0")\n        total_tax = Decimal("0")\n        \n        for line in lines:\n            quantity = line.quantity or Decimal("1")\n            unit_price = line.unit_price\n            line_total = quantity * unit_price\n            \n            tax = Decimal("0")\n            if line.tax_percent:\n                tax = line_total * (line.tax_percent / Decimal("100"))\n            \n            subtotal += line_total\n            total_tax += tax\n            \n            validated_lines.append({\n                "product_id": line.product_id,\n                "description": line.description,\n                "quantity": quantity,\n                "unit_price": unit_price,\n                "tax_percent": line.tax_percent,\n                "tax_amount": tax,\n                "line_total": line_total + tax,\n            })\n        \n        return validated_lines, {\n            "subtotal": subtotal,\n            "tax": total_tax,\n            "total": subtotal + total_tax,\n        }\n    \n    async def _calculate_due_date(\n        self,\n        invoice_date: date,\n        payment_terms: Optional[str] = None,\n    ) -> date:\n        """Calculate due date based on payment terms."""\n        if not payment_terms:\n            return invoice_date + timedelta(days=30)  # Net 30 default\n        \n        # Parse common payment terms\n        terms_map = {\n            "net_10": 10,\n            "net_15": 15,\n            "net_30": 30,\n            "net_45": 45,\n            "net_60": 60,\n            "net_90": 90,\n            "due_on_receipt": 0,\n            "cod": 0,\n        }\n        \n        days = terms_map.get(payment_terms.lower(), 30)\n        return invoice_date + timedelta(days=days)\n    \n    async def _apply_early_payment_discount(\n        self,\n        invoice: Invoice,\n        discount_data: Dict[str, Any],\n    ) -> None:\n        """Apply early payment discount terms."""\n        # Would create discount record\n        pass\n    \n    async def _send_invoice_email(\n        self,\n        invoice: Invoice,\n        email_addresses: Optional[List[str]] = None,\n        message: Optional[str] = None,\n    ) -> None:\n        """Send invoice via email."""\n        # Would integrate with email service\n        pass\n    \n    async def _generate_payment_number(\n        self,\n        company_id: UUID,\n        payment_type: str,\n    ) -> str:\n        """Generate a unique payment number."""\n        prefix = "PAY" if payment_type == "receipt" else "PYM"\n        count = await self.payment_repo.count_by_company(company_id)\n        year = date.today().year\n        \n        return f"{prefix}-{year}-{str(count + 1).zfill(6)}"\n    \n    async def _generate_credit_note_number(\n        self,\n        company_id: UUID,\n    ) -> str:\n        """Generate a unique credit note number."""\n        count = await self.credit_note_repo.count_by_company(company_id)\n        year = date.today().year\n        \n        return f"CN-{year}-{str(count + 1).zfill(6)}"\n    \n    async def _post_payment(self, payment: Payment) -> None:\n        """Post payment and update GL."""\n        # Would create journal entries for the payment\n        pass\n    \n    async def _build_invoice_response(self, invoice: Invoice) -> InvoiceResponse:\n        """Build complete invoice response with lines."""\n        lines = await self.invoice_repo.get_lines_by_invoice(invoice.id)\n        \n        return InvoiceResponse(\n            id=invoice.id,\n            company_id=invoice.company_id,\n            invoice_number=invoice.invoice_number,\n            invoice_type=invoice.invoice_type,\n            status=invoice.status,\n            invoice_date=invoice.invoice_date,\n            due_date=invoice.due_date,\n            customer_id=invoice.customer_id,\n            vendor_id=invoice.vendor_id,\n            currency_code=invoice.currency_code,\n            subtotal=invoice.subtotal,\n            discount_amount=invoice.discount_amount,\n            tax_amount=invoice.tax_amount,\n            total_amount=invoice.total_amount,\n            amount_due=invoice.amount_due,\n            amount_paid=invoice.amount_paid,\n            payment_terms=invoice.payment_terms,\n            notes=invoice.notes,\n            lines=[\n                {\n                    "id": str(l.id),\n                    "description": l.description,\n                    "quantity": l.quantity,\n                    "unit_price": l.unit_price,\n                    "tax_amount": l.tax_amount,\n                    "line_total": l.line_total,\n                }\n                for l in lines\n            ],\n            created_at=invoice.created_at,\n            created_by=invoice.created_by,\n            sent_at=invoice.sent_at,\n            paid_at=invoice.paid_at,\n        )\n    \n    async def _build_invoice_from_template(\n        self,\n        recurring: RecurringInvoice,\n    ) -> InvoiceCreate:\n        """Build invoice data from recurring template."""\n        template = recurring.invoice_template\n        \n        # Build invoice create from template\n        return InvoiceCreate(\n            invoice_type=template.get("invoice_type", InvoiceType.SALES_INVOICE),\n            customer_id=recurring.customer_id,\n            vendor_id=recurring.vendor_id,\n            currency_code=recurring.currency_code,\n            invoice_date=date.today(),\n            lines=template.get("lines", []),\n            payment_terms=template.get("payment_terms"),\n            notes=template.get("notes"),\n            terms_and_conditions=template.get("terms_and_conditions"),\n            custom_fields=template.get("custom_fields"),\n        )\n    \n    def _calculate_next_date(\n        self,\n        current_date: date,\n        frequency: str,\n        interval: int,\n    ) -> date:\n        """Calculate next occurrence date."""\n        if frequency == "daily":\n            return current_date + timedelta(days=interval)\n        elif frequency == "weekly":\n            return current_date + timedelta(weeks=interval)\n        elif frequency == "monthly":\n            # Add months\n            month = current_date.month + interval\n            year = current_date.year + (month - 1) // 12\n            month = (month - 1) % 12 + 1\n            return current_date.replace(year=year, month=month)\n        elif frequency == "yearly":\n            return current_date.replace(year=current_date.year + interval)\n        else:\n            return current_date + timedelta(days=30)\n</create-file>\n</function_calls>'}
{'role': 'user', 'content': '<tool_result> <create-file> ToolResult(success=True, output="File \'backend/app/services/invoice.py\' created successfully.") </create-file> </tool_result>'}
{'role': 'assistant', 'content': 'Now let me create the HR service for employees, payroll, and attendance management.\n\n<function_calls>\n<create-file file_path="backend/app/services/hr.py">"""\nHR Service - Human Resources and Payroll Business Logic\nSAMMS ENTERPRISE SOVEREIGN OPERATING SYSTEM (SESOS v6 OMEGA CONSTRAINED)\n\nThis service implements comprehensive HR operations including\nemployee management, payroll processing, attendance tracking,\nand leave management.\n"""\n\nfrom typing import Optional, List, Dict, Any, Tuple, TYPE_CHECKING\nfrom uuid import UUID\nfrom decimal import Decimal\nfrom datetime import date, datetime, timedelta\nfrom enum import Enum\nimport asyncio\nimport json\n\nfrom sqlalchemy import select, and_, or_, func, case, literal_column\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom sqlalchemy.orm import selectinload, joinedload\n\nfrom .base import (\n    BaseService,\n    CompanyScopedService,\n    ServiceResult,\n    ServiceContext,\n    transactional,\n    cached,\n    emit_event,\n    requires_permission,\n    CacheManager,\n    EventPublisher,\n)\nfrom ..models.hr import (\n    Employee,\n    EmployeeStatus,\n    EmploymentType,\n    Department,\n    Position,\n    PayGrade,\n    SalaryStructure,\n    SalaryComponent,\n    PayrollRun,\n    PayrollRunStatus,\n    Payslip,\n    PayslipLine,\n    AttendanceRecord,\n    AttendanceStatus,\n    LeaveType,\n    LeaveBalance,\n    LeaveRequest,\n    LeaveRequestStatus,\n    TimeEntry,\n    Timesheet,\n    TimesheetStatus,\n    PerformanceReview,\n    PerformanceReviewStatus,\n    EmployeeDocument,\n    Training,\n    EmployeeTraining,\n    Recruitment,\n    JobApplication,\n    ApplicationStatus,\n    OnboardingTask,\n    OffboardingTask,\n)\nfrom ..schemas.hr import (\n    EmployeeCreate,\n    EmployeeUpdate,\n    EmployeeResponse,\n    DepartmentCreate,\n    PositionCreate,\n    PayrollRunCreate,\n    PayslipResponse,\n    AttendanceRecordCreate,\n    LeaveRequestCreate,\n    LeaveRequestResponse,\n    TimeEntryCreate,\n    TimesheetResponse,\n    PerformanceReviewCreate,\n    EmployeeFilter,\n    PayrollFilter,\n    LeaveBalanceResponse,\n    OrgChart,\n)\nfrom ..repositories.hr import (\n    EmployeeRepository,\n    DepartmentRepository,\n    PositionRepository,\n    PayrollRepository,\n    AttendanceRepository,\n    LeaveRepository,\n    TimeEntryRepository,\n    PerformanceRepository,\n    DocumentRepository,\n    TrainingRepository,\n    RecruitmentRepository,\n)\nfrom ..core.exceptions import (\n    ValidationError,\n    NotFoundError,\n    BusinessLogicError,\n    PermissionDeniedError,\n)\n\nif TYPE_CHECKING:\n    from ..repositories.hr import EmployeeRepository\n\n\nclass HRService(CompanyScopedService):\n    """\n    Service for managing human resources and payroll operations.\n    \n    Implements:\n    - Employee lifecycle management\n    - Department and position management\n    - Salary structure and payroll processing\n    - Attendance tracking\n    - Leave management\n    - Time tracking and timesheets\n    - Performance reviews\n    - Training management\n    - Recruitment\n    - Onboarding/offboarding\n    """\n    \n    def __init__(\n        self,\n        session: AsyncSession,\n        cache: Optional[CacheManager] = None,\n        event_publisher: Optional[EventPublisher] = None,\n    ):\n        super().__init__(session, cache, event_publisher)\n        self.employee_repo = EmployeeRepository(session)\n        self.department_repo = DepartmentRepository(session)\n        self.position_repo = PositionRepository(session)\n        self.payroll_repo = PayrollRepository(session)\n        self.attendance_repo = AttendanceRepository(session)\n        self.leave_repo = LeaveRepository(session)\n        self.time_entry_repo = TimeEntryRepository(session)\n        self.performance_repo = PerformanceRepository(session)\n        self.document_repo = DocumentRepository(session)\n        self.training_repo = TrainingRepository(session)\n        self.recruitment_repo = RecruitmentRepository(session)\n    \n    # ==================== Employee Management ====================\n    \n    @transactional\n    @emit_event("employee.created")\n    @requires_permission("employee:create")\n    async def create_employee(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: EmployeeCreate,\n    ) -> ServiceResult[EmployeeResponse]:\n        """\n        Create a new employee record.\n        \n        Validates:\n        - Employee ID/number uniqueness\n        - Department and position validity\n        - Manager validity\n        - Salary structure validity\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Check for duplicate employee number\n            existing = await self.employee_repo.get_by_employee_number(\n                company_id=company_id,\n                employee_number=data.employee_number,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error=f"Employee number \'{data.employee_number}\' already exists",\n                    code="DUPLICATE_EMPLOYEE_NUMBER",\n                )\n            \n            # Validate department\n            if data.department_id:\n                department = await self.department_repo.get_by_id(data.department_id)\n                if not department or department.company_id != company_id:\n                    return ServiceResult.failure(\n                        error="Invalid department",\n                        code="INVALID_DEPARTMENT",\n                    )\n            \n            # Validate position\n            if data.position_id:\n                position = await self.position_repo.get_by_id(data.position_id)\n                if not position or position.company_id != company_id:\n                    return ServiceResult.failure(\n                        error="Invalid position",\n                        code="INVALID_POSITION",\n                    )\n            \n            # Validate manager\n            if data.manager_id:\n                manager = await self.employee_repo.get_by_id(data.manager_id)\n                if not manager or manager.company_id != company_id:\n                    return ServiceResult.failure(\n                        error="Invalid manager",\n                        code="INVALID_MANAGER",\n                    )\n            \n            # Generate employee number if not provided\n            employee_number = data.employee_number or await self._generate_employee_number(\n                company_id=company_id,\n            )\n            \n            # Create employee\n            employee = await self.employee_repo.create({\n                "company_id": company_id,\n                "employee_number": employee_number,\n                "first_name": data.first_name,\n                "last_name": data.last_name,\n                "email": data.email,\n                "phone": data.phone,\n                "personal_email": data.personal_email,\n                "date_of_birth": data.date_of_birth,\n                "gender": data.gender,\n                "nationality": data.nationality,\n                "national_id": data.national_id,\n                "passport_number": data.passport_number,\n                "marital_status": data.marital_status,\n                "address": data.address,\n                "emergency_contact": data.emergency_contact,\n                "hire_date": data.hire_date or date.today(),\n                "probation_end_date": data.probation_end_date,\n                "termination_date": data.termination_date,\n                "status": data.status or EmployeeStatus.ACTIVE,\n                "employment_type": data.employment_type or EmploymentType.FULL_TIME,\n                "department_id": data.department_id,\n                "position_id": data.position_id,\n                "manager_id": data.manager_id,\n                "work_location": data.work_location,\n                "work_email": data.work_email,\n                "work_phone": data.work_phone,\n                "base_salary": data.base_salary,\n                "salary_currency": data.salary_currency,\n                "pay_frequency": data.pay_frequency,\n                "bank_name": data.bank_name,\n                "bank_account": data.bank_account,\n                "tax_id": data.tax_id,\n                "social_security_number": data.social_security_number,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            # Initialize leave balances\n            await self._initialize_leave_balances(employee)\n            \n            # Create onboarding tasks\n            if data.create_onboarding:\n                await self._create_onboarding_tasks(employee)\n            \n            self._log_info(f"Created employee {employee.employee_number}")\n            \n            return ServiceResult.success(\n                data=EmployeeResponse.model_validate(employee),\n                message=f"Employee \'{employee.employee_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating employee: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create employee: {str(e)}",\n                code="CREATE_EMPLOYEE_ERROR",\n            )\n    \n    @transactional\n    @emit_event("employee.updated")\n    @requires_permission("employee:update")\n    async def update_employee(\n        self,\n        context: ServiceContext,\n        employee_id: UUID,\n        data: EmployeeUpdate,\n    ) -> ServiceResult[EmployeeResponse]:\n        """Update an existing employee record."""\n        try:\n            employee = await self.employee_repo.get_by_id(employee_id)\n            if not employee:\n                return ServiceResult.failure(\n                    error="Employee not found",\n                    code="EMPLOYEE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, employee.company_id)\n            \n            # Track changes for audit\n            changes = {}\n            \n            # Update fields\n            update_data = data.model_dump(exclude_unset=True)\n            update_data["updated_by"] = context.user_id\n            \n            for field, value in update_data.items():\n                old_value = getattr(employee, field, None)\n                if old_value != value:\n                    changes[field] = {"old": old_value, "new": value}\n                setattr(employee, field, value)\n            \n            # Handle status changes\n            if data.status and data.status != employee.status:\n                await self._handle_status_change(employee, data.status)\n            \n            await self._session.flush()\n            \n            # Log changes\n            if changes:\n                await self._log_employee_changes(employee, changes)\n            \n            return ServiceResult.success(\n                data=EmployeeResponse.model_validate(employee),\n                message=f"Employee \'{employee.employee_number}\' updated successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error updating employee: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to update employee: {str(e)}",\n                code="UPDATE_EMPLOYEE_ERROR",\n            )\n    \n    @transactional\n    @emit_event("employee.terminated")\n    @requires_permission("employee:terminate")\n    async def terminate_employee(\n        self,\n        context: ServiceContext,\n        employee_id: UUID,\n        termination_date: date,\n        termination_reason: str,\n        notice_period_days: Optional[int] = None,\n        create_offboarding: bool = True,\n    ) -> ServiceResult[EmployeeResponse]:\n        """Terminate an employee with offboarding."""\n        try:\n            employee = await self.employee_repo.get_by_id(employee_id)\n            if not employee:\n                return ServiceResult.failure(\n                    error="Employee not found",\n                    code="EMPLOYEE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, employee.company_id)\n            \n            if employee.status == EmployeeStatus.TERMINATED:\n                return ServiceResult.failure(\n                    error="Employee is already terminated",\n                    code="ALREADY_TERMINATED",\n                )\n            \n            # Update employee status\n            employee.status = EmployeeStatus.TERMINATED\n            employee.termination_date = termination_date\n            employee.termination_reason = termination_reason\n            employee.terminated_at = datetime.utcnow()\n            employee.terminated_by = context.user_id\n            \n            # Create offboarding tasks\n            if create_offboarding:\n                await self._create_offboarding_tasks(employee)\n            \n            # Calculate final pay\n            final_pay = await self._calculate_final_pay(employee, termination_date)\n            \n            await self._session.flush()\n            \n            self._log_info(f"Terminated employee {employee.employee_number}")\n            \n            return ServiceResult.success(\n                data=EmployeeResponse.model_validate(employee),\n                message=f"Employee \'{employee.employee_number}\' terminated successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error terminating employee: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to terminate employee: {str(e)}",\n                code="TERMINATE_EMPLOYEE_ERROR",\n            )\n    \n    async def get_employees(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        filters: Optional[EmployeeFilter] = None,\n        page: int = 1,\n        page_size: int = 50,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Get paginated employees with filtering."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            result = await self.employee_repo.get_paginated(\n                company_id=company_id,\n                filters=filters,\n                page=page,\n                page_size=page_size,\n            )\n            \n            return ServiceResult.success(data={\n                "items": [EmployeeResponse.model_validate(e) for e in result["items"]],\n                "total": result["total"],\n                "page": page,\n                "page_size": page_size,\n                "total_pages": (result["total"] + page_size - 1) // page_size,\n            })\n            \n        except Exception as e:\n            self._log_error(f"Error getting employees: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get employees: {str(e)}",\n                code="GET_EMPLOYEES_ERROR",\n            )\n    \n    async def get_org_chart(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        department_id: Optional[UUID] = None,\n    ) -> ServiceResult[OrgChart]:\n        """Get organizational chart hierarchy."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Get all active employees\n            employees = await self.employee_repo.get_by_company(\n                company_id=company_id,\n                status=EmployeeStatus.ACTIVE,\n                department_id=department_id,\n            )\n            \n            # Build org chart tree\n            tree = self._build_org_tree(employees)\n            \n            org_chart = OrgChart(\n                company_id=company_id,\n                department_id=department_id,\n                tree=tree,\n                total_employees=len(employees),\n                generated_at=datetime.utcnow(),\n            )\n            \n            return ServiceResult.success(data=org_chart)\n            \n        except Exception as e:\n            self._log_error(f"Error getting org chart: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get org chart: {str(e)}",\n                code="ORG_CHART_ERROR",\n            )\n    \n    # ==================== Department & Position Management ====================\n    \n    @transactional\n    @emit_event("department.created")\n    @requires_permission("department:create")\n    async def create_department(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: DepartmentCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a new department."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Check for duplicate name\n            existing = await self.department_repo.get_by_name(\n                company_id=company_id,\n                name=data.name,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error=f"Department \'{data.name}\' already exists",\n                    code="DUPLICATE_DEPARTMENT",\n                )\n            \n            # Validate parent department\n            if data.parent_id:\n                parent = await self.department_repo.get_by_id(data.parent_id)\n                if not parent or parent.company_id != company_id:\n                    return ServiceResult.failure(\n                        error="Invalid parent department",\n                        code="INVALID_PARENT",\n                    )\n            \n            department = await self.department_repo.create({\n                "company_id": company_id,\n                "name": data.name,\n                "code": data.code,\n                "description": data.description,\n                "parent_id": data.parent_id,\n                "manager_id": data.manager_id,\n                "cost_center_id": data.cost_center_id,\n                "location": data.location,\n                "budget": data.budget,\n                "is_active": data.is_active,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=department.to_dict(),\n                message=f"Department \'{department.name}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating department: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create department: {str(e)}",\n                code="CREATE_DEPARTMENT_ERROR",\n            )\n    \n    @transactional\n    @emit_event("position.created")\n    @requires_permission("position:create")\n    async def create_position(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: PositionCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a new position."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Validate department\n            if data.department_id:\n                dept = await self.department_repo.get_by_id(data.department_id)\n                if not dept or dept.company_id != company_id:\n                    return ServiceResult.failure(\n                        error="Invalid department",\n                        code="INVALID_DEPARTMENT",\n                    )\n            \n            position = await self.position_repo.create({\n                "company_id": company_id,\n                "title": data.title,\n                "code": data.code,\n                "description": data.description,\n                "department_id": data.department_id,\n                "reports_to_id": data.reports_to_id,\n                "grade_id": data.grade_id,\n                "min_salary": data.min_salary,\n                "max_salary": data.max_salary,\n                "responsibilities": data.responsibilities,\n                "qualifications": data.qualifications,\n                "skills_required": data.skills_required,\n                "is_active": data.is_active,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=position.to_dict(),\n                message=f"Position \'{position.title}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating position: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create position: {str(e)}",\n                code="CREATE_POSITION_ERROR",\n            )\n    \n    # ==================== Payroll Processing ====================\n    \n    @transactional\n    @emit_event("payroll_run.created")\n    @requires_permission("payroll:create")\n    async def create_payroll_run(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: PayrollRunCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """\n        Create a new payroll run.\n        \n        Processes salaries for all eligible employees in the period.\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Check for existing run for the same period\n            existing = await self.payroll_repo.get_by_period(\n                company_id=company_id,\n                pay_period_start=data.pay_period_start,\n                pay_period_end=data.pay_period_end,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error="Payroll run already exists for this period",\n                    code="PAYROLL_RUN_EXISTS",\n                )\n            \n            # Generate run number\n            run_number = await self._generate_payroll_run_number(company_id)\n            \n            # Create payroll run\n            payroll_run = await self.payroll_repo.create({\n                "company_id": company_id,\n                "run_number": run_number,\n                "pay_period_start": data.pay_period_start,\n                "pay_period_end": data.pay_period_end,\n                "pay_date": data.pay_date,\n                "status": PayrollRunStatus.DRAFT,\n                "total_gross": Decimal("0"),\n                "total_deductions": Decimal("0"),\n                "total_net": Decimal("0"),\n                "total_employees": 0,\n                "currency_code": data.currency_code,\n                "notes": data.notes,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            # Get eligible employees\n            employees = await self.employee_repo.get_for_payroll(\n                company_id=company_id,\n                as_of_date=data.pay_period_end,\n            )\n            \n            # Create payslips\n            total_gross = Decimal("0")\n            total_deductions = Decimal("0")\n            total_net = Decimal("0")\n            \n            for employee in employees:\n                payslip = await self._create_payslip(\n                    payroll_run=payroll_run,\n                    employee=employee,\n                    period_start=data.pay_period_start,\n                    period_end=data.pay_period_end,\n                )\n                \n                total_gross += payslip.gross_pay\n                total_deductions += payslip.total_deductions\n                total_net += payslip.net_pay\n            \n            # Update totals\n            payroll_run.total_gross = total_gross\n            payroll_run.total_deductions = total_deductions\n            payroll_run.total_net = total_net\n            payroll_run.total_employees = len(employees)\n            \n            await self._session.flush()\n            \n            self._log_info(f"Created payroll run {payroll_run.run_number} with {len(employees)} payslips")\n            \n            return ServiceResult.success(\n                data={\n                    "payroll_run": payroll_run.to_dict(),\n                    "payslip_count": len(employees),\n                    "total_gross": str(total_gross),\n                    "total_net": str(total_net),\n                },\n                message=f"Payroll run \'{payroll_run.run_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating payroll run: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create payroll run: {str(e)}",\n                code="CREATE_PAYROLL_RUN_ERROR",\n            )\n    \n    @transactional\n    @emit_event("payroll_run.approved")\n    @requires_permission("payroll:approve")\n    async def approve_payroll_run(\n        self,\n        context: ServiceContext,\n        run_id: UUID,\n        notes: Optional[str] = None,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Approve a payroll run for payment."""\n        try:\n            run = await self.payroll_repo.get_by_id(run_id)\n            if not run:\n                return ServiceResult.failure(\n                    error="Payroll run not found",\n                    code="PAYROLL_RUN_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, run.company_id)\n            \n            if run.status != PayrollRunStatus.DRAFT:\n                return ServiceResult.failure(\n                    error=f"Cannot approve payroll run with status \'{run.status}\'",\n                    code="INVALID_STATUS",\n                )\n            \n            # Approve the run\n            run.status = PayrollRunStatus.APPROVED\n            run.approved_at = datetime.utcnow()\n            run.approved_by = context.user_id\n            run.approval_notes = notes\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=run.to_dict(),\n                message=f"Payroll run \'{run.run_number}\' approved successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error approving payroll run: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to approve payroll run: {str(e)}",\n                code="APPROVE_PAYROLL_RUN_ERROR",\n            )\n    \n    @transactional\n    @emit_event("payroll_run.processed")\n    @requires_permission("payroll:process")\n    async def process_payroll_run(\n        self,\n        context: ServiceContext,\n        run_id: UUID,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Process approved payroll run and generate payments."""\n        try:\n            run = await self.payroll_repo.get_by_id_with_payslips(run_id)\n            if not run:\n                return ServiceResult.failure(\n                    error="Payroll run not found",\n                    code="PAYROLL_RUN_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, run.company_id)\n            \n            if run.status != PayrollRunStatus.APPROVED:\n                return ServiceResult.failure(\n                    error="Payroll run must be approved before processing",\n                    code="NOT_APPROVED",\n                )\n            \n            # Process each payslip\n            processed = []\n            failed = []\n            \n            for payslip in run.payslips:\n                try:\n                    # Create payment transaction\n                    payment = await self._create_salary_payment(payslip)\n                    payslip.payment_id = payment.get("id")\n                    payslip.status = "paid"\n                    payslip.paid_at = datetime.utcnow()\n                    processed.append(str(payslip.id))\n                except Exception as e:\n                    payslip.status = "failed"\n                    payslip.failure_reason = str(e)\n                    failed.append({"payslip_id": str(payslip.id), "error": str(e)})\n            \n            # Update run status\n            run.status = PayrollRunStatus.PROCESSED\n            run.processed_at = datetime.utcnow()\n            run.processed_by = context.user_id\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data={\n                    "run": run.to_dict(),\n                    "processed_count": len(processed),\n                    "failed_count": len(failed),\n                    "failed": failed,\n                },\n                message=f"Payroll run \'{run.run_number}\' processed successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error processing payroll run: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to process payroll run: {str(e)}",\n                code="PROCESS_PAYROLL_RUN_ERROR",\n            )\n    \n    async def get_payslip(\n        self,\n        context: ServiceContext,\n        payslip_id: UUID,\n    ) -> ServiceResult[PayslipResponse]:\n        """Get a specific payslip with all details."""\n        try:\n            payslip = await self.payroll_repo.get_payslip_by_id(payslip_id)\n            if not payslip:\n                return ServiceResult.failure(\n                    error="Payslip not found",\n                    code="PAYSLIP_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, payslip.company_id)\n            \n            lines = await self.payroll_repo.get_payslip_lines(payslip_id)\n            \n            return ServiceResult.success(\n                data=PayslipResponse(\n                    id=payslip.id,\n                    employee_id=payslip.employee_id,\n                    employee_name=f"{payslip.employee.first_name} {payslip.employee.last_name}",\n                    employee_number=payslip.employee.employee_number,\n                    pay_period_start=payslip.pay_period_start,\n                    pay_period_end=payslip.pay_period_end,\n                    pay_date=payslip.pay_date,\n                    gross_pay=payslip.gross_pay,\n                    total_deductions=payslip.total_deductions,\n                    net_pay=payslip.net_pay,\n                    currency_code=payslip.currency_code,\n                    lines=[\n                        {\n                            "component_name": l.component_name,\n                            "component_type": l.component_type,\n                            "amount": l.amount,\n                            "is_taxable": l.is_taxable,\n                        }\n                        for l in lines\n                    ],\n                    status=payslip.status,\n                    created_at=payslip.created_at,\n                )\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error getting payslip: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get payslip: {str(e)}",\n                code="GET_PAYSLIP_ERROR",\n            )\n    \n    # ==================== Attendance Management ====================\n    \n    @transactional\n    @emit_event("attendance.recorded")\n    @requires_permission("attendance:record")\n    async def record_attendance(\n        self,\n        context: ServiceContext,\n        data: AttendanceRecordCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Record employee attendance (check-in/check-out)."""\n        try:\n            employee = await self.employee_repo.get_by_id(data.employee_id)\n            if not employee:\n                return ServiceResult.failure(\n                    error="Employee not found",\n                    code="EMPLOYEE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, employee.company_id)\n            \n            # Check for existing record today\n            existing = await self.attendance_repo.get_by_employee_date(\n                employee_id=data.employee_id,\n                date=data.record_date or date.today(),\n            )\n            \n            if data.record_type == "check_in":\n                if existing and existing.check_in_time:\n                    return ServiceResult.failure(\n                        error="Already checked in for today",\n                        code="ALREADY_CHECKED_IN",\n                    )\n                \n                if existing:\n                    existing.check_in_time = data.record_time or datetime.utcnow()\n                    existing.status = AttendanceStatus.PRESENT\n                    existing.check_in_method = data.method\n                    existing.ip_address = data.ip_address\n                    existing.location = data.location\n                    record = existing\n                else:\n                    record = await self.attendance_repo.create({\n                        "company_id": employee.company_id,\n                        "employee_id": data.employee_id,\n                        "record_date": data.record_date or date.today(),\n                        "check_in_time": data.record_time or datetime.utcnow(),\n                        "status": AttendanceStatus.PRESENT,\n                        "check_in_method": data.method,\n                        "ip_address": data.ip_address,\n                        "location": data.location,\n                        "created_by": context.user_id,\n                    })\n                \n            else:  # check_out\n                if not existing:\n                    return ServiceResult.failure(\n                        error="No check-in record found for today",\n                        code="NO_CHECK_IN",\n                    )\n                \n                if existing.check_out_time:\n                    return ServiceResult.failure(\n                        error="Already checked out for today",\n                        code="ALREADY_CHECKED_OUT",\n                    )\n                \n                existing.check_out_time = data.record_time or datetime.utcnow()\n                existing.check_out_method = data.method\n                existing.hours_worked = self._calculate_hours_worked(\n                    existing.check_in_time,\n                    existing.check_out_time,\n                )\n                record = existing\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=record.to_dict(),\n                message=f"Attendance recorded successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error recording attendance: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to record attendance: {str(e)}",\n                code="RECORD_ATTENDANCE_ERROR",\n            )\n    \n    async def get_attendance_report(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        start_date: date,\n        end_date: date,\n        department_id: Optional[UUID] = None,\n        employee_id: Optional[UUID] = None,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Get attendance report for a period."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            records = await self.attendance_repo.get_by_period(\n                company_id=company_id,\n                start_date=start_date,\n                end_date=end_date,\n                department_id=department_id,\n                employee_id=employee_id,\n            )\n            \n            # Calculate statistics\n            stats = {\n                "total_days": (end_date - start_date).days + 1,\n                "total_records": len(records),\n                "present_days": sum(1 for r in records if r.status == AttendanceStatus.PRESENT),\n                "absent_days": sum(1 for r in records if r.status == AttendanceStatus.ABSENT),\n                "late_days": sum(1 for r in records if r.status == AttendanceStatus.LATE),\n                "leave_days": sum(1 for r in records if r.status == AttendanceStatus.ON_LEAVE),\n                "total_hours_worked": sum(r.hours_worked or Decimal("0") for r in records),\n                "average_hours": Decimal("0"),\n            }\n            \n            if stats["present_days"] > 0:\n                stats["average_hours"] = stats["total_hours_worked"] / stats["present_days"]\n            \n            return ServiceResult.success(data={\n                "records": [r.to_dict() for r in records],\n                "statistics": stats,\n                "period": {\n                    "start_date": start_date.isoformat(),\n                    "end_date": end_date.isoformat(),\n                },\n            })\n            \n        except Exception as e:\n            self._log_error(f"Error getting attendance report: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get attendance report: {str(e)}",\n                code="ATTENDANCE_REPORT_ERROR",\n            )\n    \n    # ==================== Leave Management ====================\n    \n    @transactional\n    @emit_event("leave_request.created")\n    @requires_permission("leave_request:create")\n    async def create_leave_request(\n        self,\n        context: ServiceContext,\n        data: LeaveRequestCreate,\n    ) -> ServiceResult[LeaveRequestResponse]:\n        """Create a leave request."""\n        try:\n            employee = await self.employee_repo.get_by_id(data.employee_id)\n            if not employee:\n                return ServiceResult.failure(\n                    error="Employee not found",\n                    code="EMPLOYEE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, employee.company_id)\n            \n            # Validate leave type\n            leave_type = await self.leave_repo.get_leave_type_by_id(data.leave_type_id)\n            if not leave_type or leave_type.company_id != employee.company_id:\n                return ServiceResult.failure(\n                    error="Invalid leave type",\n                    code="INVALID_LEAVE_TYPE",\n                )\n            \n            # Check balance\n            balance = await self.leave_repo.get_balance(\n                employee_id=data.employee_id,\n                leave_type_id=data.leave_type_id,\n                year=data.start_date.year,\n            )\n            \n            requested_days = self._calculate_leave_days(\n                data.start_date,\n                data.end_date,\n                data.is_half_day,\n            )\n            \n            if balance and balance.remaining_balance < requested_days:\n                return ServiceResult.failure(\n                    error=f"Insufficient leave balance. Available: {balance.remaining_balance}, Requested: {requested_days}",\n                    code="INSUFFICIENT_BALANCE",\n                )\n            \n            # Check for overlapping requests\n            overlapping = await self.leave_repo.get_overlapping_requests(\n                employee_id=data.employee_id,\n                start_date=data.start_date,\n                end_date=data.end_date,\n            )\n            if overlapping:\n                return ServiceResult.failure(\n                    error="Overlapping leave request exists",\n                    code="OVERLAPPING_REQUEST",\n                )\n            \n            # Create request\n            request = await self.leave_repo.create_request({\n                "company_id": employee.company_id,\n                "employee_id": data.employee_id,\n                "leave_type_id": data.leave_type_id,\n                "start_date": data.start_date,\n                "end_date": data.end_date,\n                "is_half_day": data.is_half_day,\n                "days_requested": requested_days,\n                "reason": data.reason,\n                "status": LeaveRequestStatus.PENDING,\n                "contact_number": data.contact_number,\n                "address_during_leave": data.address_during_leave,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            # Notify manager\n            await self._notify_leave_request(request, employee)\n            \n            return ServiceResult.success(\n                data=LeaveRequestResponse.model_validate(request),\n                message="Leave request submitted successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating leave request: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create leave request: {str(e)}",\n                code="CREATE_LEAVE_REQUEST_ERROR",\n            )\n    \n    @transactional\n    @emit_event("leave_request.approved")\n    @requires_permission("leave_request:approve")\n    async def approve_leave_request(\n        self,\n        context: ServiceContext,\n        request_id: UUID,\n        notes: Optional[str] = None,\n    ) -> ServiceResult[LeaveRequestResponse]:\n        """Approve a leave request."""\n        try:\n            request = await self.leave_repo.get_request_by_id(request_id)\n            if not request:\n                return ServiceResult.failure(\n                    error="Leave request not found",\n                    code="LEAVE_REQUEST_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, request.company_id)\n            \n            if request.status != LeaveRequestStatus.PENDING:\n                return ServiceResult.failure(\n                    error=f"Cannot approve request with status \'{request.status}\'",\n                    code="INVALID_STATUS",\n                )\n            \n            # Update request\n            request.status = LeaveRequestStatus.APPROVED\n            request.approved_at = datetime.utcnow()\n            request.approved_by = context.user_id\n            request.approval_notes = notes\n            \n            # Update balance\n            await self._deduct_leave_balance(\n                employee_id=request.employee_id,\n                leave_type_id=request.leave_type_id,\n                days=request.days_requested,\n                year=request.start_date.year,\n            )\n            \n            # Create attendance records for leave days\n            await self._create_leave_attendance(request)\n            \n            await self._session.flush()\n            \n            # Notify employee\n            await self._notify_leave_approval(request)\n            \n            return ServiceResult.success(\n                data=LeaveRequestResponse.model_validate(request),\n                message="Leave request approved successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error approving leave request: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to approve leave request: {str(e)}",\n                code="APPROVE_LEAVE_REQUEST_ERROR",\n            )\n    \n    @transactional\n    @emit_event("leave_request.rejected")\n    @requires_permission("leave_request:approve")\n    async def reject_leave_request(\n        self,\n        context: ServiceContext,\n        request_id: UUID,\n        reason: str,\n    ) -> ServiceResult[LeaveRequestResponse]:\n        """Reject a leave request."""\n        try:\n            request = await self.leave_repo.get_request_by_id(request_id)\n            if not request:\n                return ServiceResult.failure(\n                    error="Leave request not found",\n                    code="LEAVE_REQUEST_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, request.company_id)\n            \n            if request.status != LeaveRequestStatus.PENDING:\n                return ServiceResult.failure(\n                    error=f"Cannot reject request with status \'{request.status}\'",\n                    code="INVALID_STATUS",\n                )\n            \n            request.status = LeaveRequestStatus.REJECTED\n            request.rejected_at = datetime.utcnow()\n            request.rejected_by = context.user_id\n            request.rejection_reason = reason\n            \n            await self._session.flush()\n            \n            # Notify employee\n            await self._notify_leave_rejection(request)\n            \n            return ServiceResult.success(\n                data=LeaveRequestResponse.model_validate(request),\n                message="Leave request rejected",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error rejecting leave request: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to reject leave request: {str(e)}",\n                code="REJECT_LEAVE_REQUEST_ERROR",\n            )\n    \n    async def get_leave_balances(\n        self,\n        context: ServiceContext,\n        employee_id: UUID,\n        year: Optional[int] = None,\n    ) -> ServiceResult[List[LeaveBalanceResponse]]:\n        """Get leave balances for an employee."""\n        try:\n            employee = await self.employee_repo.get_by_id(employee_id)\n            if not employee:\n                return ServiceResult.failure(\n                    error="Employee not found",\n                    code="EMPLOYEE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, employee.company_id)\n            \n            year = year or date.today().year\n            balances = await self.leave_repo.get_all_balances(\n                employee_id=employee_id,\n                year=year,\n            )\n            \n            return ServiceResult.success(\n                data=[LeaveBalanceResponse.model_validate(b) for b in balances]\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error getting leave balances: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get leave balances: {str(e)}",\n                code="GET_LEAVE_BALANCES_ERROR",\n            )\n    \n    # ==================== Time Tracking ====================\n    \n    @transactional\n    @emit_event("time_entry.created")\n    @requires_permission("time_entry:create")\n    async def create_time_entry(\n        self,\n        context: ServiceContext,\n        data: TimeEntryCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a time entry."""\n        try:\n            employee = await self.employee_repo.get_by_id(data.employee_id)\n            if not employee:\n                return ServiceResult.failure(\n                    error="Employee not found",\n                    code="EMPLOYEE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, employee.company_id)\n            \n            # Calculate duration\n            duration = Decimal("0")\n            if data.start_time and data.end_time:\n                diff = data.end_time - data.start_time\n                duration = Decimal(str(diff.total_seconds() / 3600))\n            \n            entry = await self.time_entry_repo.create({\n                "company_id": employee.company_id,\n                "employee_id": data.employee_id,\n                "project_id": data.project_id,\n                "task_id": data.task_id,\n                "entry_date": data.entry_date or date.today(),\n                "start_time": data.start_time,\n                "end_time": data.end_time,\n                "duration_hours": data.duration_hours or duration,\n                "description": data.description,\n                "is_billable": data.is_billable,\n                "hourly_rate": data.hourly_rate,\n                "status": "draft",\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=entry.to_dict(),\n                message="Time entry created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating time entry: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create time entry: {str(e)}",\n                code="CREATE_TIME_ENTRY_ERROR",\n            )\n    \n    @transactional\n    @emit_event("timesheet.submitted")\n    @requires_permission("timesheet:submit")\n    async def submit_timesheet(\n        self,\n        context: ServiceContext,\n        employee_id: UUID,\n        period_start: date,\n        period_end: date,\n    ) -> ServiceResult[TimesheetResponse]:\n        """Submit a timesheet for approval."""\n        try:\n            employee = await self.employee_repo.get_by_id(employee_id)\n            if not employee:\n                return ServiceResult.failure(\n                    error="Employee not found",\n                    code="EMPLOYEE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, employee.company_id)\n            \n            # Get time entries for the period\n            entries = await self.time_entry_repo.get_by_period(\n                employee_id=employee_id,\n                start_date=period_start,\n                end_date=period_end,\n            )\n            \n            if not entries:\n                return ServiceResult.failure(\n                    error="No time entries found for the period",\n                    code="NO_ENTRIES",\n                )\n            \n            # Calculate totals\n            total_hours = sum(e.duration_hours or Decimal("0") for e in entries)\n            total_amount = sum(\n                (e.duration_hours or Decimal("0")) * (e.hourly_rate or Decimal("0"))\n                for e in entries if e.is_billable\n            )\n            \n            # Create timesheet\n            timesheet = await self.time_entry_repo.create_timesheet({\n                "company_id": employee.company_id,\n                "employee_id": employee_id,\n                "period_start": period_start,\n                "period_end": period_end,\n                "total_hours": total_hours,\n                "total_amount": total_amount,\n                "status": TimesheetStatus.SUBMITTED,\n                "submitted_at": datetime.utcnow(),\n                "submitted_by": context.user_id,\n            })\n            \n            # Link entries to timesheet\n            for entry in entries:\n                entry.timesheet_id = timesheet.id\n                entry.status = "submitted"\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=TimesheetResponse.model_validate(timesheet),\n                message="Timesheet submitted successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error submitting timesheet: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to submit timesheet: {str(e)}",\n                code="SUBMIT_TIMESHEET_ERROR",\n            )\n    \n    # ==================== Performance Management ====================\n    \n    @transactional\n    @emit_event("performance_review.created")\n    @requires_permission("performance_review:create")\n    async def create_performance_review(\n        self,\n        context: ServiceContext,\n        data: PerformanceReviewCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a performance review."""\n        try:\n            employee = await self.employee_repo.get_by_id(data.employee_id)\n            if not employee:\n                return ServiceResult.failure(\n                    error="Employee not found",\n                    code="EMPLOYEE_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, employee.company_id)\n            \n            # Validate reviewer\n            if data.reviewer_id:\n                reviewer = await self.employee_repo.get_by_id(data.reviewer_id)\n                if not reviewer or reviewer.company_id != employee.company_id:\n                    return ServiceResult.failure(\n                        error="Invalid reviewer",\n                        code="INVALID_REVIEWER",\n                    )\n            \n            review = await self.performance_repo.create({\n                "company_id": employee.company_id,\n                "employee_id": data.employee_id,\n                "reviewer_id": data.reviewer_id,\n                "review_period_start": data.review_period_start,\n                "review_period_end": data.review_period_end,\n                "review_type": data.review_type,\n                "status": PerformanceReviewStatus.DRAFT,\n                "goals": data.goals,\n                "competencies": data.competencies,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=review.to_dict(),\n                message="Performance review created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating performance review: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create performance review: {str(e)}",\n                code="CREATE_PERFORMANCE_REVIEW_ERROR",\n            )\n    \n    # ==================== Helper Methods ====================\n    \n    async def _generate_employee_number(self, company_id: UUID) -> str:\n        """Generate a unique employee number."""\n        count = await self.employee_repo.count_by_company(company_id)\n        year = date.today().year\n        return f"EMP-{year}-{str(count + 1).zfill(5)}"\n    \n    async def _initialize_leave_balances(self, employee: Employee) -> None:\n        """Initialize leave balances for a new employee."""\n        leave_types = await self.leave_repo.get_active_leave_types(employee.company_id)\n        year = date.today().year\n        \n        for leave_type in leave_types:\n            # Calculate pro-rated entitlement based on hire date\n            entitlement = leave_type.annual_entitlement\n            if leave_type.prorate_new_employees:\n                days_remaining = (date(year, 12, 31) - employee.hire_date).days + 1\n                entitlement = entitlement * Decimal(days_remaining) / Decimal(365)\n            \n            await self.leave_repo.create_balance({\n                "company_id": employee.company_id,\n                "employee_id": employee.id,\n                "leave_type_id": leave_type.id,\n                "year": year,\n                "entitled_days": entitlement,\n                "used_days": Decimal("0"),\n                "remaining_balance": entitlement,\n                "carried_forward": Decimal("0"),\n            })\n    \n    async def _create_onboarding_tasks(self, employee: Employee) -> None:\n        """Create onboarding tasks for a new employee."""\n        # Default onboarding tasks\n        default_tasks = [\n            {"task": "Complete new hire paperwork", "due_days": 1},\n            {"task": "Setup workstation and equipment", "due_days": 1},\n            {"task": "Email and system access setup", "due_days": 1},\n            {"task": "HR orientation meeting", "due_days": 3},\n            {"task": "Department introduction", "due_days": 5},\n            {"task": "Benefits enrollment", "due_days": 14},\n        ]\n        \n        for task in default_tasks:\n            due_date = employee.hire_date + timedelta(days=task["due_days"])\n            await self.employee_repo.create_onboarding_task({\n                "company_id": employee.company_id,\n                "employee_id": employee.id,\n                "task": task["task"],\n                "due_date": due_date,\n                "status": "pending",\n            })\n    \n    async def _create_offboarding_tasks(self, employee: Employee) -> None:\n        """Create offboarding tasks for a terminating employee."""\n        default_tasks = [\n            {"task": "Exit interview", "priority": "high"},\n            {"task": "Return company equipment", "priority": "high"},\n            {"task": "Revoke system access", "priority": "high"},\n            {"task": "Final paycheck calculation", "priority": "high"},\n            {"task": "Benefits termination", "priority": "medium"},\n            {"task": "Knowledge transfer", "priority": "medium"},\n        ]\n        \n        for task in default_tasks:\n            await self.employee_repo.create_offboarding_task({\n                "company_id": employee.company_id,\n                "employee_id": employee.id,\n                "task": task["task"],\n                "priority": task["priority"],\n                "status": "pending",\n            })\n    \n    async def _handle_status_change(\n        self,\n        employee: Employee,\n        new_status: EmployeeStatus,\n    ) -> None:\n        """Handle employee status changes."""\n        if new_status == EmployeeStatus.TERMINATED:\n            # Deactivate system access, etc.\n            pass\n        elif new_status == EmployeeStatus.ACTIVE:\n            # Reactivate if was suspended\n            pass\n    \n    async def _log_employee_changes(\n        self,\n        employee: Employee,\n        changes: Dict[str, Any],\n    ) -> None:\n        """Log employee changes for audit."""\n        # Would create audit log entry\n        pass\n    \n    async def _calculate_final_pay(\n        self,\n        employee: Employee,\n        termination_date: date,\n    ) -> Dict[str, Decimal]:\n        """Calculate final pay for terminated employee."""\n        # Would calculate pro-rated salary, unused leave, etc.\n        return {\n            "salary": Decimal("0"),\n            "unused_leave": Decimal("0"),\n            "deductions": Decimal("0"),\n            "net_pay": Decimal("0"),\n        }\n    \n    def _build_org_tree(\n        self,\n        employees: List[Employee],\n        manager_id: Optional[UUID] = None,\n    ) -> List[Dict[str, Any]]:\n        """Build organizational tree from employees."""\n        tree = []\n        \n        for emp in employees:\n            if emp.manager_id == manager_id:\n                node = {\n                    "id": str(emp.id),\n                    "employee_number": emp.employee_number,\n                    "name": f"{emp.first_name} {emp.last_name}",\n                    "position": emp.position.title if emp.position else None,\n                    "department": emp.department.name if emp.department else None,\n                    "email": emp.work_email or emp.email,\n                    "status": emp.status.value,\n                    "subordinates": self._build_org_tree(employees, emp.id),\n                }\n                tree.append(node)\n        \n        return tree\n    \n    async def _generate_payroll_run_number(self, company_id: UUID) -> str:\n        """Generate a unique payroll run number."""\n        count = await self.payroll_repo.count_by_company(company_id)\n        year = date.today().year\n        return f"PAY-{year}-{str(count + 1).zfill(4)}"\n    \n    async def _create_payslip(\n        self,\n        payroll_run: PayrollRun,\n        employee: Employee,\n        period_start: date,\n        period_end: date,\n    ) -> Payslip:\n        """Create a payslip for an employee."""\n        # Get salary structure\n        salary = employee.base_salary or Decimal("0")\n        \n        # Calculate components\n        gross = salary  # Would add allowances, overtime, etc.\n        deductions = Decimal("0")  # Would calculate tax, etc.\n        net = gross - deductions\n        \n        payslip = await self.payroll_repo.create_payslip({\n            "company_id": payroll_run.company_id,\n            "payroll_run_id": payroll_run.id,\n            "employee_id": employee.id,\n            "pay_period_start": period_start,\n            "pay_period_end": period_end,\n            "pay_date": payroll_run.pay_date,\n            "currency_code": payroll_run.currency_code,\n            "gross_pay": gross,\n            "total_deductions": deductions,\n            "net_pay": net,\n            "status": "draft",\n        })\n        \n        # Create payslip lines\n        await self.payroll_repo.create_payslip_line({\n            "payslip_id": payslip.id,\n            "component_name": "Basic Salary",\n            "component_type": "earning",\n            "amount": salary,\n            "is_taxable": True,\n        })\n        \n        return payslip\n    \n    async def _create_salary_payment(self, payslip: Payslip) -> Dict[str, Any]:\n        """Create salary payment transaction."""\n        # Would create bank transaction or payment record\n        return {"id": str(UUID(int=0))}\n    \n    def _calculate_hours_worked(\n        self,\n        check_in: datetime,\n        check_out: datetime,\n    ) -> Decimal:\n        """Calculate hours worked between check-in and check-out."""\n        diff = check_out - check_in\n        hours = diff.total_seconds() / 3600\n        # Subtract break time if applicable\n        return Decimal(str(round(hours, 2)))\n    \n    def _calculate_leave_days(\n        self,\n        start_date: date,\n        end_date: date,\n        is_half_day: bool = False,\n    ) -> Decimal:\n        """Calculate leave days between dates."""\n        days = (end_date - start_date).days + 1\n        if is_half_day:\n            return Decimal(str(days)) * Decimal("0.5")\n        return Decimal(str(days))\n    \n    async def _deduct_leave_balance(\n        self,\n        employee_id: UUID,\n        leave_type_id: UUID,\n        days: Decimal,\n        year: int,\n    ) -> None:\n        """Deduct from leave balance."""\n        balance = await self.leave_repo.get_balance(\n            employee_id=employee_id,\n            leave_type_id=leave_type_id,\n            year=year,\n        )\n        \n        if balance:\n            balance.used_days = (balance.used_days or Decimal("0")) + days\n            balance.remaining_balance = (balance.remaining_balance or Decimal("0")) - days\n    \n    async def _create_leave_attendance(self, request: LeaveRequest) -> None:\n        """Create attendance records for approved leave."""\n        current_date = request.start_date\n        while current_date <= request.end_date:\n            await self.attendance_repo.create({\n                "company_id": request.company_id,\n                "employee_id": request.employee_id,\n                "record_date": current_date,\n                "status": AttendanceStatus.ON_LEAVE,\n                "leave_request_id": request.id,\n            })\n            current_date += timedelta(days=1)\n    \n    async def _notify_leave_request(self, request: LeaveRequest, employee: Employee) -> None:\n        """Notify manager of leave request."""\n        # Would send notification\n        pass\n    \n    async def _notify_leave_approval(self, request: LeaveRequest) -> None:\n        """Notify employee of leave approval."""\n        # Would send notification\n        pass\n    \n    async def _notify_leave_rejection(self, request: LeaveRequest) -> None:\n        """Notify employee of leave rejection."""\n        # Would send notification\n        pass\n</create-file>\n</function_calls>'}
{'role': 'user', 'content': '<tool_result> <create-file> ToolResult(success=True, output="File \'backend/app/services/hr.py\' created successfully.") </create-file> </tool_result>'}
{'role': 'assistant', 'content': 'Now let me create the Tax service for tax calculations, filings, and nexus management.\n\n<function_calls>\n<create-file file_path="backend/app/services/tax.py">"""\nTax Service - Tax Engine and Compliance Business Logic\nSAMMS ENTERPRISE SOVEREIGN OPERATING SYSTEM (SESOS v6 OMEGA CONSTRAINED)\n\nThis service implements comprehensive tax management operations\nincluding tax calculations, nexus management, filings, and compliance.\n"""\n\nfrom typing import Optional, List, Dict, Any, Tuple, TYPE_CHECKING\nfrom uuid import UUID\nfrom decimal import Decimal\nfrom datetime import date, datetime, timedelta\nfrom enum import Enum\nimport asyncio\nimport json\n\nfrom sqlalchemy import select, and_, or_, func, case, literal_column\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom sqlalchemy.orm import selectinload, joinedload\n\nfrom .base import (\n    BaseService,\n    CompanyScopedService,\n    ServiceResult,\n    ServiceContext,\n    transactional,\n    cached,\n    emit_event,\n    requires_permission,\n    CacheManager,\n    EventPublisher,\n)\nfrom ..models.tax import (\n    TaxAuthority,\n    TaxType,\n    TaxRate,\n    TaxRegistration,\n    TaxTransaction,\n    TaxFiling,\n    TaxFilingStatus,\n    TaxPayment,\n    TaxReconciliation,\n    TaxNexus,\n    NexusType,\n    EconomicNexusThreshold,\n    TaxExemption,\n    TaxExemptionCertificate,\n    TaxReport,\n    WithholdingTaxRate,\n    TransferPricingRule,\n    TaxAuditLog,\n)\nfrom ..schemas.tax import (\n    TaxAuthorityCreate,\n    TaxRateCreate,\n    TaxRegistrationCreate,\n    TaxFilingCreate,\n    TaxPaymentCreate,\n    TaxNexusCreate,\n    TaxExemptionCreate,\n    TaxCalculationRequest,\n    TaxCalculationResult,\n    TaxLiabilityReport,\n    NexusAnalysisResult,\n)\nfrom ..repositories.tax import (\n    TaxAuthorityRepository,\n    TaxRateRepository,\n    TaxRegistrationRepository,\n    TaxTransactionRepository,\n    TaxFilingRepository,\n    TaxNexusRepository,\n    TaxExemptionRepository,\n)\nfrom ..core.exceptions import (\n    ValidationError,\n    NotFoundError,\n    BusinessLogicError,\n    PermissionDeniedError,\n)\n\nif TYPE_CHECKING:\n    from ..repositories.tax import TaxRateRepository\n\n\nclass TaxService(CompanyScopedService):\n    """\n    Service for managing tax operations and compliance.\n    \n    Implements:\n    - Tax authority and rate management\n    - Multi-jurisdictional tax calculations\n    - Tax nexus tracking and analysis\n    - Tax registration management\n    - Tax filing and remittance\n    - Tax exemption handling\n    - Withholding tax\n    - Transfer pricing\n    - Tax reconciliation\n    - Tax reporting\n    """\n    \n    def __init__(\n        self,\n        session: AsyncSession,\n        cache: Optional[CacheManager] = None,\n        event_publisher: Optional[EventPublisher] = None,\n    ):\n        super().__init__(session, cache, event_publisher)\n        self.authority_repo = TaxAuthorityRepository(session)\n        self.rate_repo = TaxRateRepository(session)\n        self.registration_repo = TaxRegistrationRepository(session)\n        self.transaction_repo = TaxTransactionRepository(session)\n        self.filing_repo = TaxFilingRepository(session)\n        self.nexus_repo = TaxNexusRepository(session)\n        self.exemption_repo = TaxExemptionRepository(session)\n    \n    # ==================== Tax Authority Management ====================\n    \n    @transactional\n    @emit_event("tax_authority.created")\n    @requires_permission("tax_authority:create")\n    async def create_tax_authority(\n        self,\n        context: ServiceContext,\n        data: TaxAuthorityCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a new tax authority."""\n        try:\n            authority = await self.authority_repo.create({\n                "name": data.name,\n                "code": data.code,\n                "country_code": data.country_code,\n                "state_code": data.state_code,\n                "jurisdiction_type": data.jurisdiction_type,\n                "description": data.description,\n                "website": data.website,\n                "contact_email": data.contact_email,\n                "contact_phone": data.contact_phone,\n                "address": data.address,\n                "filing_frequency": data.filing_frequency,\n                "filing_due_day": data.filing_due_day,\n                "payment_frequency": data.payment_frequency,\n                "payment_due_day": data.payment_due_day,\n                "requires_electronic_filing": data.requires_electronic_filing,\n                "e_filing_portal": data.e_filing_portal,\n                "custom_fields": data.custom_fields,\n                "is_active": True,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=authority.to_dict(),\n                message=f"Tax authority \'{authority.name}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating tax authority: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create tax authority: {str(e)}",\n                code="CREATE_TAX_AUTHORITY_ERROR",\n            )\n    \n    # ==================== Tax Rate Management ====================\n    \n    @transactional\n    @emit_event("tax_rate.created")\n    @requires_permission("tax_rate:create")\n    async def create_tax_rate(\n        self,\n        context: ServiceContext,\n        data: TaxRateCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """\n        Create a new tax rate.\n        \n        Supports:\n        - Simple rates\n        - Compound rates\n        - Tiered rates\n        - Conditional rates\n        """\n        try:\n            # Validate authority exists\n            authority = await self.authority_repo.get_by_id(data.tax_authority_id)\n            if not authority:\n                return ServiceResult.failure(\n                    error="Tax authority not found",\n                    code="AUTHORITY_NOT_FOUND",\n                )\n            \n            # Validate rate\n            if data.rate < Decimal("0") or data.rate > Decimal("100"):\n                return ServiceResult.failure(\n                    error="Tax rate must be between 0 and 100",\n                    code="INVALID_RATE",\n                )\n            \n            # Check for overlapping rates\n            if data.effective_from:\n                overlapping = await self.rate_repo.get_overlapping_rate(\n                    tax_type=data.tax_type,\n                    jurisdiction=data.jurisdiction,\n                    effective_from=data.effective_from,\n                    effective_to=data.effective_to,\n                )\n                if overlapping:\n                    return ServiceResult.failure(\n                        error="Overlapping tax rate exists for this jurisdiction",\n                        code="OVERLAPPING_RATE",\n                    )\n            \n            tax_rate = await self.rate_repo.create({\n                "tax_authority_id": data.tax_authority_id,\n                "tax_type": data.tax_type,\n                "name": data.name,\n                "code": data.code,\n                "rate": data.rate,\n                "rate_type": data.rate_type or "percentage",\n                "jurisdiction": data.jurisdiction,\n                "country_code": data.country_code,\n                "state_code": data.state_code,\n                "county": data.county,\n                "city": data.city,\n                "postal_code_from": data.postal_code_from,\n                "postal_code_to": data.postal_code_to,\n                "effective_from": data.effective_from or date.today(),\n                "effective_to": data.effective_to,\n                "is_compound": data.is_compound,\n                "compound_base_rate_id": data.compound_base_rate_id,\n                "min_taxable_amount": data.min_taxable_amount,\n                "max_taxable_amount": data.max_taxable_amount,\n                "exempt_amount_threshold": data.exempt_amount_threshold,\n                "applicable_to": data.applicable_to,\n                "exceptions": data.exceptions,\n                "custom_fields": data.custom_fields,\n                "is_active": True,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=tax_rate.to_dict(),\n                message=f"Tax rate \'{tax_rate.name}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating tax rate: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create tax rate: {str(e)}",\n                code="CREATE_TAX_RATE_ERROR",\n            )\n    \n    @cached(ttl=3600, key_prefix="applicable_rates")\n    async def get_applicable_rates(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        jurisdiction: Dict[str, Any],\n        transaction_date: date,\n        tax_types: Optional[List[TaxType]] = None,\n    ) -> ServiceResult[List[Dict[str, Any]]]:\n        """\n        Get all applicable tax rates for a jurisdiction and date.\n        \n        Considers:\n        - Geographic location\n        - Transaction type\n        - Product/service category\n        - Effective dates\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            rates = await self.rate_repo.get_applicable_rates(\n                country_code=jurisdiction.get("country_code"),\n                state_code=jurisdiction.get("state_code"),\n                county=jurisdiction.get("county"),\n                city=jurisdiction.get("city"),\n                postal_code=jurisdiction.get("postal_code"),\n                transaction_date=transaction_date,\n                tax_types=tax_types,\n            )\n            \n            # Check for exemptions\n            company_exemptions = await self.exemption_repo.get_active_by_company(company_id)\n            \n            applicable_rates = []\n            for rate in rates:\n                # Check if exempt\n                is_exempt = self._check_exemption(\n                    rate=rate,\n                    exemptions=company_exemptions,\n                    product_category=jurisdiction.get("product_category"),\n                )\n                \n                if not is_exempt:\n                    applicable_rates.append({\n                        "rate_id": str(rate.id),\n                        "name": rate.name,\n                        "code": rate.code,\n                        "tax_type": rate.tax_type.value,\n                        "rate": rate.rate,\n                        "jurisdiction": rate.jurisdiction,\n                        "authority_id": str(rate.tax_authority_id),\n                        "is_compound": rate.is_compound,\n                    })\n            \n            return ServiceResult.success(data=applicable_rates)\n            \n        except Exception as e:\n            self._log_error(f"Error getting applicable rates: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get applicable rates: {str(e)}",\n                code="GET_RATES_ERROR",\n            )\n    \n    # ==================== Tax Calculation Engine ====================\n    \n    async def calculate_taxes(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: TaxCalculationRequest,\n    ) -> ServiceResult[TaxCalculationResult]:\n        """\n        Calculate taxes for a transaction.\n        \n        Implements:\n        - Multi-jurisdictional tax calculation\n        - Compound tax handling\n        - Exemption application\n        - Tax-inclusive vs tax-exclusive pricing\n        - Currency conversion for multi-currency\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Get applicable rates\n            rates_result = await self.get_applicable_rates(\n                context=context,\n                company_id=company_id,\n                jurisdiction={\n                    "country_code": data.ship_to_country,\n                    "state_code": data.ship_to_state,\n                    "county": data.ship_to_county,\n                    "city": data.ship_to_city,\n                    "postal_code": data.ship_to_postal_code,\n                    "product_category": data.product_category,\n                },\n                transaction_date=data.transaction_date,\n                tax_types=data.tax_types,\n            )\n            \n            if not rates_result.is_success:\n                return rates_result\n            \n            rates = rates_result.data\n            \n            # Check nexus\n            nexus_check = await self._check_nexus(\n                company_id=company_id,\n                jurisdiction={\n                    "country_code": data.ship_to_country,\n                    "state_code": data.ship_to_state,\n                },\n            )\n            \n            if not nexus_check:\n                # No nexus - no tax required\n                return ServiceResult.success(\n                    data=TaxCalculationResult(\n                        company_id=company_id,\n                        transaction_id=data.transaction_id,\n                        subtotal=data.amount,\n                        total_tax=Decimal("0"),\n                        total=data.amount,\n                        tax_details=[],\n                        tax_jurisdictions=[],\n                        exemption_applied=False,\n                        no_nexus=True,\n                    )\n                )\n            \n            # Check for exemptions\n            exemptions = await self.exemption_repo.get_active_by_company(company_id)\n            customer_exemptions = []\n            \n            if data.customer_id:\n                customer_exemptions = await self.exemption_repo.get_by_customer(\n                    customer_id=data.customer_id,\n                    as_of_date=data.transaction_date,\n                )\n            \n            # Calculate taxes\n            tax_details = []\n            total_tax = Decimal("0")\n            base_amount = data.amount\n            \n            # Sort rates (compound rates last)\n            sorted_rates = sorted(rates, key=lambda r: r.get("is_compound", False))\n            \n            for rate in sorted_rates:\n                # Determine taxable amount\n                if rate.get("is_compound"):\n                    taxable_amount = base_amount + total_tax\n                else:\n                    taxable_amount = base_amount\n                \n                # Check for exemption\n                exemption = self._find_applicable_exemption(\n                    rate=rate,\n                    exemptions=exemptions + customer_exemptions,\n                    product_category=data.product_category,\n                    customer_id=data.customer_id,\n                )\n                \n                if exemption:\n                    # Apply exemption\n                    if exemption.exemption_type == "full":\n                        tax_amount = Decimal("0")\n                        exemption_id = str(exemption.id)\n                    elif exemption.exemption_type == "partial":\n                        exemption_rate = exemption.exemption_rate or Decimal("0")\n                        effective_rate = rate["rate"] - exemption_rate\n                        tax_amount = taxable_amount * (effective_rate / Decimal("100"))\n                        exemption_id = str(exemption.id)\n                    else:\n                        tax_amount = taxable_amount * (rate["rate"] / Decimal("100"))\n                        exemption_id = None\n                else:\n                    tax_amount = taxable_amount * (rate["rate"] / Decimal("100"))\n                    exemption_id = None\n                \n                total_tax += tax_amount\n                \n                tax_details.append({\n                    "rate_id": rate["rate_id"],\n                    "tax_name": rate["name"],\n                    "tax_code": rate["code"],\n                    "tax_type": rate["tax_type"],\n                    "rate": rate["rate"],\n                    "taxable_amount": taxable_amount,\n                    "tax_amount": tax_amount,\n                    "jurisdiction": rate["jurisdiction"],\n                    "authority_id": rate["authority_id"],\n                    "exemption_id": exemption_id,\n                })\n            \n            # Handle tax-inclusive pricing\n            if data.tax_inclusive:\n                # Back-calculate tax from inclusive amount\n                total_tax_ratio = sum(r["rate"] for r in sorted_rates) / Decimal("100")\n                subtotal = data.amount / (Decimal("1") + total_tax_ratio)\n                total_tax = data.amount - subtotal\n                base_amount = subtotal\n            else:\n                subtotal = data.amount\n            \n            # Create tax transaction record\n            if data.create_transaction:\n                await self._create_tax_transaction(\n                    company_id=company_id,\n                    transaction_id=data.transaction_id,\n                    tax_details=tax_details,\n                    subtotal=subtotal,\n                    total_tax=total_tax,\n                    context=context,\n                )\n            \n            result = TaxCalculationResult(\n                company_id=company_id,\n                transaction_id=data.transaction_id,\n                subtotal=subtotal,\n                total_tax=total_tax,\n                total=subtotal + total_tax,\n                tax_details=tax_details,\n                tax_jurisdictions=list(set(r["jurisdiction"] for r in tax_details)),\n                exemption_applied=any(d.get("exemption_id") for d in tax_details),\n                no_nexus=False,\n            )\n            \n            return ServiceResult.success(data=result)\n            \n        except Exception as e:\n            self._log_error(f"Error calculating taxes: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to calculate taxes: {str(e)}",\n                code="TAX_CALCULATION_ERROR",\n            )\n    \n    async def calculate_withholding_tax(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        payment_amount: Decimal,\n        payment_type: str,\n        recipient_type: str,\n        recipient_country: Optional[str] = None,\n        tax_treaty_country: Optional[str] = None,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Calculate withholding tax for payments."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Get applicable withholding rates\n            wh_rates = await self.rate_repo.get_withholding_rates(\n                payment_type=payment_type,\n                recipient_type=recipient_type,\n                recipient_country=recipient_country,\n            )\n            \n            # Check for tax treaty benefits\n            treaty_rate = None\n            if tax_treaty_country:\n                treaty_rate = await self.rate_repo.get_treaty_rate(\n                    country=tax_treaty_country,\n                    payment_type=payment_type,\n                )\n            \n            calculations = []\n            total_withholding = Decimal("0")\n            \n            for rate in wh_rates:\n                effective_rate = treaty_rate or rate.rate\n                wh_amount = payment_amount * (effective_rate / Decimal("100"))\n                total_withholding += wh_amount\n                \n                calculations.append({\n                    "rate_id": str(rate.id),\n                    "name": rate.name,\n                    "rate": effective_rate,\n                    "amount": wh_amount,\n                    "treaty_applied": treaty_rate is not None,\n                })\n            \n            return ServiceResult.success(data={\n                "gross_amount": payment_amount,\n                "withholding_amount": total_withholding,\n                "net_amount": payment_amount - total_withholding,\n                "calculations": calculations,\n                "treaty_benefits_applied": treaty_rate is not None,\n            })\n            \n        except Exception as e:\n            self._log_error(f"Error calculating withholding tax: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to calculate withholding tax: {str(e)}",\n                code="WITHHOLDING_TAX_ERROR",\n            )\n    \n    # ==================== Tax Nexus Management ====================\n    \n    @transactional\n    @emit_event("tax_nexus.created")\n    @requires_permission("tax_nexus:create")\n    async def create_tax_nexus(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: TaxNexusCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a tax nexus record."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Check for existing nexus\n            existing = await self.nexus_repo.get_by_jurisdiction(\n                company_id=company_id,\n                country_code=data.country_code,\n                state_code=data.state_code,\n                tax_type=data.tax_type,\n            )\n            \n            if existing:\n                return ServiceResult.failure(\n                    error="Nexus already exists for this jurisdiction",\n                    code="NEXUS_EXISTS",\n                )\n            \n            nexus = await self.nexus_repo.create({\n                "company_id": company_id,\n                "tax_type": data.tax_type,\n                "nexus_type": data.nexus_type,\n                "country_code": data.country_code,\n                "state_code": data.state_code,\n                "county": data.county,\n                "city": data.city,\n                "effective_from": data.effective_from or date.today(),\n                "effective_to": data.effective_to,\n                "registration_required": data.registration_required,\n                "registration_id": data.registration_id,\n                "threshold_amount": data.threshold_amount,\n                "threshold_period": data.threshold_period,\n                "economic_nexus_tracking": data.economic_nexus_tracking,\n                "notes": data.notes,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=nexus.to_dict(),\n                message="Tax nexus created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating tax nexus: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create tax nexus: {str(e)}",\n                code="CREATE_NEXUS_ERROR",\n            )\n    \n    async def analyze_nexus_exposure(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        analysis_period_start: date,\n        analysis_period_end: date,\n    ) -> ServiceResult[NexusAnalysisResult]:\n        """\n        Analyze nexus exposure across all jurisdictions.\n        \n        Checks:\n        - Economic nexus thresholds\n        - Physical presence\n        - Affiliate nexus\n        - Marketplace facilitator\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Get current nexus records\n            current_nexus = await self.nexus_repo.get_by_company(company_id)\n            \n            # Get transaction data by jurisdiction\n            transactions_by_jurisdiction = await self.transaction_repo.get_by_jurisdiction(\n                company_id=company_id,\n                start_date=analysis_period_start,\n                end_date=analysis_period_end,\n            )\n            \n            # Analyze each jurisdiction\n            jurisdictions_analyzed = []\n            new_nexus_required = []\n            potential_exposure = []\n            \n            for jurisdiction, data in transactions_by_jurisdiction.items():\n                # Check against thresholds\n                threshold_check = await self._check_economic_threshold(\n                    company_id=company_id,\n                    jurisdiction=jurisdiction,\n                    sales_amount=data.get("total_sales", Decimal("0")),\n                    transaction_count=data.get("transaction_count", 0),\n                    period_start=analysis_period_start,\n                    period_end=analysis_period_end,\n                )\n                \n                # Check if already has nexus\n                has_nexus = any(\n                    n.state_code == jurisdiction.get("state_code")\n                    and n.country_code == jurisdiction.get("country_code")\n                    for n in current_nexus\n                )\n                \n                jurisdictions_analyzed.append({\n                    "jurisdiction": jurisdiction,\n                    "total_sales": data.get("total_sales"),\n                    "transaction_count": data.get("transaction_count"),\n                    "threshold_met": threshold_check.get("threshold_met"),\n                    "threshold_percentage": threshold_check.get("threshold_percentage"),\n                    "has_nexus": has_nexus,\n                    "nexus_required": threshold_check.get("threshold_met") and not has_nexus,\n                })\n                \n                if threshold_check.get("threshold_met") and not has_nexus:\n                    new_nexus_required.append({\n                        "jurisdiction": jurisdiction,\n                        "threshold_type": threshold_check.get("threshold_type"),\n                        "sales_amount": data.get("total_sales"),\n                        "threshold_amount": threshold_check.get("threshold_amount"),\n                    })\n            \n            result = NexusAnalysisResult(\n                company_id=company_id,\n                analysis_period_start=analysis_period_start,\n                analysis_period_end=analysis_period_end,\n                jurisdictions_analyzed=len(jurisdictions_analyzed),\n                current_nexus_count=len(current_nexus),\n                new_nexus_required=new_nexus_required,\n                jurisdictions_detail=jurisdictions_analyzed,\n                generated_at=datetime.utcnow(),\n            )\n            \n            return ServiceResult.success(data=result)\n            \n        except Exception as e:\n            self._log_error(f"Error analyzing nexus exposure: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to analyze nexus exposure: {str(e)}",\n                code="NEXUS_ANALYSIS_ERROR",\n            )\n    \n    # ==================== Tax Registration Management ====================\n    \n    @transactional\n    @emit_event("tax_registration.created")\n    @requires_permission("tax_registration:create")\n    async def create_tax_registration(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: TaxRegistrationCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a tax registration."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Check for duplicate\n            existing = await self.registration_repo.get_by_number(\n                company_id=company_id,\n                registration_number=data.registration_number,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error="Registration number already exists",\n                    code="DUPLICATE_REGISTRATION",\n                )\n            \n            registration = await self.registration_repo.create({\n                "company_id": company_id,\n                "tax_authority_id": data.tax_authority_id,\n                "registration_number": data.registration_number,\n                "registration_type": data.registration_type,\n                "effective_from": data.effective_from or date.today(),\n                "effective_to": data.effective_to,\n                "legal_name": data.legal_name,\n                "trading_name": data.trading_name,\n                "registered_address": data.registered_address,\n                "filing_frequency": data.filing_frequency,\n                "filing_method": data.filing_method,\n                "next_filing_due": data.next_filing_due,\n                "status": "active",\n                "contact_name": data.contact_name,\n                "contact_email": data.contact_email,\n                "contact_phone": data.contact_phone,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=registration.to_dict(),\n                message="Tax registration created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating tax registration: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create tax registration: {str(e)}",\n                code="CREATE_REGISTRATION_ERROR",\n            )\n    \n    # ==================== Tax Filing Management ====================\n    \n    @transactional\n    @emit_event("tax_filing.created")\n    @requires_permission("tax_filing:create")\n    async def create_tax_filing(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: TaxFilingCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """\n        Create a tax filing for a period.\n        \n        Calculates tax liability and generates filing data.\n        """\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Validate registration\n            registration = await self.registration_repo.get_by_id(data.registration_id)\n            if not registration or registration.company_id != company_id:\n                return ServiceResult.failure(\n                    error="Invalid registration",\n                    code="INVALID_REGISTRATION",\n                )\n            \n            # Check for existing filing\n            existing = await self.filing_repo.get_by_period(\n                registration_id=data.registration_id,\n                period_start=data.period_start,\n                period_end=data.period_end,\n            )\n            if existing:\n                return ServiceResult.failure(\n                    error="Filing already exists for this period",\n                    code="FILING_EXISTS",\n                )\n            \n            # Calculate liability for the period\n            liability = await self._calculate_period_liability(\n                company_id=company_id,\n                registration_id=data.registration_id,\n                period_start=data.period_start,\n                period_end=data.period_end,\n            )\n            \n            # Generate filing number\n            filing_number = await self._generate_filing_number(\n                company_id=company_id,\n                registration=registration,\n            )\n            \n            filing = await self.filing_repo.create({\n                "company_id": company_id,\n                "registration_id": data.registration_id,\n                "filing_number": filing_number,\n                "filing_type": data.filing_type,\n                "period_start": data.period_start,\n                "period_end": data.period_end,\n                "due_date": data.due_date,\n                "status": TaxFilingStatus.DRAFT,\n                "gross_sales": liability.get("gross_sales", Decimal("0")),\n                "taxable_sales": liability.get("taxable_sales", Decimal("0")),\n                "nontaxable_sales": liability.get("nontaxable_sales", Decimal("0")),\n                "tax_collected": liability.get("tax_collected", Decimal("0")),\n                "adjustments": Decimal("0"),\n                "total_due": liability.get("tax_collected", Decimal("0")),\n                "currency_code": data.currency_code,\n                "notes": data.notes,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data={\n                    "filing": filing.to_dict(),\n                    "liability_summary": liability,\n                },\n                message=f"Tax filing \'{filing_number}\' created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating tax filing: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create tax filing: {str(e)}",\n                code="CREATE_FILING_ERROR",\n            )\n    \n    @transactional\n    @emit_event("tax_filing.submitted")\n    @requires_permission("tax_filing:submit")\n    async def submit_tax_filing(\n        self,\n        context: ServiceContext,\n        filing_id: UUID,\n        submission_reference: Optional[str] = None,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Submit a tax filing to the authority."""\n        try:\n            filing = await self.filing_repo.get_by_id(filing_id)\n            if not filing:\n                return ServiceResult.failure(\n                    error="Tax filing not found",\n                    code="FILING_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, filing.company_id)\n            \n            if filing.status not in [TaxFilingStatus.DRAFT, TaxFilingStatus.PENDING]:\n                return ServiceResult.failure(\n                    error=f"Cannot submit filing with status \'{filing.status}\'",\n                    code="INVALID_STATUS",\n                )\n            \n            filing.status = TaxFilingStatus.SUBMITTED\n            filing.submitted_at = datetime.utcnow()\n            filing.submitted_by = context.user_id\n            filing.submission_reference = submission_reference\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=filing.to_dict(),\n                message=f"Tax filing \'{filing.filing_number}\' submitted successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error submitting tax filing: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to submit tax filing: {str(e)}",\n                code="SUBMIT_FILING_ERROR",\n            )\n    \n    @transactional\n    @emit_event("tax_payment.created")\n    @requires_permission("tax_payment:create")\n    async def record_tax_payment(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: TaxPaymentCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Record a tax payment."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Validate filing exists\n            filing = await self.filing_repo.get_by_id(data.filing_id)\n            if not filing or filing.company_id != company_id:\n                return ServiceResult.failure(\n                    error="Invalid filing",\n                    code="INVALID_FILING",\n                )\n            \n            payment = await self.filing_repo.create_payment({\n                "company_id": company_id,\n                "filing_id": data.filing_id,\n                "payment_number": await self._generate_payment_number(company_id),\n                "payment_date": data.payment_date or date.today(),\n                "amount": data.amount,\n                "currency_code": data.currency_code,\n                "payment_method": data.payment_method,\n                "reference_number": data.reference_number,\n                "bank_account_id": data.bank_account_id,\n                "notes": data.notes,\n                "created_by": context.user_id,\n            })\n            \n            # Update filing payment status\n            filing.amount_paid = (filing.amount_paid or Decimal("0")) + data.amount\n            filing.balance_due = filing.total_due - filing.amount_paid\n            \n            if filing.balance_due <= Decimal("0"):\n                filing.payment_status = "paid"\n            else:\n                filing.payment_status = "partial"\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=payment.to_dict(),\n                message="Tax payment recorded successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error recording tax payment: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to record tax payment: {str(e)}",\n                code="RECORD_PAYMENT_ERROR",\n            )\n    \n    # ==================== Tax Exemption Management ====================\n    \n    @transactional\n    @emit_event("tax_exemption.created")\n    @requires_permission("tax_exemption:create")\n    async def create_tax_exemption(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        data: TaxExemptionCreate,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Create a tax exemption certificate."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            exemption = await self.exemption_repo.create({\n                "company_id": company_id,\n                "customer_id": data.customer_id,\n                "exemption_type": data.exemption_type,\n                "certificate_number": data.certificate_number,\n                "reason": data.reason,\n                "jurisdiction": data.jurisdiction,\n                "tax_types": data.tax_types,\n                "effective_from": data.effective_from or date.today(),\n                "effective_to": data.effective_to,\n                "exemption_rate": data.exemption_rate,\n                "product_categories": data.product_categories,\n                "certificate_document": data.certificate_document,\n                "is_valid": True,\n                "verified_at": datetime.utcnow() if data.verified else None,\n                "verified_by": context.user_id if data.verified else None,\n                "custom_fields": data.custom_fields,\n                "created_by": context.user_id,\n            })\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(\n                data=exemption.to_dict(),\n                message="Tax exemption created successfully",\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error creating tax exemption: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to create tax exemption: {str(e)}",\n                code="CREATE_EXEMPTION_ERROR",\n            )\n    \n    async def validate_exemption(\n        self,\n        context: ServiceContext,\n        exemption_id: UUID,\n    ) -> ServiceResult[Dict[str, Any]]:\n        """Validate a tax exemption certificate."""\n        try:\n            exemption = await self.exemption_repo.get_by_id(exemption_id)\n            if not exemption:\n                return ServiceResult.failure(\n                    error="Exemption not found",\n                    code="EXEMPTION_NOT_FOUND",\n                )\n            \n            await self.ensure_company_scope(context, exemption.company_id)\n            \n            validation_result = {\n                "exemption_id": str(exemption_id),\n                "is_valid": True,\n                "validation_date": date.today().isoformat(),\n                "issues": [],\n            }\n            \n            # Check expiration\n            if exemption.effective_to and exemption.effective_to < date.today():\n                validation_result["is_valid"] = False\n                validation_result["issues"].append("Certificate has expired")\n                exemption.is_valid = False\n            \n            # Check if certificate number is properly formatted\n            if not exemption.certificate_number:\n                validation_result["is_valid"] = False\n                validation_result["issues"].append("Missing certificate number")\n            \n            # Update status\n            exemption.validated_at = datetime.utcnow()\n            \n            await self._session.flush()\n            \n            return ServiceResult.success(data=validation_result)\n            \n        except Exception as e:\n            self._log_error(f"Error validating exemption: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to validate exemption: {str(e)}",\n                code="VALIDATE_EXEMPTION_ERROR",\n            )\n    \n    # ==================== Tax Reports ====================\n    \n    @cached(ttl=300, key_prefix="tax_liability")\n    @requires_permission("report:tax_liability")\n    async def generate_tax_liability_report(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        start_date: date,\n        end_date: date,\n        group_by: str = "jurisdiction",\n    ) -> ServiceResult[TaxLiabilityReport]:\n        """Generate tax liability report for a period."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            # Get tax transactions for period\n            transactions = await self.transaction_repo.get_by_period(\n                company_id=company_id,\n                start_date=start_date,\n                end_date=end_date,\n            )\n            \n            # Group by specified dimension\n            grouped_data: Dict[str, Dict[str, Decimal]] = {}\n            total_liability = Decimal("0")\n            total_taxable = Decimal("0")\n            \n            for txn in transactions:\n                key = self._get_group_key(txn, group_by)\n                \n                if key not in grouped_data:\n                    grouped_data[key] = {\n                        "taxable_amount": Decimal("0"),\n                        "tax_amount": Decimal("0"),\n                        "transaction_count": 0,\n                    }\n                \n                grouped_data[key]["taxable_amount"] += txn.taxable_amount\n                grouped_data[key]["tax_amount"] += txn.tax_amount\n                grouped_data[key]["transaction_count"] += 1\n                \n                total_liability += txn.tax_amount\n                total_taxable += txn.taxable_amount\n            \n            # Get payments made\n            payments = await self.filing_repo.get_payments_by_period(\n                company_id=company_id,\n                start_date=start_date,\n                end_date=end_date,\n            )\n            total_paid = sum(p.amount for p in payments)\n            \n            report = TaxLiabilityReport(\n                company_id=company_id,\n                start_date=start_date,\n                end_date=end_date,\n                group_by=group_by,\n                grouped_data=grouped_data,\n                total_taxable=total_taxable,\n                total_liability=total_liability,\n                total_paid=total_paid,\n                balance_due=total_liability - total_paid,\n                generated_at=datetime.utcnow(),\n            )\n            \n            return ServiceResult.success(data=report)\n            \n        except Exception as e:\n            self._log_error(f"Error generating tax liability report: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to generate tax liability report: {str(e)}",\n                code="TAX_LIABILITY_REPORT_ERROR",\n            )\n    \n    async def get_upcoming_filings(\n        self,\n        context: ServiceContext,\n        company_id: UUID,\n        days_ahead: int = 30,\n    ) -> ServiceResult[List[Dict[str, Any]]]:\n        """Get upcoming tax filings due."""\n        try:\n            await self.ensure_company_scope(context, company_id)\n            \n            end_date = date.today() + timedelta(days=days_ahead)\n            \n            filings = await self.filing_repo.get_upcoming(\n                company_id=company_id,\n                due_before=end_date,\n            )\n            \n            return ServiceResult.success(\n                data=[f.to_dict() for f in filings]\n            )\n            \n        except Exception as e:\n            self._log_error(f"Error getting upcoming filings: {str(e)}")\n            return ServiceResult.failure(\n                error=f"Failed to get upcoming filings: {str(e)}",\n                code="GET_FILINGS_ERROR",\n            )\n    \n    # ==================== Helper Methods ====================\n    \n    def _check_exemption(\n        self,\n        rate: Dict[str, Any],\n        exemptions: List[Any],\n        product_category: Optional[str],\n    ) -> bool:\n        """Check if a rate is exempt for the given conditions."""\n        for exemption in exemptions:\n            # Check jurisdiction match\n            if exemption.jurisdiction and rate.get("jurisdiction") != exemption.jurisdiction:\n                continue\n            \n            # Check tax type\n            if exemption.tax_types and rate.get("tax_type") not in exemption.tax_types:\n                continue\n            \n            # Check product category\n            if exemption.product_categories and product_category:\n                if product_category not in exemption.product_categories:\n                    continue\n            \n            # Valid exemption found\n            return True\n        \n        return False\n    \n    def _find_applicable_exemption(\n        self,\n        rate: Dict[str, Any],\n        exemptions: List[Any],\n        product_category: Optional[str],\n        customer_id: Optional[UUID],\n    ) -> Optional[Any]:\n        """Find the most applicable exemption."""\n        for exemption in exemptions:\n            # Check if exemption is for specific customer\n            if exemption.customer_id and exemption.customer_id != customer_id:\n                continue\n            \n            # Check jurisdiction\n            if exemption.jurisdiction and rate.get("jurisdiction") != exemption.jurisdiction:\n                continue\n            \n            # Check tax type\n            if exemption.tax_types and rate.get("tax_type") not in exemption.tax_types:\n                continue\n            \n            # Check product category\n            if exemption.product_categories and product_category:\n                if product_category not in exemption.product_categories:\n                    continue\n            \n            # Check validity\n            if exemption.effective_from and exemption.effective_from > date.today():\n                continue\n            if exemption.effective_to and exemption.effective_to < date.today():\n                continue\n            \n            if exemption.is_valid:\n                return exemption\n        \n        return None\n    \n    async def _check_nexus(\n        self,\n        company_id: UUID,\n        jurisdiction: Dict[str, str],\n    ) -> bool:\n        """Check if company has nexus in a jurisdiction."""\n        nexus = await self.nexus_repo.get_by_jurisdiction(\n            company_id=company_id,\n            country_code=jurisdiction.get("country_code"),\n            state_code=jurisdiction.get("state_code"),\n        )\n        \n        return len(nexus) > 0\n    \n    async def _check_economic_threshold(\n        self,\n        company_id: UUID,\n        jurisdiction: Dict[str, str],\n        sales_amount: Decimal,\n        transaction_count: int,\n        period_start: date,\n        period_end: date,\n    ) -> Dict[str, Any]:\n        """Check if economic nexus threshold is met."""\n        thresholds = await self.nexus_repo.get_economic_thresholds(\n            country_code=jurisdiction.get("country_code"),\n            state_code=jurisdiction.get("state_code"),\n        )\n        \n        if not thresholds:\n            return {"threshold_met": False, "threshold_percentage": 0}\n        \n        for threshold in thresholds:\n            met = False\n            percentage = Decimal("0")\n            \n            if threshold.threshold_type == "sales_amount":\n                percentage = (sales_amount / threshold.threshold_amount) * Decimal("100")\n                met = sales_amount >= threshold.threshold_amount\n            elif threshold.threshold_type == "transaction_count":\n                percentage = Decimal(str(transaction_count / threshold.threshold_count * 100))\n                met = transaction_count >= threshold.threshold_count\n            elif threshold.threshold_type == "either":\n                sales_met = sales_amount >= threshold.threshold_amount\n                count_met = transaction_count >= threshold.threshold_count\n                met = sales_met or count_met\n                percentage = max(\n                    (sales_amount / threshold.threshold_amount) * Decimal("100"),\n                    Decimal(str(transaction_count / threshold.threshold_count * 100)),\n                )\n            \n            if met:\n                return {\n                    "threshold_met": True,\n                    "threshold_type": threshold.threshold_type,\n                    "threshold_amount": threshold.threshold_amount,\n                    "threshold_percentage": percentage,\n                }\n        \n        return {"threshold_met": False, "threshold_percentage": percentage}\n    \n    async def _create_tax_transaction(\n        self,\n        company_id: UUID,\n        transaction_id: Optional[str],\n        tax_details: List[Dict[str, Any]],\n        subtotal: Decimal,\n        total_tax: Decimal,\n        context: ServiceContext,\n    ) -> None:\n        """Create tax transaction record."""\n        for detail in tax_details:\n            await self.transaction_repo.create({\n                "company_id": company_id,\n                "source_transaction_id": transaction_id,\n                "tax_rate_id": detail.get("rate_id"),\n                "taxable_amount": detail.get("taxable_amount"),\n                "tax_amount": detail.get("tax_amount"),\n                "tax_type": detail.get("tax_type"),\n                "jurisdiction": detail.get("jurisdiction"),\n                "transaction_date": date.today(),\n                "created_by": context.user_id,\n            })\n    \n    async def _calculate_period_liability(\n        self,\n        company_id: UUID,\n        registration_id: UUID,\n        period_start: date,\n        period_end: date,\n    ) -> Dict[str, Decimal]:\n        """Calculate tax liability for a period."""\n        transactions = await self.transaction_repo.get_by_period(\n            company_id=company_id,\n            start_date=period_start,\n            end_date=period_end,\n            registration_id=registration_id,\n        )\n        \n        gross_sales = Decimal("0")\n        taxable_sales = Decimal("0")\n        nontaxable_sales = Decimal("0")\n        tax_collected = Decimal("0")\n        \n        for txn in transactions:\n            gross_sales += txn.taxable_amount + txn.tax_amount\n            if txn.tax_amount > 0:\n                taxable_sales += txn.taxable_amount\n            else:\n                nontaxable_sales += txn.taxable_amount\n            tax_collected += txn.tax_amount\n        \n        return {\n            "gross_sales": gross_sales,\n            "taxable_sales": taxable_sales,\n            "nontaxable_sales": nontaxable_sales,\n            "tax_collected": tax_collected,\n        }\n    \n    async def _generate_filing_number(\n        self,\n        company_id: UUID,\n        registration: TaxRegistration,\n    ) -> str:\n        """Generate a unique filing number."""\n        count = await self.filing_repo.count_by_registration(registration.id)\n        year = date.today().year\n        return f"TF-{registration.registration_number}-{year}-{str(count + 1).zfill(4)}"\n    \n    async def _generate_payment_number(self, company_id: UUID) -> str:\n        """Generate a unique payment number."""\n        count = await self.filing_repo.count_payments(company_id)\n        year = date.today().year\n        return f"TP-{year}-{str(count + 1).zfill(6)}"\n    \n    def _get_group_key(self, transaction: Any, group_by: str) -> str:\n        """Get grouping key for transaction."""\n        if group_by == "jurisdiction":\n            return transaction.jurisdiction or "Unknown"\n        elif group_by == "tax_type":\n            return transaction.tax_type.value if transaction.tax_type else "Unknown"\n        elif group_by == "rate":\n            return str(transaction.tax_rate_id)\n        else:\n            return "All"\n</create-file>\n</function_calls>'}
{'role': 'user', 'content': '<tool_result> <create-file> ToolResult(success=True, output="File \'backend/app/services/tax.py\' created successfully.") </create-file> </tool_result>'}