feat: Migrate to ultrathin monolith architecture
Consolidate 3 separate applications (reports-app, data-entry-app, telegram-bot) into a unified
architecture with single backend and frontend:
Backend Changes:
- Unified FastAPI backend at backend/ with modular structure
- Modules: reports, data_entry, telegram in backend/modules/
- Centralized config.py and main.py with all routers registered
- Single worker mode (--workers 1) for Telegram bot compatibility
- Shared Oracle connection pool and JWT authentication
- Unified requirements.txt and environment configuration
Frontend Changes:
- Single Vue.js SPA with module-based routing
- Unified frontend at src/ with modules in src/modules/{reports,data-entry}/
- Shared components and stores in src/shared/
- Error boundaries for module isolation
- Dual API proxy in Vite for module communication
Infrastructure:
- New unified startup scripts: start-prod.sh, start-test.sh, start-backend.sh
- Environment templates: .env.dev.example, .env.test.example, .env.prod.example
- Updated deployment scripts for Windows IIS
- Simplified SSH tunnel management
Documentation:
- Comprehensive CLAUDE.md with architecture overview
- Module-specific docs in docs/{data-entry,telegram}/
- Architecture decision records in docs/ARCHITECTURE-DECISIONS.md
- Deployment guides consolidated in deployment/windows/docs/
This migration reduces complexity, improves maintainability, and enables easier
deployment while maintaining all existing functionality.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,115 +0,0 @@
|
||||
{
|
||||
"gotchas": [
|
||||
{
|
||||
"id": "got_20251222_182000",
|
||||
"timestamp": "2025-12-22T18:20:00Z",
|
||||
"title": "Import Path Hell: Default vs Named Exports",
|
||||
"problem": "Build failed with 'apiService is not exported' errors even though the module exports default api. Legacy code was using import { apiService } from 'api.js' which doesn't work with export default api.",
|
||||
"solution": "Changed all imports from import { apiService } to import api, then updated all references from apiService.get to api.get. Also renamed imports to avoid conflicts (e.g., import apiClient from 'api').",
|
||||
"context": "Encountered during build when migrating stores that expected named exports but API services used default exports",
|
||||
"tags": ["javascript", "imports", "exports", "build-errors", "migration"],
|
||||
"feature": "unified-app"
|
||||
},
|
||||
{
|
||||
"id": "got_20251222_182001",
|
||||
"timestamp": "2025-12-22T18:20:01Z",
|
||||
"title": "Pinia Store Factory Pattern Not Auto-Exported",
|
||||
"problem": "Build failed with 'useCompanyStore is not exported by companies.js' because the shared stores are factory functions (createCompaniesStore), not direct exports (useCompanyStore).",
|
||||
"solution": "Created module-specific sharedStores.js files that instantiate the factory functions with the module's API service, then export the actual store instances. Components import from module's sharedStores.js, not from @shared directly.",
|
||||
"context": "Shared stores were designed as factories but modules were trying to import them as if they were regular stores",
|
||||
"tags": ["pinia", "stores", "factory-pattern", "imports", "architecture"],
|
||||
"feature": "unified-app"
|
||||
},
|
||||
{
|
||||
"id": "got_20251222_182002",
|
||||
"timestamp": "2025-12-22T18:20:02Z",
|
||||
"title": "Sed Command Quote Mismatch in Bulk Find-Replace",
|
||||
"problem": "Bulk sed commands using single quotes in pattern didn't match imports using double quotes, and vice versa. Commands like sed 's|from '@/stores/'|...' didn't replace from \"@/stores/\" lines.",
|
||||
"solution": "Always use the quote style that matches the target files. For Vue/JS files with ESLint using double quotes, use double quotes in sed patterns. Better yet: use find -exec with separate sed for each file to handle both quote styles.",
|
||||
"context": "Spent significant time debugging why sed replacements weren't working during mass import path updates",
|
||||
"tags": ["sed", "regex", "scripting", "find-replace", "migration"],
|
||||
"feature": "unified-app"
|
||||
},
|
||||
{
|
||||
"id": "got_20251222_182003",
|
||||
"timestamp": "2025-12-22T18:20:03Z",
|
||||
"title": "Circular Reference in API Wrapper",
|
||||
"problem": "receiptsStore.js failed to build with 'Identifier api has already been declared' because it imported api and then declared const api = { ... } wrapper object using the same name.",
|
||||
"solution": "Renamed the import to apiClient (import apiClient from 'api') and used it in the wrapper: const api = { get: (url) => apiClient.get('/receipts${url}') }. This keeps the wrapper name 'api' for internal use while avoiding the conflict.",
|
||||
"context": "Store was creating a scoped API wrapper for DRY principle but shadowed the import name",
|
||||
"tags": ["javascript", "naming", "scope", "imports", "build-errors"],
|
||||
"feature": "unified-app"
|
||||
},
|
||||
{
|
||||
"id": "got_20251222_182004",
|
||||
"timestamp": "2025-12-22T18:20:04Z",
|
||||
"title": "CSS Import Paths Breaking Build in Unified Structure",
|
||||
"problem": "Build failed with 'Unable to resolve @import \"../../../../../shared/frontend/styles/layout/header.css\"' because the CSS files were copied but their import paths still pointed to old shared/frontend location.",
|
||||
"solution": "Commented out the problematic @import statements in main.css since those styles are already imported in App.vue. Alternatively, could have updated paths to use @shared alias or relative paths from new location.",
|
||||
"context": "CSS files from reports-app referenced shared styles using relative paths that became invalid in unified structure",
|
||||
"tags": ["css", "imports", "build-errors", "migration", "paths"],
|
||||
"feature": "unified-app"
|
||||
},
|
||||
{
|
||||
"id": "got_20251222_182005",
|
||||
"timestamp": "2025-12-22T18:20:05Z",
|
||||
"title": "Module Component Utilities Not Copied During Migration",
|
||||
"problem": "Build failed with 'Could not resolve ../utils/exportUtils' because views referenced utils/ and components/ directories that weren't copied during initial migration (only views and stores were copied).",
|
||||
"solution": "Copied the entire utils/ and components/ directories from source apps to module directories. These supporting files are essential dependencies of the views and must be migrated together.",
|
||||
"context": "Initial migration focused on views/stores but missed the supporting utilities and components they depend on",
|
||||
"tags": ["migration", "dependencies", "file-structure", "build-errors"],
|
||||
"feature": "unified-app"
|
||||
},
|
||||
{
|
||||
"id": "got_20251222_182006",
|
||||
"timestamp": "2025-12-22T18:20:06Z",
|
||||
"title": "Vite Build Transform Count is Progress Indicator",
|
||||
"problem": "Hard to tell if build is making progress when fixing import issues. Each fix revealed new errors, causing frustration.",
|
||||
"solution": "Watch the 'transforming... ✓ N modules transformed' count - it increases with each successful fix even if build ultimately fails. Going from 200→573→1490→1492 modules meant we were getting close to success. Use this as encouragement!",
|
||||
"context": "During iterative import path fixing, the transform count showed we were making real progress toward a successful build",
|
||||
"tags": ["vite", "build", "debugging", "progress-tracking", "developer-experience"],
|
||||
"feature": "unified-app"
|
||||
},
|
||||
{
|
||||
"id": "got_20251224_001000",
|
||||
"timestamp": "2025-12-24T00:10:00Z",
|
||||
"title": "Menu Structure Mismatch: Flat Array vs Nested Sections",
|
||||
"problem": "Hamburger menu appeared completely empty (no menu items visible) even though enabledMenuItems computed property returned data. Used .flatMap() to create flat array [{item1}, {item2}] but SlideMenu component expected nested structure [{title: 'Section', items: [...]}, ...].",
|
||||
"solution": "Removed .flatMap() transformation and returned the nested structure directly from getEnabledMenuSections(). Component's v-for=\"section in menuItems\" now properly iterates over sections, then v-for=\"item in section.items\" shows all items.",
|
||||
"context": "User reported 'MENIUL HAMBURGER ARE IN CONTINUARE TEXT ALB PE FUNDAL ALB' and provided screenshot showing menu with only user profile at bottom, no menu sections/items visible",
|
||||
"tags": ["vue", "data-structure", "component-contract", "v-for", "ux"],
|
||||
"feature": "unified-app-ux"
|
||||
},
|
||||
{
|
||||
"id": "got_20251224_001001",
|
||||
"timestamp": "2025-12-24T00:10:01Z",
|
||||
"title": "TypeError: useAuthStore is not a function - Store Timing Issue",
|
||||
"problem": "Period store threw 'TypeError: useAuthStore is not a function' when trying to call useAuthStore() in getStorageKey() function. Stores were passed as factory parameters but weren't callable in that context/timing.",
|
||||
"solution": "Wrap store access in try-catch with lazy instantiation. Call useAuthStore() inside the function that needs it, not at module level. Return null if stores aren't ready yet. This allows graceful degradation when stores haven't been initialized by Pinia yet.",
|
||||
"context": "accountingPeriod.js tried to access auth/company stores to generate localStorage key for persisting user's period selection",
|
||||
"tags": ["pinia", "stores", "timing", "initialization", "error-handling"],
|
||||
"feature": "unified-app-ux"
|
||||
},
|
||||
{
|
||||
"id": "got_20251224_001002",
|
||||
"timestamp": "2025-12-24T00:10:02Z",
|
||||
"title": "Missing Auth Token in API Requests Causes 500 Errors",
|
||||
"problem": "Backend returned 500 Internal Server Error when frontend tried to load accounting periods. Console showed no Authorization header in requests even though user was logged in and JWT token existed in localStorage.",
|
||||
"solution": "Add axios request interceptor to automatically inject token: authApi.interceptors.request.use(config => { const token = localStorage.getItem('access_token'); if (token) config.headers.Authorization = `Bearer ${token}`; return config; }). Place this AFTER creating axios instance but BEFORE making any API calls.",
|
||||
"context": "App.vue created authApi axios instance with only baseURL and Content-Type, but didn't configure automatic token injection from localStorage",
|
||||
"tags": ["axios", "jwt", "authentication", "api", "interceptor"],
|
||||
"feature": "unified-app-ux"
|
||||
},
|
||||
{
|
||||
"id": "got_20251224_001003",
|
||||
"timestamp": "2025-12-24T00:10:03Z",
|
||||
"title": "Period Auto-Load Never Triggered Despite Handler Exists",
|
||||
"problem": "Period dropdown stayed on 'Selectare perioada' placeholder even after manually selecting company. handleCompanyChanged() function existed and logged messages, but periods.value and selectedPeriod.value remained empty. No automatic loading occurred.",
|
||||
"solution": "Add Vue watch() on companyStore.selectedCompany to automatically call periodStore.loadPeriods() when company changes. Handler alone isn't enough - need reactive watcher with { immediate: true } to handle both initial load and subsequent changes. Watch triggers for ALL company changes (auto-select on login + manual selection).",
|
||||
"context": "User explicitly reported 'dar si dupa ce aleg o firma MARIUS M AUTO, AR TREBUI SA SE SELECTEZE AUTOMAT ULTIMA LUNA, SI NU SE SELECTEAZA' after manually selecting company",
|
||||
"tags": ["vue", "watch", "reactive", "auto-load", "ux"],
|
||||
"feature": "unified-app-ux"
|
||||
}
|
||||
],
|
||||
"updated": "2025-12-24T00:10:00Z"
|
||||
}
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"id": "pat_20251222_182000",
|
||||
"timestamp": "2025-12-22T18:20:00Z",
|
||||
"title": "Unified Vue SPA with Module Isolation via Error Boundaries",
|
||||
"description": "Consolidate multiple Vue apps into a single SPA using lazy-loaded modules wrapped in error boundaries. Each module has its own layout component with ErrorBoundary wrapper to prevent crashes from propagating across modules.",
|
||||
"context": "Implemented while unifying Reports App and Data Entry App into single deployment",
|
||||
"example": {
|
||||
"file": "src/modules/reports/ReportsLayout.vue",
|
||||
"lines": "1-7",
|
||||
"snippet": "<template>\n <ErrorBoundary module-name=\"Rapoarte\">\n <router-view />\n </ErrorBoundary>\n</template>\n\n<script setup>\nimport ErrorBoundary from '@shared/components/ErrorBoundary.vue'\n</script>"
|
||||
},
|
||||
"tags": ["vue", "spa", "error-boundary", "module-isolation", "architecture"],
|
||||
"feature": "unified-app",
|
||||
"usageCount": 0
|
||||
},
|
||||
{
|
||||
"id": "pat_20251222_182001",
|
||||
"timestamp": "2025-12-22T18:20:01Z",
|
||||
"title": "Dual API Proxy Pattern in Vite for Microservices",
|
||||
"description": "Configure Vite dev server to proxy multiple backend microservices under different paths. Allows unified frontend to communicate with separate backend services while maintaining CORS and authentication.",
|
||||
"context": "Needed to route /api/reports to port 8001 and /api/data-entry to port 8003 from single frontend",
|
||||
"example": {
|
||||
"file": "vite.config.js",
|
||||
"lines": "38-62",
|
||||
"snippet": "proxy: {\n '/api/reports': {\n target: 'http://localhost:8001',\n changeOrigin: true,\n rewrite: (path) => path.replace(/^\\/api\\/reports/, '/api'),\n configure: (proxy) => {\n proxy.on('proxyReq', (proxyReq, req) => {\n if (req.headers.authorization) {\n proxyReq.setHeader('Authorization', req.headers.authorization);\n }\n });\n }\n },\n '/api/data-entry': {\n target: 'http://localhost:8003',\n changeOrigin: true,\n rewrite: (path) => path.replace(/^\\/api\\/data-entry/, '/api')\n }\n}"
|
||||
},
|
||||
"tags": ["vite", "proxy", "microservices", "api", "configuration"],
|
||||
"feature": "unified-app",
|
||||
"usageCount": 0
|
||||
},
|
||||
{
|
||||
"id": "pat_20251222_182002",
|
||||
"timestamp": "2025-12-22T18:20:02Z",
|
||||
"title": "Pinia Store Factory Pattern for Shared Stores",
|
||||
"description": "Create shared Pinia stores as factory functions that accept API service instances. Each module instantiates the shared stores with its own API service, ensuring proper module isolation while sharing store logic.",
|
||||
"context": "Auth, companies, and accounting period stores needed to work with both Reports (port 8001) and Data Entry (port 8003) APIs",
|
||||
"example": {
|
||||
"file": "src/shared/stores/auth.js",
|
||||
"lines": "21-32",
|
||||
"snippet": "export function createAuthStore(apiService) {\n return defineStore('auth', () => {\n const accessToken = ref(localStorage.getItem('access_token'))\n // ... state\n\n const login = async (credentials) => {\n const response = await apiService.post('/auth/login', credentials)\n // ... handle response\n }\n\n return { login, logout, isAuthenticated, currentUser }\n })\n}"
|
||||
},
|
||||
"tags": ["pinia", "stores", "factory-pattern", "module-isolation", "vue"],
|
||||
"feature": "unified-app",
|
||||
"usageCount": 0
|
||||
},
|
||||
{
|
||||
"id": "pat_20251222_182003",
|
||||
"timestamp": "2025-12-22T18:20:03Z",
|
||||
"title": "Module-Specific Shared Store Instances",
|
||||
"description": "Instantiate shared store factories in each module's dedicated file to ensure proper API service binding. Prevents import confusion and ensures each module uses its own API base URL.",
|
||||
"context": "Needed to prevent modules from accidentally using wrong API service when importing shared stores",
|
||||
"example": {
|
||||
"file": "src/modules/reports/stores/sharedStores.js",
|
||||
"lines": "1-18",
|
||||
"snippet": "import { createAuthStore } from '@shared/stores/auth'\nimport { createCompaniesStore } from '@shared/stores/companies'\nimport { createAccountingPeriodStore } from '@shared/stores/accountingPeriod'\nimport api from '@reports/services/api'\n\n// Create instances with Reports API service\nexport const useAuthStore = createAuthStore(api)\nexport const useCompanyStore = createCompaniesStore(api, useAuthStore)\nexport const useAccountingPeriodStore = createAccountingPeriodStore(api)\n\n// All reports components import from this file, not directly from @shared"
|
||||
},
|
||||
"tags": ["pinia", "stores", "module-isolation", "api", "architecture"],
|
||||
"feature": "unified-app",
|
||||
"usageCount": 0
|
||||
},
|
||||
{
|
||||
"id": "pat_20251222_182004",
|
||||
"timestamp": "2025-12-22T18:20:04Z",
|
||||
"title": "Vite Alias Strategy for Module Organization",
|
||||
"description": "Use Vite path aliases to create clear module boundaries: @shared for shared code, @reports and @data-entry for module-specific code. Makes imports explicit and prevents accidental cross-module dependencies.",
|
||||
"context": "Needed clear import paths when consolidating two apps with different import patterns",
|
||||
"example": {
|
||||
"file": "vite.config.js",
|
||||
"lines": "19-26",
|
||||
"snippet": "resolve: {\n alias: {\n '@': fileURLToPath(new URL('./src', import.meta.url)),\n '@shared': fileURLToPath(new URL('./src/shared', import.meta.url)),\n '@reports': fileURLToPath(new URL('./src/modules/reports', import.meta.url)),\n '@data-entry': fileURLToPath(new URL('./src/modules/data-entry', import.meta.url))\n },\n dedupe: ['vue', 'vue-router', 'pinia', 'primevue']\n}"
|
||||
},
|
||||
"tags": ["vite", "aliases", "imports", "module-organization", "architecture"],
|
||||
"feature": "unified-app",
|
||||
"usageCount": 0
|
||||
},
|
||||
{
|
||||
"id": "pat_20251222_182005",
|
||||
"timestamp": "2025-12-22T18:20:05Z",
|
||||
"title": "IIS URL Rewrite Rules for SPA with Multiple API Backends",
|
||||
"description": "Configure IIS web.config to proxy different API paths to different backend ports while serving SPA for all other routes. Enables single IIS site to route to multiple microservices.",
|
||||
"context": "Production deployment needs unified frontend to communicate with both backend services through IIS",
|
||||
"example": {
|
||||
"file": "public/web.config",
|
||||
"lines": "5-28",
|
||||
"snippet": "<rewrite>\n <rules>\n <rule name=\"Proxy Reports API\" stopProcessing=\"true\">\n <match url=\"^api/reports/(.*)\" />\n <action type=\"Rewrite\" url=\"http://localhost:8001/api/{R:1}\" />\n </rule>\n <rule name=\"Proxy Data Entry API\" stopProcessing=\"true\">\n <match url=\"^api/data-entry/(.*)\" />\n <action type=\"Rewrite\" url=\"http://localhost:8003/api/{R:1}\" />\n </rule>\n <rule name=\"SPA Fallback\" stopProcessing=\"true\">\n <match url=\".*\" />\n <conditions logicalGrouping=\"MatchAll\">\n <add input=\"{REQUEST_FILENAME}\" matchType=\"IsFile\" negate=\"true\" />\n </conditions>\n <action type=\"Rewrite\" url=\"/index.html\" />\n </rule>\n </rules>\n</rewrite>"
|
||||
},
|
||||
"tags": ["iis", "deployment", "spa", "microservices", "proxy"],
|
||||
"feature": "unified-app",
|
||||
"usageCount": 0
|
||||
},
|
||||
{
|
||||
"id": "pat_20251224_001000",
|
||||
"timestamp": "2025-12-24T00:10:00Z",
|
||||
"title": "Vue Watcher for Auto-Loading Dependent Data",
|
||||
"description": "Use Vue watch() to automatically trigger data loading when dependent selections change. Watch company selection changes to auto-load accounting periods, ensuring UI stays synchronized without manual intervention.",
|
||||
"context": "Users manually selected company but period dropdown stayed on placeholder instead of auto-selecting current period",
|
||||
"example": {
|
||||
"file": "src/App.vue",
|
||||
"lines": "88-100",
|
||||
"snippet": "watch(\n () => companyStore.selectedCompany,\n async (newCompany, oldCompany) => {\n if (newCompany && newCompany.id_firma && newCompany !== oldCompany) {\n console.log('[App] Company changed via watch, loading periods for:', newCompany.id_firma)\n await periodStore.loadPeriods(newCompany.id_firma)\n console.log('[App] Periods auto-loaded successfully')\n }\n },\n { immediate: true }\n)"
|
||||
},
|
||||
"tags": ["vue", "watch", "reactive", "auto-load", "ux"],
|
||||
"feature": "unified-app-ux",
|
||||
"usageCount": 0
|
||||
},
|
||||
{
|
||||
"id": "pat_20251224_001001",
|
||||
"timestamp": "2025-12-24T00:10:01Z",
|
||||
"title": "Axios Request Interceptor for JWT Token Injection",
|
||||
"description": "Add axios request interceptor to automatically inject JWT Bearer token from localStorage into all API requests. Eliminates manual token handling in every API call and prevents 401/500 authentication errors.",
|
||||
"context": "API requests were failing with 500 errors because JWT token wasn't being sent in Authorization header",
|
||||
"example": {
|
||||
"file": "src/App.vue",
|
||||
"lines": "61-68",
|
||||
"snippet": "authApi.interceptors.request.use(config => {\n const token = localStorage.getItem('access_token')\n if (token) {\n config.headers.Authorization = `Bearer ${token}`\n }\n return config\n})"
|
||||
},
|
||||
"tags": ["axios", "jwt", "authentication", "interceptor", "api"],
|
||||
"feature": "unified-app-ux",
|
||||
"usageCount": 0
|
||||
},
|
||||
{
|
||||
"id": "pat_20251224_001002",
|
||||
"timestamp": "2025-12-24T00:10:02Z",
|
||||
"title": "Pinia Store Factory with Lazy Instantiation",
|
||||
"description": "When store factories need to access other stores, use lazy instantiation with try-catch to avoid timing issues. Access stores inside functions (not at module level) and gracefully handle cases where stores aren't ready yet.",
|
||||
"context": "Period store tried to access auth/company stores for localStorage key generation but got 'useAuthStore is not a function' error",
|
||||
"example": {
|
||||
"file": "src/shared/stores/accountingPeriod.js",
|
||||
"lines": "52-64",
|
||||
"snippet": "const getStorageKey = () => {\n try {\n const authStore = useAuthStore();\n const companyStore = useCompanyStore();\n const username = authStore.user?.username;\n const companyId = companyStore.selectedCompany?.id_firma;\n if (!username || !companyId) return null;\n return `selected_period_${username}_${companyId}`;\n } catch (e) {\n // Stores not yet initialized, skip localStorage\n return null;\n }\n};"
|
||||
},
|
||||
"tags": ["pinia", "stores", "lazy-initialization", "try-catch", "timing"],
|
||||
"feature": "unified-app-ux",
|
||||
"usageCount": 0
|
||||
}
|
||||
],
|
||||
"updated": "2025-12-24T00:10:00Z"
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
{
|
||||
"session_id": "ses_20251222_182000",
|
||||
"timestamp": "2025-12-22T18:20:00Z",
|
||||
"feature": "unified-app",
|
||||
"duration": "2h 30m",
|
||||
"insights_saved": [
|
||||
{"type": "pattern", "id": "pat_20251222_182000", "title": "Unified Vue SPA with Module Isolation via Error Boundaries"},
|
||||
{"type": "pattern", "id": "pat_20251222_182001", "title": "Dual API Proxy Pattern in Vite for Microservices"},
|
||||
{"type": "pattern", "id": "pat_20251222_182002", "title": "Pinia Store Factory Pattern for Shared Stores"},
|
||||
{"type": "pattern", "id": "pat_20251222_182003", "title": "Module-Specific Shared Store Instances"},
|
||||
{"type": "pattern", "id": "pat_20251222_182004", "title": "Vite Alias Strategy for Module Organization"},
|
||||
{"type": "pattern", "id": "pat_20251222_182005", "title": "IIS URL Rewrite Rules for SPA with Multiple API Backends"},
|
||||
{"type": "gotcha", "id": "got_20251222_182000", "title": "Import Path Hell: Default vs Named Exports"},
|
||||
{"type": "gotcha", "id": "got_20251222_182001", "title": "Pinia Store Factory Pattern Not Auto-Exported"},
|
||||
{"type": "gotcha", "id": "got_20251222_182002", "title": "Sed Command Quote Mismatch in Bulk Find-Replace"},
|
||||
{"type": "gotcha", "id": "got_20251222_182003", "title": "Circular Reference in API Wrapper"},
|
||||
{"type": "gotcha", "id": "got_20251222_182004", "title": "CSS Import Paths Breaking Build in Unified Structure"},
|
||||
{"type": "gotcha", "id": "got_20251222_182005", "title": "Module Component Utilities Not Copied During Migration"},
|
||||
{"type": "gotcha", "id": "got_20251222_182006", "title": "Vite Build Transform Count is Progress Indicator"}
|
||||
],
|
||||
"files_created": [
|
||||
"package.json",
|
||||
"vite.config.js",
|
||||
"src/router/index.js",
|
||||
"src/config/menu.js",
|
||||
"src/config/features.js",
|
||||
"src/App.vue",
|
||||
"src/main.js",
|
||||
"src/shared/components/ErrorBoundary.vue",
|
||||
"src/modules/reports/ReportsLayout.vue",
|
||||
"src/modules/data-entry/DataEntryLayout.vue",
|
||||
"src/modules/reports/services/api.js",
|
||||
"src/modules/data-entry/services/api.js",
|
||||
"src/modules/reports/stores/sharedStores.js",
|
||||
"src/modules/data-entry/stores/sharedStores.js",
|
||||
"public/web.config",
|
||||
".env.example",
|
||||
"index.html"
|
||||
],
|
||||
"files_migrated": [
|
||||
"src/modules/reports/views/*.vue (6 files)",
|
||||
"src/modules/reports/stores/*.js (5 files)",
|
||||
"src/modules/reports/components/** (dashboard cards, layout)",
|
||||
"src/modules/reports/utils/*.js",
|
||||
"src/modules/data-entry/views/receipts/*.vue (2 files)",
|
||||
"src/modules/data-entry/components/ocr/*.vue (3 files)",
|
||||
"src/modules/data-entry/stores/receiptsStore.js",
|
||||
"src/shared/components/** (LoginView, CompanySelector, PeriodSelector, AppHeader, SlideMenu)",
|
||||
"src/shared/stores/** (auth.js, companies.js, accountingPeriod.js)",
|
||||
"src/shared/styles/**",
|
||||
"src/assets/css/** (complete CSS system from reports-app)"
|
||||
],
|
||||
"summary": "Successfully consolidated Reports App and Data Entry App into single unified SPA. Implemented module isolation via error boundaries, dual API proxy configuration, and lazy loading. Build completed successfully with 1492 modules transformed, generating 12M dist with proper code splitting.",
|
||||
"build_stats": {
|
||||
"modules_transformed": 1492,
|
||||
"bundle_size": "12M",
|
||||
"chunks_generated": 63,
|
||||
"largest_chunks": [
|
||||
"vendor-export.js (704KB)",
|
||||
"vendor-primevue.js (524KB)",
|
||||
"vendor-charts.js (207KB)"
|
||||
]
|
||||
},
|
||||
"next_steps": [
|
||||
"Test dev server with npm run dev",
|
||||
"Update App.vue to use module-specific store instances",
|
||||
"Deploy dist/ to IIS and verify API proxies",
|
||||
"Test module switching and error boundary isolation",
|
||||
"Create unified-app README documentation"
|
||||
]
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
{
|
||||
"session_id": "20251224-001000-unified-app-ux",
|
||||
"timestamp": "2025-12-24T00:10:00Z",
|
||||
"feature": "unified-app-ux",
|
||||
"branch": "feature/ab-unified-app",
|
||||
"summary": "Fixed critical UX issues: period auto-selection after company change and empty hamburger menu display",
|
||||
"goals": [
|
||||
"Fix period dropdown auto-selection when company is manually selected",
|
||||
"Fix empty hamburger menu (no menu items visible)",
|
||||
"Ensure JWT token is sent in all API requests",
|
||||
"Resolve Pinia store timing issues"
|
||||
],
|
||||
"outcomes": {
|
||||
"success": true,
|
||||
"patterns_learned": 3,
|
||||
"gotchas_encountered": 4,
|
||||
"files_modified": 4,
|
||||
"tests_created": 3,
|
||||
"commits": 1
|
||||
},
|
||||
"key_files": [
|
||||
"src/App.vue",
|
||||
"src/shared/stores/accountingPeriod.js",
|
||||
"src/shared/stores/companies.js",
|
||||
"src/shared/components/layout/AppHeader.vue"
|
||||
],
|
||||
"commits": [
|
||||
{
|
||||
"hash": "287b9a9",
|
||||
"message": "fix: Implement complete auto-selection and fix hamburger menu display",
|
||||
"files_changed": 4,
|
||||
"insertions": 97,
|
||||
"deletions": 68
|
||||
}
|
||||
],
|
||||
"technical_insights": [
|
||||
"Vue watch() with { immediate: true } is essential for reactive data loading when dependent selections change",
|
||||
"Axios request interceptors must be configured AFTER creating instance but BEFORE making API calls",
|
||||
"Pinia store factories accessing other stores require lazy instantiation with try-catch to avoid timing issues",
|
||||
"Component data structure contracts must be honored - flatMap() broke nested section structure expected by SlideMenu",
|
||||
"Handler functions alone aren't enough for reactive updates - need watchers to trigger automatic data loading"
|
||||
],
|
||||
"problems_solved": [
|
||||
{
|
||||
"problem": "Period dropdown stayed on placeholder after company selection",
|
||||
"root_cause": "Missing Vue watcher to trigger loadPeriods() and missing JWT token in API requests",
|
||||
"solution": "Added watch() on companyStore.selectedCompany + axios request interceptor for JWT token",
|
||||
"files": ["src/App.vue"]
|
||||
},
|
||||
{
|
||||
"problem": "Hamburger menu completely empty (no menu items)",
|
||||
"root_cause": "enabledMenuItems used flatMap() creating flat array but SlideMenu expected nested sections",
|
||||
"solution": "Removed flatMap() and returned nested structure directly from getEnabledMenuSections()",
|
||||
"files": ["src/App.vue"]
|
||||
},
|
||||
{
|
||||
"problem": "TypeError: useAuthStore is not a function",
|
||||
"root_cause": "Period store tried to access stores before Pinia initialization",
|
||||
"solution": "Wrapped store access in try-catch with lazy instantiation in getStorageKey()",
|
||||
"files": ["src/shared/stores/accountingPeriod.js"]
|
||||
},
|
||||
{
|
||||
"problem": "API requests returning 500 errors",
|
||||
"root_cause": "No Authorization header with JWT token in requests",
|
||||
"solution": "Added axios request interceptor to inject Bearer token from localStorage",
|
||||
"files": ["src/App.vue"]
|
||||
}
|
||||
],
|
||||
"patterns_added": [
|
||||
"pat_20251224_001000 - Vue Watcher for Auto-Loading Dependent Data",
|
||||
"pat_20251224_001001 - Axios Request Interceptor for JWT Token Injection",
|
||||
"pat_20251224_001002 - Pinia Store Factory with Lazy Instantiation"
|
||||
],
|
||||
"gotchas_added": [
|
||||
"got_20251224_001000 - Menu Structure Mismatch: Flat Array vs Nested Sections",
|
||||
"got_20251224_001001 - TypeError: useAuthStore is not a function - Store Timing Issue",
|
||||
"got_20251224_001002 - Missing Auth Token in API Requests Causes 500 Errors",
|
||||
"got_20251224_001003 - Period Auto-Load Never Triggered Despite Handler Exists"
|
||||
],
|
||||
"testing": {
|
||||
"approach": "Playwright E2E tests",
|
||||
"tests_created": [
|
||||
"tests/e2e/unified-app/manual-company-select-test.spec.js - Verified period auto-selection",
|
||||
"tests/e2e/unified-app/hamburger-menu-visual-test.spec.js - Checked for white-on-white issues",
|
||||
"tests/e2e/unified-app/hamburger-menu-items-test.spec.js - Verified menu sections/items visible"
|
||||
],
|
||||
"all_tests_passed": true,
|
||||
"cleanup": "All test files deleted per user request after verification"
|
||||
},
|
||||
"user_feedback": [
|
||||
"dar si dupa ce aleg o firma MARIUS M AUTO, AR TREBUI SA SE SELECTEZE AUTOMAT ULTIMA LUNA, SI NU SE SELECTEAZA",
|
||||
"MENIUL HAMBURGER ARE IN CONTINUARE TEXT ALB PE FUNDAL ALB",
|
||||
"iata un screenshot cu meniul hamburger complet blank",
|
||||
"commit cu toate fix-urile",
|
||||
"sterge toate testele si rapoartele si screenshot-urile"
|
||||
],
|
||||
"lessons_learned": [
|
||||
"Always use Vue watchers for reactive data dependencies, not just event handlers",
|
||||
"Component props must match expected data structure - verify with component source",
|
||||
"Pinia stores must be lazily instantiated when accessing other stores to avoid timing issues",
|
||||
"Axios interceptors are critical for automatic JWT token injection - don't rely on manual headers",
|
||||
"Test automation helps verify fixes but manual testing with real scenarios catches UX issues"
|
||||
],
|
||||
"next_steps": [],
|
||||
"tags": ["vue", "pinia", "axios", "ux", "auto-selection", "authentication", "watchers", "stores"]
|
||||
}
|
||||
@@ -1,535 +0,0 @@
|
||||
# Feature: Ultrathin Monolith Backend Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
Transform ROA2WEB from 3 separate backend services (reports-app:8001, data-entry-app:8003, telegram-bot:8002) into a single unified backend process running on port 8000. This architectural transformation simplifies deployment, centralizes logs, and reduces operational complexity for solo developer workflow while maintaining clear module boundaries.
|
||||
|
||||
## Problem Statement
|
||||
|
||||
**Current Pain Points:**
|
||||
- Running 3 separate backend processes is complex to manage (start/stop/monitor)
|
||||
- Windows deployment requires 3 NSSM services, significantly increasing deployment time
|
||||
- Debugging is difficult with fragmented logs across multiple processes
|
||||
- Resource overhead of multiple Python processes and connection pools
|
||||
- Frontend already unified into single SPA, but backend remains fragmented
|
||||
|
||||
**Who Benefits:**
|
||||
- Solo developer: Easier development workflow, simplified debugging
|
||||
- Windows deployment: Single service instead of 3 (faster deployment)
|
||||
- System resources: Single Oracle pool, single process overhead
|
||||
|
||||
## User Stories
|
||||
|
||||
- As a developer, I want to start the entire backend with a single command (`python main.py`) so that I can begin working faster
|
||||
- As a developer, I want centralized logs in a single stream so that I can debug issues across all modules more easily
|
||||
- As a deployer, I want to install a single Windows service so that deployment takes less time
|
||||
- As a system administrator, I want a single backend process so that resource usage is more efficient
|
||||
- As a developer, I want to keep separate module directories (reports/, data-entry/, telegram-bot/) so that code organization remains clear
|
||||
|
||||
## Functional Requirements
|
||||
|
||||
### Core Requirements
|
||||
|
||||
1. **Single Entry Point**
|
||||
- One `main.py` file at project root (`/backend/main.py`)
|
||||
- Single FastAPI application instance
|
||||
- Single uvicorn process running on port 8000
|
||||
|
||||
2. **Module Prefix Routing**
|
||||
- Reports endpoints: `/api/reports/*` (e.g., `/api/reports/invoices`, `/api/reports/dashboard`)
|
||||
- Data Entry endpoints: `/api/data-entry/*` (e.g., `/api/data-entry/receipts`, `/api/data-entry/ocr`)
|
||||
- Telegram endpoints: `/api/telegram/*` (e.g., `/api/telegram/auth/verify-user`)
|
||||
- Shared endpoints: `/api/auth/*`, `/api/companies`, `/api/calendar` (no prefix change)
|
||||
|
||||
3. **Integrated Telegram Bot**
|
||||
- Telegram bot runs in same process (background thread/task)
|
||||
- Internal API endpoints accessible via `/api/telegram/*`
|
||||
- Shared database access (no separate SQLite for bot)
|
||||
|
||||
4. **Unified Startup/Shutdown**
|
||||
- Single lifespan manager initializing all modules
|
||||
- Parallel initialization where possible (Oracle pool + PaddleOCR)
|
||||
- Graceful shutdown for all components
|
||||
- Centralized logging configuration
|
||||
|
||||
5. **Module Directory Structure**
|
||||
- Keep existing directories: `backend/modules/reports/`, `backend/modules/data-entry/`, `backend/modules/telegram/`
|
||||
- Each module retains its own routers, services, models
|
||||
- Shared code remains in `shared/` (auth, database, routes)
|
||||
|
||||
### Secondary Requirements
|
||||
|
||||
1. **Environment Variable Consolidation**
|
||||
- Single `.env` file at `backend/.env`
|
||||
- Merge variables from all 3 backends
|
||||
- Namespace conflicts resolved (e.g., `REPORTS_*`, `DATA_ENTRY_*` prefixes)
|
||||
|
||||
2. **Single Virtual Environment**
|
||||
- Merged `requirements.txt` with all dependencies
|
||||
- De-duplicated dependencies (single version of FastAPI, etc.)
|
||||
- Optional dependency groups for OCR/Telegram
|
||||
|
||||
3. **Health Check Aggregation**
|
||||
- Single `/health` endpoint reporting status of all modules
|
||||
- Check Oracle connection, SQLite database, Telegram bot status
|
||||
- Return comprehensive health information
|
||||
|
||||
4. **Code Cleanup**
|
||||
- Remove duplicate auth router implementations (use shared router factory)
|
||||
- Remove duplicate company/calendar router implementations
|
||||
- Consolidate logging configuration
|
||||
- Remove unused imports and code
|
||||
|
||||
## Technical Requirements
|
||||
|
||||
### Files to Create
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `/backend/main.py` | Single unified entry point - creates FastAPI app, registers all module routers, manages lifecycle |
|
||||
| `/backend/config.py` | Unified configuration management (merges settings from all apps) |
|
||||
| `/backend/requirements.txt` | Consolidated dependencies from all 3 backends |
|
||||
| `/backend/.env.example` | Merged environment variables template |
|
||||
| `/backend/modules/__init__.py` | Module package marker |
|
||||
| `/backend/modules/reports/__init__.py` | Reports module package |
|
||||
| `/backend/modules/data-entry/__init__.py` | Data entry module package |
|
||||
| `/backend/modules/telegram/__init__.py` | Telegram module package |
|
||||
|
||||
### Files to Modify
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `reports-app/backend/app/routers/*.py` | Move to `backend/modules/reports/routers/` |
|
||||
| `reports-app/backend/app/services/*.py` | Move to `backend/modules/reports/services/` |
|
||||
| `reports-app/backend/app/models/*.py` | Move to `backend/modules/reports/models/` |
|
||||
| `reports-app/backend/app/cache/**/*.py` | Move to `backend/modules/reports/cache/` (reports-specific caching) |
|
||||
| `data-entry-app/backend/app/routers/*.py` | Move to `backend/modules/data-entry/routers/` |
|
||||
| `data-entry-app/backend/app/services/*.py` | Move to `backend/modules/data-entry/services/` |
|
||||
| `data-entry-app/backend/app/db/**/*.py` | Move to `backend/modules/data-entry/db/` |
|
||||
| `data-entry-app/backend/migrations/` | Move to `backend/modules/data-entry/migrations/` |
|
||||
| `reports-app/telegram-bot/app/bot/*.py` | Move to `backend/modules/telegram/bot/` |
|
||||
| `reports-app/telegram-bot/app/db/*.py` | Move to `backend/modules/telegram/db/` |
|
||||
| `reports-app/telegram-bot/app/internal_api.py` | Integrate into main FastAPI app as router |
|
||||
| All router files | Update imports to reflect new paths |
|
||||
| `vite.config.js` | Update proxy configuration to single backend (port 8000) |
|
||||
| `public/web.config` (IIS) | Update proxy rules to single backend (port 8000) |
|
||||
|
||||
### New Directory Structure
|
||||
|
||||
```
|
||||
roa2web/
|
||||
├── backend/ # UNIFIED BACKEND
|
||||
│ ├── main.py # Single entry point
|
||||
│ ├── config.py # Unified configuration
|
||||
│ ├── requirements.txt # Merged dependencies
|
||||
│ ├── .env # Merged environment variables
|
||||
│ ├── .env.example # Environment template
|
||||
│ ├── venv/ # Single virtual environment
|
||||
│ │
|
||||
│ ├── modules/ # Module-specific code
|
||||
│ │ ├── __init__.py
|
||||
│ │ │
|
||||
│ │ ├── reports/ # Reports module (from reports-app)
|
||||
│ │ │ ├── __init__.py
|
||||
│ │ │ ├── routers/ # API endpoints (invoices, dashboard, treasury, etc.)
|
||||
│ │ │ ├── services/ # Business logic
|
||||
│ │ │ ├── models/ # Pydantic models
|
||||
│ │ │ ├── schemas/ # Response schemas
|
||||
│ │ │ └── cache/ # Caching system (L1+L2)
|
||||
│ │ │
|
||||
│ │ ├── data-entry/ # Data entry module (from data-entry-app)
|
||||
│ │ │ ├── __init__.py
|
||||
│ │ │ ├── routers/ # Receipts, OCR, nomenclature routers
|
||||
│ │ │ ├── services/ # Business logic + OCR service
|
||||
│ │ │ ├── db/ # SQLModel models + CRUD
|
||||
│ │ │ ├── schemas/ # Pydantic schemas
|
||||
│ │ │ └── migrations/ # Alembic migrations
|
||||
│ │ │
|
||||
│ │ └── telegram/ # Telegram bot module (from telegram-bot)
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── bot/ # Command handlers, formatters
|
||||
│ │ ├── db/ # Bot-specific database (auth codes, sessions)
|
||||
│ │ └── routers/ # Internal API endpoints (auth code management)
|
||||
│ │
|
||||
│ └── data/ # Data files
|
||||
│ ├── cache/ # SQLite cache files (reports module)
|
||||
│ ├── receipts/ # SQLite DB + uploads (data-entry module)
|
||||
│ └── telegram/ # SQLite DB for bot (telegram module)
|
||||
│
|
||||
├── shared/ # Shared components (unchanged)
|
||||
│ ├── auth/ # JWT authentication
|
||||
│ ├── database/ # Oracle pool
|
||||
│ ├── routes/ # Shared router factories
|
||||
│ └── frontend/ # Shared Vue components
|
||||
│
|
||||
├── frontend/ # Unified frontend (unchanged structure)
|
||||
│ ├── src/
|
||||
│ │ ├── modules/
|
||||
│ │ │ ├── reports/
|
||||
│ │ │ └── data-entry/
|
||||
│ │ └── shared/
|
||||
│ └── vite.config.js # UPDATE: Single proxy to localhost:8000
|
||||
│
|
||||
├── deployment/ # Deployment scripts
|
||||
│ └── windows/ # Windows deployment
|
||||
│ └── install-service.ps1 # UPDATE: Single NSSM service
|
||||
│
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
### Dependencies Consolidation
|
||||
|
||||
**Common Dependencies** (deduplicated):
|
||||
```
|
||||
fastapi>=0.109.0
|
||||
uvicorn[standard]>=0.27.0
|
||||
pydantic>=2.5.3
|
||||
python-dotenv>=1.0.0
|
||||
PyJWT>=2.8.0
|
||||
python-jose[cryptography]>=3.3.0
|
||||
oracledb>=2.0.1
|
||||
aiosqlite>=0.19.0
|
||||
httpx>=0.27.0
|
||||
python-multipart>=0.0.6
|
||||
```
|
||||
|
||||
**Reports-Specific**:
|
||||
```
|
||||
openpyxl>=3.1.0 # Excel export
|
||||
fpdf2>=2.7.0 # PDF generation
|
||||
python-dateutil>=2.8.2 # Date utilities
|
||||
```
|
||||
|
||||
**Data Entry-Specific**:
|
||||
```
|
||||
sqlmodel>=0.0.14 # ORM
|
||||
alembic>=1.13.1 # Migrations
|
||||
aiofiles>=23.2.1 # Async file handling
|
||||
Pillow>=10.2.0 # Image processing
|
||||
paddleocr>=2.7.0 # OCR engine
|
||||
paddlepaddle>=2.5.0 # PaddleOCR backend
|
||||
opencv-python>=4.8.0 # Image processing
|
||||
pytesseract>=0.3.10 # Tesseract OCR
|
||||
pdf2image>=1.16.0 # PDF to image conversion
|
||||
numpy>=1.24.0 # Array operations
|
||||
```
|
||||
|
||||
**Telegram-Specific**:
|
||||
```
|
||||
python-telegram-bot>=20.7 # Telegram bot SDK
|
||||
aiosmtplib>=3.0.0 # Email (2FA)
|
||||
sentry-sdk>=1.40.0 # Monitoring (optional)
|
||||
```
|
||||
|
||||
### Database Changes
|
||||
|
||||
**No schema changes** - databases remain separate:
|
||||
- Oracle: Shared via `oracle_pool` (reports + data-entry nomenclatures + auth)
|
||||
- SQLite (`receipts.db`): Data entry module receipts data
|
||||
- SQLite (`cache.db`): Reports module caching
|
||||
- SQLite (`telegram.db`): Telegram bot auth codes and sessions
|
||||
|
||||
**Database Access Patterns**:
|
||||
- Oracle Pool: Single shared instance initialized at startup, used by all modules
|
||||
- SQLite databases: Separate per module, stored in `backend/data/`
|
||||
|
||||
### API Changes
|
||||
|
||||
**URL Migration** (frontend must update):
|
||||
|
||||
| Old URL | New URL | Module |
|
||||
|---------|---------|--------|
|
||||
| `http://localhost:8001/api/invoices` | `http://localhost:8000/api/reports/invoices` | Reports |
|
||||
| `http://localhost:8001/api/dashboard` | `http://localhost:8000/api/reports/dashboard` | Reports |
|
||||
| `http://localhost:8001/api/treasury` | `http://localhost:8000/api/reports/treasury` | Reports |
|
||||
| `http://localhost:8001/api/trial-balance` | `http://localhost:8000/api/reports/trial-balance` | Reports |
|
||||
| `http://localhost:8001/api/cache/*` | `http://localhost:8000/api/reports/cache/*` | Reports |
|
||||
| `http://localhost:8003/api/receipts` | `http://localhost:8000/api/data-entry/receipts` | Data Entry |
|
||||
| `http://localhost:8003/api/ocr` | `http://localhost:8000/api/data-entry/ocr` | Data Entry |
|
||||
| `http://localhost:8003/api/nomenclature` | `http://localhost:8000/api/data-entry/nomenclature` | Data Entry |
|
||||
| `http://localhost:8002/api/telegram/*` | `http://localhost:8000/api/telegram/*` | Telegram |
|
||||
| `http://localhost:8001/api/auth/login` | `http://localhost:8000/api/auth/login` | Shared (no change) |
|
||||
| `http://localhost:8001/api/companies` | `http://localhost:8000/api/companies` | Shared (no change) |
|
||||
| `http://localhost:8001/api/calendar` | `http://localhost:8000/api/calendar` | Shared (no change) |
|
||||
|
||||
**Frontend Configuration Updates**:
|
||||
|
||||
```javascript
|
||||
// vite.config.js - BEFORE (3 proxies)
|
||||
proxy: {
|
||||
'/api/reports': { target: 'http://localhost:8001', rewrite: (path) => path.replace(/^\/api\/reports/, '/api') },
|
||||
'/api/data-entry': { target: 'http://localhost:8003', rewrite: (path) => path.replace(/^\/api\/data-entry/, '/api') },
|
||||
'/api/telegram': { target: 'http://localhost:8002', rewrite: (path) => path.replace(/^\/api\/telegram/, '/api') }
|
||||
}
|
||||
|
||||
// vite.config.js - AFTER (1 proxy)
|
||||
proxy: {
|
||||
'/api': { target: 'http://localhost:8000', changeOrigin: true }
|
||||
}
|
||||
```
|
||||
|
||||
```xml
|
||||
<!-- public/web.config - BEFORE (3 rules) -->
|
||||
<rule name="Proxy Reports API" stopProcessing="true">
|
||||
<match url="^api/reports/(.*)" />
|
||||
<action type="Rewrite" url="http://localhost:8001/api/{R:1}" />
|
||||
</rule>
|
||||
<rule name="Proxy Data Entry API" stopProcessing="true">
|
||||
<match url="^api/data-entry/(.*)" />
|
||||
<action type="Rewrite" url="http://localhost:8003/api/{R:1}" />
|
||||
</rule>
|
||||
<rule name="Proxy Telegram API" stopProcessing="true">
|
||||
<match url="^api/telegram/(.*)" />
|
||||
<action type="Rewrite" url="http://localhost:8002/api/{R:1}" />
|
||||
</rule>
|
||||
|
||||
<!-- public/web.config - AFTER (1 rule) -->
|
||||
<rule name="Proxy Backend API" stopProcessing="true">
|
||||
<match url="^api/(.*)" />
|
||||
<action type="Rewrite" url="http://localhost:8000/api/{R:1}" />
|
||||
</rule>
|
||||
```
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### Approach: Module-Based Monolith with Clear Boundaries
|
||||
|
||||
**Why This Approach:**
|
||||
1. **Maintains Module Isolation**: Each module (reports, data-entry, telegram) keeps its own directory structure
|
||||
2. **Simplifies Deployment**: Single process, single service, single log stream
|
||||
3. **Preserves Code Organization**: Module boundaries remain clear in file structure
|
||||
4. **Enables Gradual Migration**: Can move one module at a time
|
||||
5. **Reduces Complexity**: Solo developer doesn't need microservices orchestration
|
||||
|
||||
**Key Architectural Patterns:**
|
||||
- **Single FastAPI App**: One application instance with multiple routers registered with prefixes
|
||||
- **Module Routers**: Each module exports a function that returns its configured router
|
||||
- **Parallel Initialization**: Oracle pool and PaddleOCR load concurrently at startup
|
||||
- **Unified Logging**: Single logging configuration with module-specific loggers
|
||||
- **Shared Singleton**: Oracle pool shared across all modules
|
||||
|
||||
### Alternatives Considered
|
||||
|
||||
**Alternative 1: Keep Microservices Architecture**
|
||||
- **Rejected**: Adds complexity without benefits for solo developer
|
||||
- **Rejected**: Windows deployment remains slow (3 services)
|
||||
- **Rejected**: Debugging remains difficult (fragmented logs)
|
||||
|
||||
**Alternative 2: Merge Everything into Flat Structure**
|
||||
- **Rejected**: Loses module boundaries, code becomes harder to navigate
|
||||
- **Rejected**: Difficult to understand which code belongs to which feature
|
||||
|
||||
**Alternative 3: Use Docker Compose for Orchestration**
|
||||
- **Rejected**: Adds Docker overhead without solving core issues
|
||||
- **Rejected**: Windows deployment still needs Docker Desktop or manual setup
|
||||
- **Rejected**: Doesn't simplify solo developer workflow
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Single `python backend/main.py` command starts entire backend
|
||||
- [ ] All endpoints accessible on port 8000 with module prefixes (`/api/reports/*`, `/api/data-entry/*`, `/api/telegram/*`)
|
||||
- [ ] Telegram bot runs in same process (background task), receives/sends messages
|
||||
- [ ] Oracle pool initialized once, shared across all modules
|
||||
- [ ] PaddleOCR loads in parallel with Oracle pool at startup (non-blocking)
|
||||
- [ ] All logs appear in single console stream with module prefixes
|
||||
- [ ] `/health` endpoint returns comprehensive status (Oracle, SQLite, Telegram bot)
|
||||
- [ ] Frontend can access all functionality through unified backend
|
||||
- [ ] Windows deployment requires only 1 NSSM service (not 3)
|
||||
- [ ] All existing tests pass after migration
|
||||
- [ ] Startup script simplified to single command (no parallel process management)
|
||||
- [ ] Development mode: `uvicorn backend.main:app --reload` auto-reloads on code changes
|
||||
- [ ] Module directories clearly separated (`modules/reports/`, `modules/data-entry/`, `modules/telegram/`)
|
||||
|
||||
## Out of Scope
|
||||
|
||||
**Explicitly NOT Included in This Transformation:**
|
||||
|
||||
- ❌ Frontend code changes (only configuration updates: vite.config.js, web.config)
|
||||
- ❌ Business logic modifications (pure architectural refactoring)
|
||||
- ❌ Database schema changes (no migrations needed)
|
||||
- ❌ Authentication system changes (JWT logic stays same)
|
||||
- ❌ API contract changes (same endpoints, just different base URL)
|
||||
- ❌ Oracle database migration (stays on Oracle)
|
||||
- ❌ SQLite schema changes (data-entry and telegram databases unchanged)
|
||||
- ❌ UI/UX modifications (frontend functionality stays identical)
|
||||
- ❌ Performance optimizations (beyond parallel initialization)
|
||||
- ❌ New features (pure consolidation work)
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| Import path issues after moving files | High | Medium | Use absolute imports with module prefixes (`from backend.modules.reports.services import ...`), update all imports systematically |
|
||||
| Oracle pool initialization delays startup | Medium | Low | Use parallel initialization (Oracle + PaddleOCR concurrently), add timeout handling |
|
||||
| Telegram bot blocks main thread | Medium | High | Run bot in background thread with `asyncio.create_task()` or `threading.Thread(daemon=True)` |
|
||||
| PaddleOCR memory usage in same process | Low | Medium | Keep background initialization, monitor memory usage, add configuration to disable OCR if needed |
|
||||
| Port 8000 conflicts with existing service | Low | Low | Make port configurable via environment variable, document port change |
|
||||
| Frontend cache issues with URL changes | Medium | Low | Clear browser cache, document URL changes, use cache-busting headers |
|
||||
| Windows service migration breaks existing deployment | Medium | High | Create new service name (`ROA2WEB-Unified`), test thoroughly before removing old services, document rollback procedure |
|
||||
| Circular imports between modules | Low | Medium | Keep module dependencies one-way (reports/data-entry/telegram should not import each other), use shared code for common functionality |
|
||||
| Environment variable conflicts | Low | Medium | Use namespaced prefixes (`REPORTS_*`, `DATA_ENTRY_*`, `TELEGRAM_*`), document all variables in `.env.example` |
|
||||
| Lost logs during migration | Low | High | Keep old logs in archive, test logging thoroughly in dev environment first |
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Telegram Bot Threading**: Should we use `asyncio.create_task()` (async) or `threading.Thread()` (sync) for Telegram bot polling?
|
||||
- **Recommendation**: Use `asyncio.create_task()` to stay in async context, avoid GIL issues
|
||||
|
||||
2. **Module Import Strategy**: Should modules import from each other, or only through shared code?
|
||||
- **Recommendation**: NO cross-module imports (reports ↔ data-entry ↔ telegram), only via `shared/`
|
||||
|
||||
3. **Health Check Depth**: How detailed should `/health` endpoint be (simple up/down vs full diagnostics)?
|
||||
- **Recommendation**: Return dict with per-module status, keep fast (<500ms)
|
||||
|
||||
4. **Backward Compatibility**: Should we support old URLs temporarily (redirect 8001→8000)?
|
||||
- **Recommendation**: NO - clean break, update frontend configuration once
|
||||
|
||||
5. **Development vs Production Config**: Should we have separate startup modes?
|
||||
- **Recommendation**: Use environment variables (`ENV=development|production`), single entry point
|
||||
|
||||
6. **Migration Strategy**: Big bang (all at once) or incremental (one module at a time)?
|
||||
- **Recommendation**: Big bang - easier to test, cleaner migration, no production deployment yet
|
||||
|
||||
7. **Logging Format**: Should we prefix all log messages with module name `[REPORTS]`, `[DATA-ENTRY]`, `[TELEGRAM]`?
|
||||
- **Recommendation**: YES - use Python logging with module-specific loggers
|
||||
|
||||
## Estimated Complexity
|
||||
|
||||
**HIGH** - Major architectural transformation requiring:
|
||||
|
||||
**Justification:**
|
||||
- Moving and reorganizing ~50+ files across 3 backends
|
||||
- Updating ~200+ import statements
|
||||
- Merging 3 separate lifecycle managers into one
|
||||
- Integrating Telegram bot from separate process into main app
|
||||
- Testing all functionality after migration
|
||||
- Updating deployment scripts and documentation
|
||||
- Ensuring no regressions across all modules
|
||||
|
||||
**Estimated Effort:**
|
||||
- File reorganization: 4-6 hours
|
||||
- Import path updates: 3-4 hours
|
||||
- Unified main.py + lifecycle: 3-4 hours
|
||||
- Telegram bot integration: 2-3 hours
|
||||
- Testing (manual + automated): 4-6 hours
|
||||
- Frontend configuration updates: 1-2 hours
|
||||
- Deployment script updates: 2-3 hours
|
||||
- Documentation updates: 2-3 hours
|
||||
- **Total: 21-31 hours (~3-4 working days)**
|
||||
|
||||
**Recommended Approach:**
|
||||
1. Create new structure, copy files (don't delete old ones yet)
|
||||
2. Update imports module by module
|
||||
3. Test each module independently
|
||||
4. Integrate Telegram bot last
|
||||
5. Full integration testing
|
||||
6. Update frontend configuration
|
||||
7. Archive old backend directories after verification
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Startup Sequence
|
||||
|
||||
```python
|
||||
# backend/main.py - Startup lifespan
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Unified startup/shutdown for all modules"""
|
||||
|
||||
# 1. Initialize logging
|
||||
setup_logging()
|
||||
|
||||
# 2. Parallel initialization (Oracle + PaddleOCR)
|
||||
await asyncio.gather(
|
||||
init_oracle_pool(),
|
||||
init_paddle_ocr(), # Background initialization
|
||||
init_data_entry_db(),
|
||||
init_telegram_db()
|
||||
)
|
||||
|
||||
# 3. Initialize module-specific components
|
||||
await init_reports_cache()
|
||||
|
||||
# 4. Start Telegram bot (background task)
|
||||
telegram_task = asyncio.create_task(run_telegram_bot())
|
||||
|
||||
logger.info("🚀 ROA2WEB Unified Backend started on port 8000")
|
||||
|
||||
yield
|
||||
|
||||
# Shutdown
|
||||
telegram_task.cancel()
|
||||
await close_all_resources()
|
||||
```
|
||||
|
||||
### Router Registration Pattern
|
||||
|
||||
```python
|
||||
# backend/main.py - Router registration
|
||||
from modules.reports.routers import create_reports_router
|
||||
from modules.data_entry.routers import create_data_entry_router
|
||||
from modules.telegram.routers import create_telegram_router
|
||||
|
||||
# Register module routers with prefixes
|
||||
app.include_router(create_reports_router(), prefix="/api/reports", tags=["reports"])
|
||||
app.include_router(create_data_entry_router(), prefix="/api/data-entry", tags=["data-entry"])
|
||||
app.include_router(create_telegram_router(), prefix="/api/telegram", tags=["telegram"])
|
||||
|
||||
# Register shared routers (no prefix)
|
||||
app.include_router(auth_router, prefix="/api/auth", tags=["auth"])
|
||||
app.include_router(companies_router, prefix="/api/companies", tags=["companies"])
|
||||
app.include_router(calendar_router, prefix="/api/calendar", tags=["calendar"])
|
||||
```
|
||||
|
||||
### Module Router Factory Pattern
|
||||
|
||||
```python
|
||||
# backend/modules/reports/routers/__init__.py
|
||||
from fastapi import APIRouter
|
||||
from .invoices import router as invoices_router
|
||||
from .dashboard import router as dashboard_router
|
||||
from .treasury import router as treasury_router
|
||||
# ... other routers
|
||||
|
||||
def create_reports_router() -> APIRouter:
|
||||
"""Create and configure reports module router"""
|
||||
router = APIRouter()
|
||||
|
||||
# Include all sub-routers (no prefix - already in main.py)
|
||||
router.include_router(invoices_router, prefix="/invoices")
|
||||
router.include_router(dashboard_router, prefix="/dashboard")
|
||||
router.include_router(treasury_router, prefix="/treasury")
|
||||
# ... other routers
|
||||
|
||||
return router
|
||||
```
|
||||
|
||||
### Critical Files List
|
||||
|
||||
**Top 10 Files That Will Be Affected:**
|
||||
|
||||
1. `/backend/main.py` - **NEW** - Single entry point, replaces 3 main.py files
|
||||
2. `/backend/config.py` - **NEW** - Unified configuration
|
||||
3. `/frontend/vite.config.js` - Update proxy configuration
|
||||
4. `/frontend/public/web.config` - Update IIS rewrite rules
|
||||
5. `reports-app/backend/app/main.py` - Move logic to unified main.py
|
||||
6. `data-entry-app/backend/app/main.py` - Move logic to unified main.py
|
||||
7. `reports-app/telegram-bot/app/main.py` - Move logic to unified main.py
|
||||
8. `shared/database/oracle_pool.py` - Ensure singleton pattern for shared pool
|
||||
9. `/deployment/windows/install-service.ps1` - Update to install single service
|
||||
10. `/start-dev.sh` - Simplify to start single backend process
|
||||
|
||||
**Additional Critical Files:**
|
||||
- All router files in `reports-app/backend/app/routers/` (~10 files)
|
||||
- All router files in `data-entry-app/backend/app/routers/` (~4 files)
|
||||
- Telegram bot handlers in `reports-app/telegram-bot/app/bot/handlers.py`
|
||||
- Cache system in `reports-app/backend/app/cache/` (~9 files)
|
||||
- OCR service in `data-entry-app/backend/app/services/ocr_service.py`
|
||||
- Database models in `data-entry-app/backend/app/db/`
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Created**: 2025-12-24
|
||||
**Author**: Auto-Build System
|
||||
**Status**: Draft - Ready for Review
|
||||
@@ -1,121 +0,0 @@
|
||||
{
|
||||
"feature": "ultrathin-monolith",
|
||||
"status": "spec_created",
|
||||
"created_at": "2025-12-24T00:00:00Z",
|
||||
"updated_at": "2025-12-24T00:00:00Z",
|
||||
"spec_version": "1.0",
|
||||
"complexity": "high",
|
||||
"estimated_hours": "21-31",
|
||||
"estimated_days": "3-4",
|
||||
"key_changes": [
|
||||
"Single backend entry point (backend/main.py) replacing 3 separate main.py files",
|
||||
"Module-based directory structure (backend/modules/reports, backend/modules/data-entry, backend/modules/telegram)",
|
||||
"API URL changes with module prefixes (/api/reports/*, /api/data-entry/*, /api/telegram/*)",
|
||||
"Telegram bot integrated into main process (background task)",
|
||||
"Parallel initialization (Oracle pool + PaddleOCR)",
|
||||
"Unified logging with module prefixes",
|
||||
"Single virtual environment with merged dependencies",
|
||||
"Single Windows service for deployment (instead of 3)"
|
||||
],
|
||||
"modules_affected": [
|
||||
"reports-app/backend",
|
||||
"data-entry-app/backend",
|
||||
"reports-app/telegram-bot",
|
||||
"frontend (vite.config.js, web.config)",
|
||||
"shared/database",
|
||||
"shared/auth",
|
||||
"deployment/windows"
|
||||
],
|
||||
"files_to_create": [
|
||||
"/backend/main.py",
|
||||
"/backend/config.py",
|
||||
"/backend/requirements.txt",
|
||||
"/backend/.env.example",
|
||||
"/backend/modules/__init__.py",
|
||||
"/backend/modules/reports/__init__.py",
|
||||
"/backend/modules/data-entry/__init__.py",
|
||||
"/backend/modules/telegram/__init__.py"
|
||||
],
|
||||
"files_to_move": [
|
||||
"reports-app/backend/app/routers/* -> backend/modules/reports/routers/",
|
||||
"reports-app/backend/app/services/* -> backend/modules/reports/services/",
|
||||
"reports-app/backend/app/models/* -> backend/modules/reports/models/",
|
||||
"reports-app/backend/app/cache/* -> backend/modules/reports/cache/",
|
||||
"data-entry-app/backend/app/routers/* -> backend/modules/data-entry/routers/",
|
||||
"data-entry-app/backend/app/services/* -> backend/modules/data-entry/services/",
|
||||
"data-entry-app/backend/app/db/* -> backend/modules/data-entry/db/",
|
||||
"data-entry-app/backend/migrations/* -> backend/modules/data-entry/migrations/",
|
||||
"reports-app/telegram-bot/app/bot/* -> backend/modules/telegram/bot/",
|
||||
"reports-app/telegram-bot/app/db/* -> backend/modules/telegram/db/"
|
||||
],
|
||||
"critical_files": [
|
||||
"/backend/main.py (NEW - single entry point)",
|
||||
"/backend/config.py (NEW - unified configuration)",
|
||||
"/frontend/vite.config.js (UPDATE - single proxy)",
|
||||
"/frontend/public/web.config (UPDATE - single IIS rule)",
|
||||
"reports-app/backend/app/main.py (MIGRATE to unified)",
|
||||
"data-entry-app/backend/app/main.py (MIGRATE to unified)",
|
||||
"reports-app/telegram-bot/app/main.py (MIGRATE to unified)",
|
||||
"shared/database/oracle_pool.py (VERIFY singleton)",
|
||||
"/deployment/windows/install-service.ps1 (UPDATE single service)",
|
||||
"/start-dev.sh (SIMPLIFY single process)"
|
||||
],
|
||||
"risks": [
|
||||
{
|
||||
"risk": "Import path issues after moving files",
|
||||
"likelihood": "high",
|
||||
"impact": "medium",
|
||||
"mitigation": "Use absolute imports, update systematically"
|
||||
},
|
||||
{
|
||||
"risk": "Telegram bot blocks main thread",
|
||||
"likelihood": "medium",
|
||||
"impact": "high",
|
||||
"mitigation": "Run in background task with asyncio.create_task()"
|
||||
},
|
||||
{
|
||||
"risk": "Windows service migration breaks deployment",
|
||||
"likelihood": "medium",
|
||||
"impact": "high",
|
||||
"mitigation": "Create new service, test thoroughly, document rollback"
|
||||
},
|
||||
{
|
||||
"risk": "Frontend cache issues with URL changes",
|
||||
"likelihood": "medium",
|
||||
"impact": "low",
|
||||
"mitigation": "Clear browser cache, cache-busting headers"
|
||||
}
|
||||
],
|
||||
"acceptance_criteria_count": 14,
|
||||
"out_of_scope_count": 10,
|
||||
"dependencies": [
|
||||
"Frontend unified app (already completed)",
|
||||
"SSH tunnel (unchanged)",
|
||||
"Oracle database (unchanged)",
|
||||
"Shared modules (auth, database, routes) - unchanged"
|
||||
],
|
||||
"testing_strategy": [
|
||||
"Module-by-module verification during migration",
|
||||
"Integration testing after all modules migrated",
|
||||
"Frontend connectivity testing",
|
||||
"Telegram bot functionality testing",
|
||||
"Performance testing (startup time, memory usage)",
|
||||
"Windows deployment testing"
|
||||
],
|
||||
"migration_approach": "big_bang",
|
||||
"migration_notes": "Create new structure alongside old, copy files, update imports, test thoroughly, then archive old directories after verification",
|
||||
"next_steps": [
|
||||
"Review and approve specification",
|
||||
"Create backend/ directory structure",
|
||||
"Copy files to new structure (keep old as backup)",
|
||||
"Update imports module by module",
|
||||
"Create unified main.py",
|
||||
"Test module routers independently",
|
||||
"Integrate Telegram bot",
|
||||
"Update frontend configuration",
|
||||
"Full integration testing",
|
||||
"Update deployment scripts",
|
||||
"Update documentation",
|
||||
"Archive old backend directories"
|
||||
]
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
# Unified App Migration Checklist
|
||||
|
||||
**Track your progress** as you implement the unified app.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Project Setup (0.5 days)
|
||||
|
||||
### Directory Structure
|
||||
- [ ] Create directory structure in root (`/mnt/e/proiecte/roa2web/`)
|
||||
- [ ] Create `src/` subdirectories:
|
||||
- [ ] `src/modules/reports/views/`
|
||||
- [ ] `src/modules/reports/stores/`
|
||||
- [ ] `src/modules/reports/services/`
|
||||
- [ ] `src/modules/data-entry/views/receipts/`
|
||||
- [ ] `src/modules/data-entry/components/ocr/`
|
||||
- [ ] `src/modules/data-entry/stores/`
|
||||
- [ ] `src/modules/data-entry/services/`
|
||||
- [ ] `src/shared/components/`
|
||||
- [ ] `src/shared/stores/`
|
||||
- [ ] `src/shared/styles/`
|
||||
- [ ] `src/config/`
|
||||
- [ ] `src/router/`
|
||||
- [ ] `src/assets/css/`
|
||||
|
||||
### Core Configuration Files
|
||||
- [ ] Create `package.json` (merge dependencies from both apps)
|
||||
- [ ] Create `vite.config.js` (dual proxy + lazy loading)
|
||||
- [ ] Create `src/main.js` (PrimeVue setup)
|
||||
- [ ] Create `.env.example` (environment variables)
|
||||
- [ ] Create `.gitignore`
|
||||
- [ ] Create `README.md`
|
||||
|
||||
### Copy Shared Resources
|
||||
- [ ] Copy `shared/frontend/components/` → `src/shared/components/`
|
||||
- [ ] Copy `shared/frontend/stores/` → `src/shared/stores/`
|
||||
- [ ] Copy `shared/frontend/styles/` → `src/shared/styles/`
|
||||
- [ ] Copy `reports-app/frontend/src/assets/css/` → `src/assets/css/`
|
||||
|
||||
### Verification
|
||||
- [ ] Run `npm install` - succeeds
|
||||
- [ ] Run `npm run dev` - starts on port 3000
|
||||
- [ ] No console errors
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Module Migration (1 day)
|
||||
|
||||
### Reports Module - Views
|
||||
- [ ] Copy `DashboardView.vue` → `src/modules/reports/views/`
|
||||
- [ ] Copy `InvoicesView.vue` → `src/modules/reports/views/`
|
||||
- [ ] Copy `BankCashRegisterView.vue` → `src/modules/reports/views/`
|
||||
- [ ] Copy `TrialBalanceView.vue` → `src/modules/reports/views/`
|
||||
- [ ] Copy `TelegramView.vue` → `src/modules/reports/views/`
|
||||
- [ ] Copy `CacheStatsView.vue` → `src/modules/reports/views/`
|
||||
- [ ] Update imports in all views (if needed)
|
||||
|
||||
### Reports Module - Stores
|
||||
- [ ] Copy `dashboard.js` → `src/modules/reports/stores/`
|
||||
- [ ] Copy `invoices.js` → `src/modules/reports/stores/`
|
||||
- [ ] Copy `treasury.js` → `src/modules/reports/stores/`
|
||||
- [ ] Copy `trialBalance.js` → `src/modules/reports/stores/`
|
||||
- [ ] Copy `cacheStore.js` → `src/modules/reports/stores/`
|
||||
- [ ] Update imports in all stores (if needed)
|
||||
|
||||
### Reports Module - Services
|
||||
- [ ] Copy `api.js` → `src/modules/reports/services/` (in root)
|
||||
- [ ] Update base URL to `/api/reports/` in api.js
|
||||
- [ ] Test API calls route to localhost:8001
|
||||
|
||||
### Data Entry Module - Views
|
||||
- [ ] Copy `ReceiptsListView.vue` → `src/modules/data-entry/views/receipts/`
|
||||
- [ ] Copy `ReceiptCreateView.vue` → `src/modules/data-entry/views/receipts/`
|
||||
- [ ] Update imports in views (if needed)
|
||||
|
||||
### Data Entry Module - Components
|
||||
- [ ] Copy `OCRUploadZone.vue` → `src/modules/data-entry/components/ocr/`
|
||||
- [ ] Copy `OCRPreview.vue` → `src/modules/data-entry/components/ocr/`
|
||||
- [ ] Copy `OCRConfidenceIndicator.vue` → `src/modules/data-entry/components/ocr/`
|
||||
- [ ] Update imports in components (if needed)
|
||||
|
||||
### Data Entry Module - Stores
|
||||
- [ ] Copy `receiptsStore.js` → `src/modules/data-entry/stores/`
|
||||
- [ ] Update imports in store (if needed)
|
||||
|
||||
### Data Entry Module - Services
|
||||
- [ ] Copy `api.js` → `src/modules/data-entry/services/` (in root)
|
||||
- [ ] Update base URL to `/api/data-entry/` in api.js
|
||||
- [ ] Test API calls route to localhost:8003
|
||||
|
||||
### CSS Merge
|
||||
- [ ] Review `data-entry-app/frontend/src/assets/css/main.css`
|
||||
- [ ] Merge unique styles into `src/assets/css/` (in root)
|
||||
- [ ] Resolve any style conflicts
|
||||
- [ ] Test responsive design
|
||||
|
||||
### Verification
|
||||
- [ ] All Reports views render without errors
|
||||
- [ ] All Data Entry views render without errors
|
||||
- [ ] No import errors in console
|
||||
- [ ] CSS loads correctly
|
||||
- [ ] No style conflicts
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Routing & Navigation (0.5 days)
|
||||
|
||||
### Router Configuration
|
||||
- [ ] Create `src/router/index.js` (in root)
|
||||
- [ ] Add `/login` route (eager loaded)
|
||||
- [ ] Add `/reports/*` routes (lazy loaded)
|
||||
- [ ] Add `/data-entry/*` routes (lazy loaded)
|
||||
- [ ] Add redirect `/` → `/reports/dashboard`
|
||||
- [ ] Add authentication guard
|
||||
- [ ] Add 404 handling
|
||||
- [ ] Test all routes work
|
||||
|
||||
### Menu Configuration
|
||||
- [ ] Create `src/config/menu.js` (in root)
|
||||
- [ ] Define "Rapoarte" section
|
||||
- [ ] Define "Introduceri Date" section
|
||||
- [ ] Define "Sistem" section
|
||||
- [ ] Export menu configuration
|
||||
|
||||
### Feature Flags
|
||||
- [ ] Create `src/config/features.js` (in root)
|
||||
- [ ] Add reports.enabled flag
|
||||
- [ ] Add dataEntry.enabled flag
|
||||
- [ ] Add module-level flags
|
||||
- [ ] Export isFeatureEnabled function
|
||||
|
||||
### Root Component
|
||||
- [ ] Create `src/App.vue` (in root)
|
||||
- [ ] Integrate AppHeader with unified menu
|
||||
- [ ] Integrate SlideMenu with menu sections
|
||||
- [ ] Add router-view
|
||||
- [ ] Add Toast and ConfirmDialog
|
||||
- [ ] Test menu navigation
|
||||
|
||||
### Verification
|
||||
- [ ] Can navigate to all routes
|
||||
- [ ] Menu highlights active route
|
||||
- [ ] Authentication guard works
|
||||
- [ ] Login redirects to dashboard
|
||||
- [ ] 404 redirects work
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Error Boundaries & Resilience (0.25 days)
|
||||
|
||||
### Error Boundary Component
|
||||
- [ ] Create `src/shared/components/ErrorBoundary.vue` (in root)
|
||||
- [ ] Implement onErrorCaptured hook
|
||||
- [ ] Add user-friendly error display
|
||||
- [ ] Add retry functionality
|
||||
- [ ] Add navigate away option
|
||||
- [ ] Test with intentional error
|
||||
|
||||
### Module Layouts
|
||||
- [ ] Create `src/modules/reports/ReportsLayout.vue` (in root)
|
||||
- [ ] Wrap with ErrorBoundary component
|
||||
- [ ] Create `src/modules/data-entry/DataEntryLayout.vue` (in root)
|
||||
- [ ] Wrap with ErrorBoundary component
|
||||
|
||||
### Error Isolation Testing
|
||||
- [ ] Introduce error in Reports module
|
||||
- [ ] Verify Data Entry still works
|
||||
- [ ] Introduce error in Data Entry module
|
||||
- [ ] Verify Reports still works
|
||||
- [ ] Test retry functionality
|
||||
- [ ] Test navigate away functionality
|
||||
|
||||
### Feature Flags Testing
|
||||
- [ ] Disable Reports module
|
||||
- [ ] Verify menu items hidden
|
||||
- [ ] Disable Data Entry module
|
||||
- [ ] Verify menu items hidden
|
||||
- [ ] Re-enable all modules
|
||||
|
||||
### Loading States
|
||||
- [ ] Add loading spinner for lazy routes
|
||||
- [ ] Test module switching loading
|
||||
- [ ] Add skeleton screens (optional)
|
||||
|
||||
### Verification
|
||||
- [ ] Error boundary catches component errors
|
||||
- [ ] User sees friendly error message
|
||||
- [ ] Can retry or navigate away
|
||||
- [ ] Module isolation works (error in one doesn't crash other)
|
||||
- [ ] Feature flags work
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Build & Deploy (0.25 days)
|
||||
|
||||
### Production Build
|
||||
- [ ] Run `npm run build`
|
||||
- [ ] Build succeeds without errors
|
||||
- [ ] Check `dist/` output
|
||||
- [ ] Verify chunks created:
|
||||
- [ ] vendor-core.[hash].js
|
||||
- [ ] vendor-primevue.[hash].js
|
||||
- [ ] vendor-utils.[hash].js
|
||||
- [ ] reports.[hash].js (lazy)
|
||||
- [ ] data-entry.[hash].js (lazy)
|
||||
- [ ] main.[hash].js
|
||||
- [ ] main.[hash].css
|
||||
|
||||
### Bundle Analysis
|
||||
- [ ] Install `rollup-plugin-visualizer`
|
||||
- [ ] Analyze bundle sizes
|
||||
- [ ] Verify total size ≤ sum of current apps
|
||||
- [ ] Check for duplicate dependencies
|
||||
|
||||
### Local Preview
|
||||
- [ ] Run `npm run preview`
|
||||
- [ ] Test all routes work
|
||||
- [ ] Test API calls work
|
||||
- [ ] Test error boundaries
|
||||
- [ ] Test lazy loading
|
||||
|
||||
### IIS Configuration
|
||||
- [ ] Create `web.config` (in root) with URL rewrite rules
|
||||
- [ ] Add SPA routing rule (all → index.html)
|
||||
- [ ] Add proxy rule: `/api/reports/*` → `http://localhost:8001/api/*`
|
||||
- [ ] Add proxy rule: `/api/data-entry/*` → `http://localhost:8003/api/*`
|
||||
- [ ] Add proxy rule: `/uploads/*` → `http://localhost:8003/uploads/*`
|
||||
|
||||
### Staging Deployment
|
||||
- [ ] Deploy to staging IIS site
|
||||
- [ ] Test all routes
|
||||
- [ ] Test API calls
|
||||
- [ ] Test error boundaries
|
||||
- [ ] Test on different browsers
|
||||
- [ ] Test on mobile devices
|
||||
|
||||
### Production Deployment
|
||||
- [ ] Backup current IIS configuration
|
||||
- [ ] Backup current builds
|
||||
- [ ] Deploy unified app
|
||||
- [ ] Test all routes
|
||||
- [ ] Test API calls
|
||||
- [ ] Monitor error logs
|
||||
|
||||
### Verification
|
||||
- [ ] Build succeeds
|
||||
- [ ] Chunks split correctly
|
||||
- [ ] IIS deployment works
|
||||
- [ ] API routing correct
|
||||
- [ ] All features work in production
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Manual Testing
|
||||
- [ ] Login flow works
|
||||
- [ ] Navigate to all Reports views
|
||||
- [ ] Navigate to all Data Entry views
|
||||
- [ ] Switch between modules
|
||||
- [ ] Test company selector
|
||||
- [ ] Test period selector
|
||||
- [ ] Test logout
|
||||
- [ ] Test on mobile (375px)
|
||||
- [ ] Test on tablet (768px)
|
||||
- [ ] Test on desktop (1920px)
|
||||
|
||||
### E2E Tests
|
||||
- [ ] Create `tests/e2e/login.spec.js` (in root)
|
||||
- [ ] Create `tests/e2e/reports-navigation.spec.js` (in root)
|
||||
- [ ] Create `tests/e2e/data-entry-navigation.spec.js` (in root)
|
||||
- [ ] Create `tests/e2e/module-switching.spec.js` (in root)
|
||||
- [ ] Create `tests/e2e/error-isolation.spec.js` (in root)
|
||||
- [ ] Run all E2E tests - pass
|
||||
|
||||
### Performance Testing
|
||||
- [ ] Run Lighthouse audit
|
||||
- [ ] Performance score ≥ 90
|
||||
- [ ] Initial load < 2 seconds
|
||||
- [ ] Module switching < 500ms (cached)
|
||||
|
||||
---
|
||||
|
||||
## Post-Implementation Checklist
|
||||
|
||||
### Documentation Updates
|
||||
- [ ] Update `CLAUDE.md` - Architecture section
|
||||
- [ ] Update `CLAUDE.md` - Deployment section
|
||||
- [ ] Update `README.md` - Quick start
|
||||
- [ ] Update `README.md` - URL structure
|
||||
- [ ] Update `DEPLOYMENT_GUIDE.md` - IIS configuration
|
||||
- [ ] Update `docs/ARCHITECTURE_SCHEMA.md` - Diagrams
|
||||
- [ ] Update `deployment/windows/README.md` - Deployment steps
|
||||
|
||||
### Cleanup (After 1 Week)
|
||||
- [ ] Archive `reports-app/frontend/` → `reports-app/frontend-archived/`
|
||||
- [ ] Archive `data-entry-app/frontend/` → `data-entry-app/frontend-archived/`
|
||||
- [ ] Update `start-test.sh` to use root directory
|
||||
- [ ] Update `start-data-entry.sh` to use root directory
|
||||
- [ ] Update CI/CD pipelines (if any)
|
||||
- [ ] Document migration in CHANGELOG.md
|
||||
|
||||
### Monitoring (First Week)
|
||||
- [ ] Day 1: Review error logs
|
||||
- [ ] Day 2: Review error logs
|
||||
- [ ] Day 3: Review error logs
|
||||
- [ ] Day 4: Review error logs
|
||||
- [ ] Day 5: Review error logs
|
||||
- [ ] Day 6: Review error logs
|
||||
- [ ] Day 7: Review error logs + user feedback
|
||||
|
||||
### Optimization (First Month)
|
||||
- [ ] Week 2: Analyze bundle sizes
|
||||
- [ ] Week 2: Optimize images/assets
|
||||
- [ ] Week 3: Consider further code splitting
|
||||
- [ ] Week 3: Add performance monitoring
|
||||
- [ ] Week 4: Evaluate feature flag usage
|
||||
- [ ] Week 4: Plan next improvements
|
||||
|
||||
---
|
||||
|
||||
## Common Issues & Solutions
|
||||
|
||||
### Issue: CSS Conflicts
|
||||
**Solution**: Use CSS modules, check `docs/CSS_PATTERNS.md`
|
||||
|
||||
### Issue: Import Errors
|
||||
**Solution**: Update import paths, check alias configuration in vite.config.js
|
||||
|
||||
### Issue: API Calls Failing
|
||||
**Solution**: Verify proxy configuration, check backend is running
|
||||
|
||||
### Issue: Error Boundary Not Catching
|
||||
**Solution**: Check onErrorCaptured implementation, add global error handler
|
||||
|
||||
### Issue: Large Bundle Size
|
||||
**Solution**: Review manualChunks, enable tree shaking, lazy load more
|
||||
|
||||
### Issue: IIS Routing Not Working
|
||||
**Solution**: Check web.config URL rewrite rules, verify IIS URL Rewrite module installed
|
||||
|
||||
### Issue: Store Contamination
|
||||
**Solution**: Use module-scoped stores, check for global state
|
||||
|
||||
### Issue: PrimeVue Theme Conflicts
|
||||
**Solution**: Use single theme (saga-blue), override in vendor CSS
|
||||
|
||||
---
|
||||
|
||||
## Progress Tracking
|
||||
|
||||
**Phase 1: Setup**
|
||||
- Started: ___________
|
||||
- Completed: ___________
|
||||
- Time Spent: ___________ hours
|
||||
|
||||
**Phase 2: Migration**
|
||||
- Started: ___________
|
||||
- Completed: ___________
|
||||
- Time Spent: ___________ hours
|
||||
|
||||
**Phase 3: Routing**
|
||||
- Started: ___________
|
||||
- Completed: ___________
|
||||
- Time Spent: ___________ hours
|
||||
|
||||
**Phase 4: Error Boundaries**
|
||||
- Started: ___________
|
||||
- Completed: ___________
|
||||
- Time Spent: ___________ hours
|
||||
|
||||
**Phase 5: Build & Deploy**
|
||||
- Started: ___________
|
||||
- Completed: ___________
|
||||
- Time Spent: ___________ hours
|
||||
|
||||
**Total Time**: ___________ hours (Estimated: 20 hours)
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### Deployment
|
||||
- [ ] Single IIS site running (not 2)
|
||||
- [ ] Single build process (not 2)
|
||||
- [ ] Zero downtime during deployment
|
||||
|
||||
### Performance
|
||||
- [ ] Initial load < 2 seconds
|
||||
- [ ] Module switching < 500ms
|
||||
- [ ] Lighthouse score ≥ 90
|
||||
- [ ] Bundle size ≤ sum of old apps
|
||||
|
||||
### Quality
|
||||
- [ ] 100% feature parity
|
||||
- [ ] All E2E tests passing
|
||||
- [ ] Zero user-reported bugs (first week)
|
||||
- [ ] Error isolation verified
|
||||
|
||||
---
|
||||
|
||||
**Status**: ⬜ Not Started | 🔵 In Progress | ✅ Complete
|
||||
|
||||
**Last Updated**: ___________
|
||||
**Completed By**: ___________
|
||||
**Production Deploy Date**: ___________
|
||||
@@ -1,346 +0,0 @@
|
||||
# Unified App Specification - Executive Summary
|
||||
|
||||
**Created**: 2025-12-22
|
||||
**Status**: Implementation-Ready
|
||||
**Estimated Effort**: 2.5 days
|
||||
|
||||
---
|
||||
|
||||
## What We're Building
|
||||
|
||||
A single unified SPA that consolidates the Reports App and Data Entry App into one application with:
|
||||
- Unified menu navigation between modules
|
||||
- Module isolation via error boundaries
|
||||
- Lazy loading for optimal performance
|
||||
- Single build and deployment process
|
||||
|
||||
---
|
||||
|
||||
## Key Requirements (Top 5)
|
||||
|
||||
1. **Unified Navigation**: Single menu with Reports and Data Entry sections
|
||||
2. **Module Isolation**: Error in one module doesn't crash the other
|
||||
3. **Lazy Loading**: Modules loaded on-demand, not upfront
|
||||
4. **Simplified Deployment**: Single IIS site instead of 2
|
||||
5. **Zero Backend Changes**: Both backends (8001, 8003) remain unchanged
|
||||
|
||||
---
|
||||
|
||||
## Technical Approach
|
||||
|
||||
### Architecture: Pragmatic Monolith
|
||||
|
||||
**NOT using micro-frontends** because:
|
||||
- Only 1 developer (not 20+ team)
|
||||
- Weekly deploys (not multiple times per day)
|
||||
- 1-5 concurrent users (not millions)
|
||||
- Same tech stack (Vue 3 only)
|
||||
|
||||
**Using instead**:
|
||||
- Error boundaries for isolation (50-70% blast radius reduction)
|
||||
- Lazy loading for performance
|
||||
- Feature flags for module control
|
||||
- Shared dependencies for smaller bundles
|
||||
|
||||
### URL Structure
|
||||
|
||||
```
|
||||
/login → Login
|
||||
/ → Redirect to /reports/dashboard
|
||||
|
||||
/reports/dashboard → Dashboard
|
||||
/reports/invoices → Invoices
|
||||
/reports/bank-cash → Bank/Cash
|
||||
/reports/trial-balance → Trial Balance
|
||||
/reports/telegram → Telegram Bot
|
||||
/reports/cache-stats → Cache Stats
|
||||
|
||||
/data-entry → Receipts List
|
||||
/data-entry/create → New Receipt
|
||||
/data-entry/:id → View Receipt
|
||||
/data-entry/:id/edit → Edit Receipt
|
||||
```
|
||||
|
||||
### API Routing
|
||||
|
||||
**Vite Dev Proxy**:
|
||||
- `/api/reports/*` → `http://localhost:8001/api/*`
|
||||
- `/api/data-entry/*` → `http://localhost:8003/api/*`
|
||||
- `/uploads/*` → `http://localhost:8003/uploads/*`
|
||||
|
||||
**IIS Production Proxy**:
|
||||
- Same routing via web.config URL rewrite rules
|
||||
|
||||
---
|
||||
|
||||
## Critical Files (Top 10)
|
||||
|
||||
### To Create (in root directory)
|
||||
|
||||
1. `package.json` - Merged dependencies
|
||||
2. `vite.config.js` - Dual proxy + lazy loading
|
||||
3. `src/main.js` - App initialization
|
||||
4. `src/App.vue` - Root with unified menu
|
||||
5. `src/router/index.js` - Unified router
|
||||
6. `src/config/menu.js` - Menu configuration
|
||||
7. `src/shared/components/ErrorBoundary.vue` - Error isolation
|
||||
8. `src/modules/reports/ReportsLayout.vue` - Reports wrapper
|
||||
9. `src/modules/data-entry/DataEntryLayout.vue` - Data Entry wrapper
|
||||
10. `web.config` - IIS configuration
|
||||
|
||||
### To Migrate
|
||||
|
||||
**Reports Module** (7 views, 5 stores, 1 service):
|
||||
- Views: Dashboard, Invoices, BankCash, TrialBalance, Telegram, CacheStats
|
||||
- Stores: dashboard, invoices, treasury, trialBalance, cacheStore
|
||||
- Service: api.js (change base URL to `/api/reports/`)
|
||||
|
||||
**Data Entry Module** (2 views, 3 components, 1 store, 1 service):
|
||||
- Views: ReceiptsList, ReceiptCreate
|
||||
- Components: OCRUploadZone, OCRPreview, OCRConfidenceIndicator
|
||||
- Store: receiptsStore
|
||||
- Service: api.js (change base URL to `/api/data-entry/`)
|
||||
|
||||
**Shared** (5 components, 3 stores):
|
||||
- Components: LoginView, AppHeader, SlideMenu, CompanySelector, PeriodSelector
|
||||
- Stores: auth, companies, accountingPeriod (factories)
|
||||
|
||||
**CSS**: Copy entire `reports-app/frontend/src/assets/css/` structure
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Setup (0.5 days)
|
||||
- Create directory structure
|
||||
- Setup package.json, vite.config.js
|
||||
- Copy shared components and CSS
|
||||
|
||||
**Verify**: `npm install` and `npm run dev` work
|
||||
|
||||
### Phase 2: Migration (1 day)
|
||||
- Migrate all views, components, stores
|
||||
- Update API service base URLs
|
||||
- Merge CSS
|
||||
|
||||
**Verify**: All views render, no import errors
|
||||
|
||||
### Phase 3: Routing & Navigation (0.5 days)
|
||||
- Create unified router with lazy loading
|
||||
- Create menu configuration
|
||||
- Create error boundaries
|
||||
- Integrate AppHeader and SlideMenu
|
||||
|
||||
**Verify**: Navigation works, lazy loading works
|
||||
|
||||
### Phase 4: Error Boundaries & Resilience (0.25 days)
|
||||
- Test error isolation
|
||||
- Test feature flags
|
||||
- Add loading states
|
||||
|
||||
**Verify**: Error in one module doesn't crash other
|
||||
|
||||
### Phase 5: Build & Deploy (0.25 days)
|
||||
- Production build
|
||||
- IIS configuration
|
||||
- Deploy to staging
|
||||
- Test all routes
|
||||
|
||||
**Verify**: Build succeeds, IIS works, APIs route correctly
|
||||
|
||||
---
|
||||
|
||||
## Expected Build Output
|
||||
|
||||
```
|
||||
dist/
|
||||
├── index.html
|
||||
├── assets/
|
||||
│ ├── vendor-core.[hash].js (~150KB) - Vue, Router, Pinia
|
||||
│ ├── vendor-primevue.[hash].js (~200KB) - PrimeVue components
|
||||
│ ├── vendor-utils.[hash].js (~80KB) - Axios, date-fns
|
||||
│ ├── vendor-charts.[hash].js (~150KB) - Chart.js (lazy)
|
||||
│ ├── vendor-export.[hash].js (~200KB) - XLSX, jsPDF (lazy)
|
||||
│ ├── reports.[hash].js (~150KB) - Reports module (lazy)
|
||||
│ ├── data-entry.[hash].js (~100KB) - Data Entry module (lazy)
|
||||
│ ├── main.[hash].js (~50KB) - App shell
|
||||
│ └── main.[hash].css (~80KB) - Global CSS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Must Have (Before Production)
|
||||
- [ ] All Reports views work correctly
|
||||
- [ ] All Data Entry views work correctly
|
||||
- [ ] Navigation preserves auth and company/period
|
||||
- [ ] Error in one module doesn't crash other
|
||||
- [ ] Single IIS site deployment works
|
||||
- [ ] API routing to both backends works
|
||||
|
||||
### Performance Targets
|
||||
- [ ] Initial load < 2 seconds
|
||||
- [ ] Module switching < 500ms (cached)
|
||||
- [ ] Bundle size ≤ sum of current apps
|
||||
- [ ] Lighthouse score ≥ 90
|
||||
|
||||
### Testing
|
||||
- [ ] E2E tests pass for login
|
||||
- [ ] E2E tests pass for Reports navigation
|
||||
- [ ] E2E tests pass for Data Entry navigation
|
||||
- [ ] E2E tests pass for module switching
|
||||
- [ ] E2E tests verify error isolation
|
||||
|
||||
---
|
||||
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| CSS conflicts | Use design tokens, test thoroughly |
|
||||
| Large bundle size | Lazy loading, code splitting, tree shaking |
|
||||
| Error boundary gaps | Test error scenarios, add global handler |
|
||||
| IIS deployment complexity | Document config, test on staging first |
|
||||
| Store contamination | Module-scoped stores, test isolation |
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
**If deployment fails**:
|
||||
|
||||
1. **Keep both apps running** (zero downtime)
|
||||
- Leave `/roa2web/` and `/data-entry/` running
|
||||
- Add unified app at `/unified/` for testing
|
||||
|
||||
2. **Quick rollback** (15 minutes)
|
||||
- Restore IIS config from backup
|
||||
- Restore builds from `dist-backup/`
|
||||
|
||||
3. **Git rollback**
|
||||
- Tag before merge: `v1.0-pre-unified`
|
||||
- Revert if needed: `git reset --hard v1.0-pre-unified`
|
||||
|
||||
---
|
||||
|
||||
## Post-Implementation
|
||||
|
||||
### After 1 Week of Stability
|
||||
|
||||
**Archive old frontends**:
|
||||
```bash
|
||||
mv reports-app/frontend reports-app/frontend-archived
|
||||
mv data-entry-app/frontend data-entry-app/frontend-archived
|
||||
```
|
||||
|
||||
**Update documentation**:
|
||||
- CLAUDE.md - Architecture and deployment
|
||||
- DEPLOYMENT_GUIDE.md - IIS configuration
|
||||
- README.md - Quick start and commands
|
||||
|
||||
**Update scripts**:
|
||||
- `./start-test.sh` - Point to root directory
|
||||
- `./start-data-entry.sh` - Point to root directory
|
||||
|
||||
### Monitoring (First Month)
|
||||
|
||||
**Week 1**: Daily error log review
|
||||
**Week 2-4**: Performance optimization
|
||||
- Bundle size optimization
|
||||
- Further code splitting
|
||||
- Cache optimization
|
||||
|
||||
---
|
||||
|
||||
## Open Questions & Recommendations
|
||||
|
||||
### 1. PrimeVue Theme
|
||||
**Question**: Use `saga-blue` (reports-app) or `lara-light-blue` (data-entry-app)?
|
||||
**Recommendation**: `saga-blue` (reports-app is primary)
|
||||
|
||||
### 2. Feature Flags
|
||||
**Question**: Config file or environment variables?
|
||||
**Recommendation**: Config file for simplicity, env vars for override
|
||||
|
||||
### 3. Module Activation
|
||||
**Question**: All active by default or opt-in?
|
||||
**Recommendation**: All active by default (disable via config if needed)
|
||||
|
||||
### 4. Monitoring
|
||||
**Question**: Console logs only or add Sentry/similar?
|
||||
**Recommendation**: Console logs for MVP, add monitoring later
|
||||
|
||||
---
|
||||
|
||||
## Key Decisions Made
|
||||
|
||||
1. **Architecture**: Pragmatic monolith (not micro-frontends)
|
||||
2. **Error Isolation**: Error boundaries per module
|
||||
3. **Code Splitting**: Lazy loading with manual chunks
|
||||
4. **URL Structure**: `/reports/*` and `/data-entry/*`
|
||||
5. **API Routing**: Proxy via Vite (dev) and IIS (prod)
|
||||
6. **CSS System**: Use reports-app CSS structure
|
||||
7. **PrimeVue Theme**: saga-blue (from reports-app)
|
||||
8. **Shared Components**: Use existing from `shared/frontend/`
|
||||
9. **Deployment**: Single IIS site at root `/`
|
||||
10. **Backends**: No changes (remain at 8001, 8003)
|
||||
|
||||
---
|
||||
|
||||
## Documentation Locations
|
||||
|
||||
**Complete Spec**: `.auto-build-data/specs/unified-app/spec.md`
|
||||
**Critical Files**: `.auto-build-data/specs/unified-app/critical-files.md`
|
||||
**This Summary**: `.auto-build-data/specs/unified-app/SUMMARY.md`
|
||||
|
||||
**Note**: All implementation files will be created in the project root directory (`.` or `/mnt/e/proiecte/roa2web/`)
|
||||
|
||||
**Reference Docs**:
|
||||
- `IMPLEMENTATION_PLAN_UNIFIED_APP.md` - Original plan
|
||||
- `CLAUDE.md` - Project documentation (update after)
|
||||
- `docs/ONBOARDING_CSS.md` - CSS system guide
|
||||
- `docs/CSS_PATTERNS.md` - Available CSS patterns
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Read the complete spec**: `spec.md` (detailed technical specification)
|
||||
2. **Review critical files**: `critical-files.md` (files to migrate/create)
|
||||
3. **Start Phase 1**: Project setup (0.5 days)
|
||||
4. **Follow implementation plan**: 5 phases over 2.5 days
|
||||
5. **Test thoroughly**: E2E tests before production
|
||||
6. **Deploy to staging**: Test IIS configuration
|
||||
7. **Deploy to production**: Single site deployment
|
||||
8. **Monitor for 1 week**: Daily error log review
|
||||
9. **Archive old apps**: After stability confirmed
|
||||
10. **Update docs**: Complete documentation updates
|
||||
|
||||
---
|
||||
|
||||
## Quick Stats
|
||||
|
||||
- **Files to create**: ~15
|
||||
- **Files to migrate**: ~20
|
||||
- **CSS files to copy**: ~30
|
||||
- **Total files affected**: ~65
|
||||
- **Estimated time**: 2.5 days (20 hours)
|
||||
- **Complexity**: Medium
|
||||
- **Risk level**: Medium-Low (with mitigations)
|
||||
|
||||
---
|
||||
|
||||
**Specification Status**: ✅ Implementation-Ready
|
||||
**All Technical Decisions**: ✅ Made
|
||||
**Rollback Plan**: ✅ Defined
|
||||
**Success Criteria**: ✅ Defined
|
||||
|
||||
**Ready to implement!** 🚀
|
||||
|
||||
---
|
||||
|
||||
**Version**: 1.0
|
||||
**Created**: 2025-12-22
|
||||
**Author**: Claude (Specification Agent)
|
||||
**For**: ROA2WEB Unified App Feature
|
||||
@@ -1,477 +0,0 @@
|
||||
# Unified App - Critical Files Reference
|
||||
|
||||
**Quick Reference**: Files that will be most affected during implementation
|
||||
|
||||
---
|
||||
|
||||
## Files to Analyze (Before Implementation)
|
||||
|
||||
### Configuration Files
|
||||
|
||||
**Reports App**:
|
||||
- `/mnt/e/proiecte/roa2web/reports-app/frontend/package.json` - Dependencies to merge
|
||||
- `/mnt/e/proiecte/roa2web/reports-app/frontend/vite.config.js` - Proxy config, build settings
|
||||
- `/mnt/e/proiecte/roa2web/reports-app/frontend/src/main.js` - PrimeVue setup
|
||||
|
||||
**Data Entry App**:
|
||||
- `/mnt/e/proiecte/roa2web/data-entry-app/frontend/package.json` - Dependencies to merge
|
||||
- `/mnt/e/proiecte/roa2web/data-entry-app/frontend/vite.config.js` - Proxy config
|
||||
- `/mnt/e/proiecte/roa2web/data-entry-app/frontend/src/main.js` - PrimeVue setup
|
||||
|
||||
### Application Entry Points
|
||||
|
||||
**Reports App**:
|
||||
- `/mnt/e/proiecte/roa2web/reports-app/frontend/src/App.vue` - Root component, menu integration
|
||||
- `/mnt/e/proiecte/roa2web/reports-app/frontend/src/router/index.js` - Router config
|
||||
|
||||
**Data Entry App**:
|
||||
- `/mnt/e/proiecte/roa2web/data-entry-app/frontend/src/App.vue` - Root component, menu integration
|
||||
- `/mnt/e/proiecte/roa2web/data-entry-app/frontend/src/router/index.js` - Router config
|
||||
|
||||
### Shared Components (Already Created)
|
||||
|
||||
- `/mnt/e/proiecte/roa2web/shared/frontend/components/LoginView.vue`
|
||||
- `/mnt/e/proiecte/roa2web/shared/frontend/components/layout/AppHeader.vue`
|
||||
- `/mnt/e/proiecte/roa2web/shared/frontend/components/layout/SlideMenu.vue`
|
||||
- `/mnt/e/proiecte/roa2web/shared/frontend/components/CompanySelector.vue`
|
||||
- `/mnt/e/proiecte/roa2web/shared/frontend/components/PeriodSelector.vue`
|
||||
|
||||
### Shared Stores (Factories)
|
||||
|
||||
- `/mnt/e/proiecte/roa2web/shared/frontend/stores/auth.js`
|
||||
- `/mnt/e/proiecte/roa2web/shared/frontend/stores/companies.js`
|
||||
- `/mnt/e/proiecte/roa2web/shared/frontend/stores/accountingPeriod.js`
|
||||
|
||||
---
|
||||
|
||||
## Views to Migrate
|
||||
|
||||
### Reports Module (7 views)
|
||||
|
||||
Source: `/mnt/e/proiecte/roa2web/reports-app/frontend/src/views/`
|
||||
Destination: `/mnt/e/proiecte/roa2web/src/modules/reports/views/`
|
||||
|
||||
1. `DashboardView.vue` - Main dashboard with metrics
|
||||
2. `InvoicesView.vue` - Invoices table and filters
|
||||
3. `BankCashRegisterView.vue` - Bank and cash register transactions
|
||||
4. `TrialBalanceView.vue` - Trial balance report
|
||||
5. `TelegramView.vue` - Telegram bot management
|
||||
6. `CacheStatsView.vue` - Cache statistics
|
||||
7. ~~`LoginView.vue`~~ - USE SHARED VERSION
|
||||
|
||||
### Data Entry Module (2 views + 3 components)
|
||||
|
||||
**Views**:
|
||||
Source: `/mnt/e/proiecte/roa2web/data-entry-app/frontend/src/views/receipts/`
|
||||
Destination: `/mnt/e/proiecte/roa2web/src/modules/data-entry/views/receipts/`
|
||||
|
||||
1. `ReceiptsListView.vue` - List of receipts with filters
|
||||
2. `ReceiptCreateView.vue` - Create/edit receipt form
|
||||
|
||||
**Components**:
|
||||
Source: `/mnt/e/proiecte/roa2web/data-entry-app/frontend/src/components/ocr/`
|
||||
Destination: `/mnt/e/proiecte/roa2web/src/modules/data-entry/components/ocr/`
|
||||
|
||||
1. `OCRUploadZone.vue` - File upload zone for receipts
|
||||
2. `OCRPreview.vue` - Preview uploaded receipt image
|
||||
3. `OCRConfidenceIndicator.vue` - OCR confidence indicator
|
||||
|
||||
---
|
||||
|
||||
## Stores to Migrate
|
||||
|
||||
### Reports Module (5 stores)
|
||||
|
||||
Source: `/mnt/e/proiecte/roa2web/reports-app/frontend/src/stores/`
|
||||
Destination: `/mnt/e/proiecte/roa2web/src/modules/reports/stores/`
|
||||
|
||||
1. `dashboard.js` - Dashboard data and metrics
|
||||
2. `invoices.js` - Invoices data and filters
|
||||
3. `treasury.js` - Bank/cash register data
|
||||
4. `trialBalance.js` - Trial balance data
|
||||
5. `cacheStore.js` - Cache statistics
|
||||
|
||||
**SKIP** (use shared):
|
||||
- ~~`auth.js`~~ - Use `/mnt/e/proiecte/roa2web/shared/frontend/stores/auth.js`
|
||||
- ~~`companies.js`~~ - Use `/mnt/e/proiecte/roa2web/shared/frontend/stores/companies.js`
|
||||
- ~~`accountingPeriod.js`~~ - Use `/mnt/e/proiecte/roa2web/shared/frontend/stores/accountingPeriod.js`
|
||||
|
||||
### Data Entry Module (1 store)
|
||||
|
||||
Source: `/mnt/e/proiecte/roa2web/data-entry-app/frontend/src/stores/`
|
||||
Destination: `/mnt/e/proiecte/roa2web/src/modules/data-entry/stores/`
|
||||
|
||||
1. `receiptsStore.js` - Receipts data and CRUD operations
|
||||
|
||||
**SKIP** (use shared):
|
||||
- ~~`auth.js`~~ - Use shared
|
||||
- ~~`companies.js`~~ - Use shared
|
||||
- ~~`accountingPeriod.js`~~ - Use shared
|
||||
|
||||
---
|
||||
|
||||
## Services to Migrate
|
||||
|
||||
### Reports Module
|
||||
|
||||
Source: `/mnt/e/proiecte/roa2web/reports-app/frontend/src/services/`
|
||||
Destination: `/mnt/e/proiecte/roa2web/src/modules/reports/services/`
|
||||
|
||||
1. `api.js` - **MODIFY**: Change base URL to `/api/reports/`
|
||||
|
||||
**Current**:
|
||||
```javascript
|
||||
const api = axios.create({
|
||||
baseURL: '/api'
|
||||
})
|
||||
```
|
||||
|
||||
**New**:
|
||||
```javascript
|
||||
const api = axios.create({
|
||||
baseURL: '/api/reports'
|
||||
})
|
||||
```
|
||||
|
||||
### Data Entry Module
|
||||
|
||||
Source: `/mnt/e/proiecte/roa2web/data-entry-app/frontend/src/services/`
|
||||
Destination: `/mnt/e/proiecte/roa2web/src/modules/data-entry/services/`
|
||||
|
||||
1. `api.js` - **MODIFY**: Change base URL to `/api/data-entry/`
|
||||
|
||||
**Current**:
|
||||
```javascript
|
||||
const api = axios.create({
|
||||
baseURL: '/api'
|
||||
})
|
||||
```
|
||||
|
||||
**New**:
|
||||
```javascript
|
||||
const api = axios.create({
|
||||
baseURL: '/api/data-entry'
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CSS to Migrate
|
||||
|
||||
### Reports Module CSS (COPY ENTIRE STRUCTURE)
|
||||
|
||||
Source: `/mnt/e/proiecte/roa2web/reports-app/frontend/src/assets/css/`
|
||||
Destination: `/mnt/e/proiecte/roa2web/src/assets/css/`
|
||||
|
||||
**Copy ALL files** (this is the main CSS system):
|
||||
- `core/` - Design tokens (colors, spacing, typography)
|
||||
- `components/` - Reusable UI patterns (buttons, forms, cards, tables, stats)
|
||||
- `patterns/` - Interactive patterns (animations, dashboard, interactive)
|
||||
- `layout/` - Page structure (containers, grid, navigation)
|
||||
- `utilities/` - Utility classes (colors, spacing, flex, text, display)
|
||||
- `vendor/` - PrimeVue overrides
|
||||
- `global.css` - Global styles
|
||||
- `main.css` - Main entry point
|
||||
- `mobile.css` - Mobile responsive styles
|
||||
|
||||
### Data Entry Module CSS (MERGE)
|
||||
|
||||
Source: `/mnt/e/proiecte/roa2web/data-entry-app/frontend/src/assets/css/`
|
||||
|
||||
**Analyze and merge unique styles**:
|
||||
- `main.css` - Merge with reports-app main.css
|
||||
- Any component-specific styles - Integrate into unified system
|
||||
|
||||
### Shared CSS
|
||||
|
||||
Source: `/mnt/e/proiecte/roa2web/shared/frontend/styles/`
|
||||
Destination: `/mnt/e/proiecte/roa2web/src/shared/styles/`
|
||||
|
||||
1. `login.css` - Login page styles
|
||||
2. `layout/header.css` - Header styles
|
||||
3. `layout/navigation.css` - Navigation styles
|
||||
|
||||
---
|
||||
|
||||
## Critical Implementation Files
|
||||
|
||||
### 1. Package Configuration
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/package.json`
|
||||
|
||||
**Dependencies to merge**:
|
||||
- From reports-app: axios, chart.js, date-fns, jspdf, jspdf-autotable, qrcode.vue, xlsx
|
||||
- From data-entry-app: All covered by reports-app
|
||||
- Shared: vue@^3.4.0, vue-router@^4.2.5, pinia@^2.1.7, primevue@^3.46.0
|
||||
|
||||
### 2. Vite Configuration
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/vite.config.js`
|
||||
|
||||
**Key sections**:
|
||||
- Dual proxy for `/api/reports/` → `http://localhost:8001`
|
||||
- Dual proxy for `/api/data-entry/` → `http://localhost:8003`
|
||||
- Lazy loading configuration (manualChunks)
|
||||
- Alias configuration (@, @shared, @reports, @data-entry)
|
||||
|
||||
### 3. Application Entry
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/src/main.js`
|
||||
|
||||
**Key setup**:
|
||||
- PrimeVue configuration (theme: saga-blue)
|
||||
- Global components registration
|
||||
- Router, Pinia, ToastService, ConfirmationService
|
||||
- CSS imports
|
||||
|
||||
### 4. Root Component
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/src/App.vue`
|
||||
|
||||
**Key elements**:
|
||||
- AppHeader with unified menu
|
||||
- SlideMenu with module sections
|
||||
- router-view
|
||||
- Toast, ConfirmDialog
|
||||
|
||||
### 5. Router Configuration
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/src/router/index.js`
|
||||
|
||||
**Key routes**:
|
||||
- `/login` - LoginView (eager loaded)
|
||||
- `/reports/*` - ReportsLayout (lazy loaded) with children
|
||||
- `/data-entry/*` - DataEntryLayout (lazy loaded) with children
|
||||
- Authentication guard
|
||||
|
||||
### 6. Menu Configuration
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/src/config/menu.js`
|
||||
|
||||
**Sections**:
|
||||
- Rapoarte (Reports)
|
||||
- Introduceri Date (Data Entry)
|
||||
- Sistem (System)
|
||||
|
||||
### 7. Feature Flags
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/src/config/features.js`
|
||||
|
||||
**Flags**:
|
||||
- reports.enabled
|
||||
- dataEntry.enabled
|
||||
- Module-level flags
|
||||
|
||||
### 8. Error Boundary Component
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/src/shared/components/ErrorBoundary.vue`
|
||||
|
||||
**Key features**:
|
||||
- onErrorCaptured hook
|
||||
- User-friendly error display
|
||||
- Retry functionality
|
||||
- Navigate away option
|
||||
|
||||
### 9. Module Layouts
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/src/modules/reports/ReportsLayout.vue`
|
||||
|
||||
**Wrapper**:
|
||||
```vue
|
||||
<ErrorBoundary module-name="Reports">
|
||||
<router-view />
|
||||
</ErrorBoundary>
|
||||
```
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/src/modules/data-entry/DataEntryLayout.vue`
|
||||
|
||||
**Wrapper**:
|
||||
```vue
|
||||
<ErrorBoundary module-name="Data Entry">
|
||||
<router-view />
|
||||
</ErrorBoundary>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment Files
|
||||
|
||||
### IIS Configuration
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/web.config`
|
||||
|
||||
**Key rules**:
|
||||
- SPA routing (all routes → index.html)
|
||||
- API proxy: `/api/reports/*` → `http://localhost:8001/api/*`
|
||||
- API proxy: `/api/data-entry/*` → `http://localhost:8003/api/*`
|
||||
- Uploads proxy: `/uploads/*` → `http://localhost:8003/uploads/*`
|
||||
|
||||
### Environment Variables
|
||||
|
||||
**File**: `/mnt/e/proiecte/roa2web/.env.example`
|
||||
|
||||
**Variables**:
|
||||
```bash
|
||||
# API Endpoints (dev only, production uses IIS proxy)
|
||||
VITE_REPORTS_API=http://localhost:8001
|
||||
VITE_DATA_ENTRY_API=http://localhost:8003
|
||||
|
||||
# Feature Flags
|
||||
VITE_ENABLE_REPORTS=true
|
||||
VITE_ENABLE_DATA_ENTRY=true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Files
|
||||
|
||||
### E2E Tests
|
||||
|
||||
**New files to create**:
|
||||
- `/mnt/e/proiecte/roa2web/tests/e2e/login.spec.js`
|
||||
- `/mnt/e/proiecte/roa2web/tests/e2e/reports-navigation.spec.js`
|
||||
- `/mnt/e/proiecte/roa2web/tests/e2e/data-entry-navigation.spec.js`
|
||||
- `/mnt/e/proiecte/roa2web/tests/e2e/module-switching.spec.js`
|
||||
- `/mnt/e/proiecte/roa2web/tests/e2e/error-isolation.spec.js`
|
||||
|
||||
**Existing tests to adapt**:
|
||||
- `/mnt/e/proiecte/roa2web/reports-app/frontend/tests/` - Update routes
|
||||
- `/mnt/e/proiecte/roa2web/data-entry-app/frontend/tests/` - Update routes
|
||||
|
||||
---
|
||||
|
||||
## Documentation Files to Update
|
||||
|
||||
### Primary Documentation
|
||||
|
||||
1. **CLAUDE.md** (root)
|
||||
- Update architecture diagram
|
||||
- Add unified-app section
|
||||
- Mark old apps as archived
|
||||
- Update deployment instructions
|
||||
|
||||
2. **README.md** (root)
|
||||
- Update quick start
|
||||
- Update deployment section
|
||||
- Update URL structure
|
||||
|
||||
3. **DEPLOYMENT_GUIDE.md**
|
||||
- Update IIS configuration
|
||||
- Update build process
|
||||
- Add rollback instructions
|
||||
|
||||
4. **docs/ARCHITECTURE_SCHEMA.md**
|
||||
- Update architecture diagrams
|
||||
- Document module structure
|
||||
- Add error boundary architecture
|
||||
|
||||
### Deployment Documentation
|
||||
|
||||
5. **deployment/windows/README.md**
|
||||
- Update deployment steps
|
||||
- Update IIS configuration
|
||||
- Update proxy rules
|
||||
|
||||
6. **deployment/windows/docs/WINDOWS_DEPLOYMENT.md**
|
||||
- Complete Windows guide updates
|
||||
|
||||
---
|
||||
|
||||
## File Count Summary
|
||||
|
||||
**To Create**: ~15 new files
|
||||
- 9 core files (package.json, vite.config.js, main.js, App.vue, router, menu, features, ErrorBoundary, 2 layouts)
|
||||
- 3 configuration files (.env.example, web.config, README.md)
|
||||
- 3 documentation updates
|
||||
|
||||
**To Migrate**: ~20 files
|
||||
- 7 Reports views
|
||||
- 2 Data Entry views
|
||||
- 3 Data Entry components
|
||||
- 5 Reports stores
|
||||
- 1 Data Entry store
|
||||
- 2 API services
|
||||
|
||||
**To Copy**: ~30 CSS files
|
||||
- Entire reports-app CSS structure
|
||||
|
||||
**Total Files Affected**: ~65 files
|
||||
|
||||
---
|
||||
|
||||
## Time Estimates per File Type
|
||||
|
||||
| Task | Files | Avg Time | Total |
|
||||
|------|-------|----------|-------|
|
||||
| Create core files | 9 | 30 min | 4.5 hours |
|
||||
| Migrate views | 9 | 20 min | 3 hours |
|
||||
| Migrate components | 3 | 15 min | 45 min |
|
||||
| Migrate stores | 6 | 20 min | 2 hours |
|
||||
| Migrate services | 2 | 30 min | 1 hour |
|
||||
| Copy CSS | 1 | 1 hour | 1 hour |
|
||||
| Configuration | 3 | 30 min | 1.5 hours |
|
||||
| Documentation | 6 | 30 min | 3 hours |
|
||||
| Testing & fixes | - | - | 4 hours |
|
||||
|
||||
**Total**: ~20 hours (~2.5 days)
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After migrating each category:
|
||||
|
||||
### Configuration Files ✓
|
||||
- [ ] package.json has all dependencies
|
||||
- [ ] vite.config.js has dual proxy
|
||||
- [ ] main.js initializes correctly
|
||||
- [ ] npm install succeeds
|
||||
- [ ] npm run dev starts
|
||||
|
||||
### Views ✓
|
||||
- [ ] All Reports views render
|
||||
- [ ] All Data Entry views render
|
||||
- [ ] No import errors
|
||||
- [ ] Routes work
|
||||
|
||||
### Stores ✓
|
||||
- [ ] All stores import correctly
|
||||
- [ ] No duplicate store instances
|
||||
- [ ] Shared stores work
|
||||
- [ ] Module stores isolated
|
||||
|
||||
### Services ✓
|
||||
- [ ] API calls route to correct backend
|
||||
- [ ] /api/reports/ → :8001
|
||||
- [ ] /api/data-entry/ → :8003
|
||||
- [ ] Auth headers preserved
|
||||
|
||||
### CSS ✓
|
||||
- [ ] No style conflicts
|
||||
- [ ] Design tokens work
|
||||
- [ ] PrimeVue theme consistent
|
||||
- [ ] Responsive works
|
||||
|
||||
### Router ✓
|
||||
- [ ] All routes accessible
|
||||
- [ ] Lazy loading works
|
||||
- [ ] Auth guard works
|
||||
- [ ] 404 redirects
|
||||
|
||||
### Error Boundaries ✓
|
||||
- [ ] Catches component errors
|
||||
- [ ] Displays user message
|
||||
- [ ] Module isolation works
|
||||
- [ ] Can retry/navigate
|
||||
|
||||
### Build ✓
|
||||
- [ ] npm run build succeeds
|
||||
- [ ] Chunks split correctly
|
||||
- [ ] Bundle size acceptable
|
||||
- [ ] Preview works
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-12-22
|
||||
**For**: Unified App Implementation
|
||||
**Reference**: See spec.md for complete specification
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,86 +0,0 @@
|
||||
{
|
||||
"feature": "unified-app",
|
||||
"status": "IMPLEMENTATION_COMPLETE",
|
||||
"created": "2025-12-22T01:09:00Z",
|
||||
"updated": "2025-12-23T21:25:00Z",
|
||||
"complexity": "medium",
|
||||
"estimated_effort": "2.5 days",
|
||||
"worktree": "/mnt/e/proiecte/ab-worktrees/roa2web-unified-app",
|
||||
"branch": "feature/ab-unified-app",
|
||||
"totalTasks": 28,
|
||||
"currentTask": 28,
|
||||
"completedTasks": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28],
|
||||
"tasksCompleted": 28,
|
||||
"testing": {
|
||||
"playwrightTestsCreated": 6,
|
||||
"testsExecuted": 40,
|
||||
"testsPassed": 4,
|
||||
"testsBlocked": 36,
|
||||
"blockingReason": "Backend authentication error (Oracle DB/SSH tunnel configuration)",
|
||||
"frontendQuality": "100/100 - EXCELLENT",
|
||||
"visualRegressionIssues": 0,
|
||||
"consoleErrors": 0,
|
||||
"consoleWarnings": 0
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
"status": "SPEC_DRAFT",
|
||||
"at": "2025-12-22T01:09:00Z"
|
||||
},
|
||||
{
|
||||
"status": "SPEC_COMPLETE",
|
||||
"at": "2025-12-22T01:13:00Z"
|
||||
},
|
||||
{
|
||||
"status": "PLANNING",
|
||||
"at": "2025-12-22T09:30:00Z"
|
||||
},
|
||||
{
|
||||
"status": "PLANNING_COMPLETE",
|
||||
"at": "2025-12-22T09:35:00Z"
|
||||
},
|
||||
{
|
||||
"status": "IMPLEMENTING",
|
||||
"at": "2025-12-22T18:25:00Z",
|
||||
"task": 1
|
||||
},
|
||||
{
|
||||
"status": "IMPLEMENTING",
|
||||
"at": "2025-12-22T20:45:00Z",
|
||||
"task": 28,
|
||||
"started": true
|
||||
},
|
||||
{
|
||||
"status": "TESTING",
|
||||
"at": "2025-12-23T21:00:00Z",
|
||||
"description": "Comprehensive Playwright E2E testing"
|
||||
},
|
||||
{
|
||||
"status": "IMPLEMENTATION_COMPLETE",
|
||||
"at": "2025-12-23T21:25:00Z",
|
||||
"task": 28,
|
||||
"completed": true,
|
||||
"note": "All 28 tasks completed. Frontend fully functional. Backend auth requires Oracle/SSH configuration."
|
||||
}
|
||||
],
|
||||
"files": {
|
||||
"spec": "spec.md",
|
||||
"summary": "SUMMARY.md",
|
||||
"critical_files": "critical-files.md",
|
||||
"migration_checklist": "MIGRATION_CHECKLIST.md",
|
||||
"plan": "plan.md",
|
||||
"test_report": "../../UNIFIED_APP_TEST_REPORT.md"
|
||||
},
|
||||
"stats": {
|
||||
"files_to_create": 15,
|
||||
"files_to_migrate": 20,
|
||||
"css_files_to_copy": 30,
|
||||
"total_files_affected": 65
|
||||
},
|
||||
"deployment": {
|
||||
"ready": true,
|
||||
"frontendStatus": "PRODUCTION_READY",
|
||||
"backendStatus": "NEEDS_CONFIGURATION",
|
||||
"blockers": ["Oracle DB authentication", "SSH tunnel configuration"]
|
||||
}
|
||||
}
|
||||
25
.gitignore
vendored
25
.gitignore
vendored
@@ -249,17 +249,17 @@ quick_test.*
|
||||
.dockerignore
|
||||
.eggs/
|
||||
.eggs/
|
||||
.env
|
||||
# Environment files - ignore ALL .env files (security best practice)
|
||||
.env
|
||||
.env.*
|
||||
.env.*.local
|
||||
.env.local
|
||||
.env.production
|
||||
.env.production
|
||||
.env.test
|
||||
# Allow .env.example files (configuration templates)
|
||||
|
||||
# Allow only .env.example templates (no credentials)
|
||||
!.env.example
|
||||
!**/.env.example
|
||||
!.env.*.example
|
||||
!**/.env.*.example
|
||||
# Allow .dockerignore files (Docker build configuration)
|
||||
!.dockerignore
|
||||
!**/.dockerignore
|
||||
@@ -428,9 +428,6 @@ playwright-report/
|
||||
profile_default/
|
||||
quick_test.*
|
||||
quick_test.*
|
||||
# Removed duplicate - already covered in deployment section above
|
||||
roa2web/reports-app/telegram-bot/data/*.db
|
||||
roa2web/reports-app/telegram-bot/data/*.db-*
|
||||
run_tests.*
|
||||
run_tests.*
|
||||
scan_*.json
|
||||
@@ -512,6 +509,16 @@ secrets-backup/**/.env.*
|
||||
!secrets-backup/**/*.gpg
|
||||
|
||||
.playwright-mcp/*
|
||||
data-entry-app/backend/data/*
|
||||
# Auto-Build local data (worktrees, cache)
|
||||
.auto-build-data/
|
||||
|
||||
# ============================================================================
|
||||
# 🏗️ ULTRATHIN MONOLITH BACKEND DATA - DO NOT COMMIT
|
||||
# ============================================================================
|
||||
# Backend unified data directories (cache, receipts, telegram)
|
||||
backend/data/cache/*.db
|
||||
backend/data/receipts/*.db
|
||||
backend/data/telegram/*.db
|
||||
backend/data/receipts/uploads/*
|
||||
!backend/data/*/.gitkeep
|
||||
|
||||
|
||||
164
CLAUDE.md
164
CLAUDE.md
@@ -4,9 +4,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## 🚀 Project Overview
|
||||
|
||||
**ROA2WEB** - Modern ERP Application with two main modules:
|
||||
1. **Reports App** (`reports-app/`) - Read-only reports from Oracle (raportări)
|
||||
2. **Data Entry App** (`data-entry-app/`) - Data input with approval workflow (introduceri date)
|
||||
**ROA2WEB** - Modern ERP Application with ultrathin monolith architecture:
|
||||
1. **Reports Module** (`backend/modules/reports/`) - Read-only reports from Oracle (raportări)
|
||||
2. **Data Entry Module** (`backend/modules/data_entry/`) - Data input with approval workflow (introduceri date)
|
||||
3. **Telegram Bot Module** (`backend/modules/telegram/`) - Telegram bot integration
|
||||
|
||||
**Main Branch**: `main` (use for PRs)
|
||||
**Working Directory**: Repository root
|
||||
@@ -15,57 +16,74 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
---
|
||||
|
||||
## 📁 Application-Specific Instructions
|
||||
## 📁 Module-Specific Instructions
|
||||
|
||||
> **IMPORTANT**: When working on a specific application, ALWAYS read its dedicated CLAUDE.md first!
|
||||
> **IMPORTANT**: When working on a specific module, read its documentation first!
|
||||
|
||||
| Application | CLAUDE.md Location | Description |
|
||||
|-------------|-------------------|-------------|
|
||||
| **Data Entry** | `data-entry-app/CLAUDE.md` | Bonuri fiscale, chitanțe, workflow aprobare |
|
||||
| Module | Documentation Location | Description |
|
||||
|--------|----------------------|-------------|
|
||||
| **Data Entry** | `docs/data-entry/DATA-ENTRY-MODULE.md` | Bonuri fiscale, chitanțe, workflow aprobare |
|
||||
| **Reports** | This file (below) | Rapoarte Oracle read-only |
|
||||
| **Telegram Bot** | `reports-app/telegram-bot/README.md` | Bot Telegram |
|
||||
| **Telegram Bot** | `docs/telegram/README.md` | Bot Telegram |
|
||||
|
||||
### When to Use Which Instructions
|
||||
|
||||
**Working on `data-entry-app/`**:
|
||||
→ **FIRST read `data-entry-app/CLAUDE.md`** for:
|
||||
**Working on Data Entry** (`backend/modules/data_entry/` or `src/modules/data-entry/`):
|
||||
→ **FIRST read `docs/data-entry/DATA-ENTRY-MODULE.md`** for:
|
||||
- SQLModel + Alembic patterns (NOT Oracle)
|
||||
- SQLite database (NOT Oracle pool)
|
||||
- Workflow states (DRAFT → PENDING → APPROVED)
|
||||
- Receipt/Attachment/AccountingEntry models
|
||||
- Expense types and auto-generation logic
|
||||
- Oracle nomenclatures integration
|
||||
|
||||
**Working on `reports-app/` or `shared/`**:
|
||||
**Working on Reports Module** (`backend/modules/reports/` or `src/modules/reports/`):
|
||||
→ Use instructions from this file (below)
|
||||
|
||||
**Working on shared components** (`shared/auth/`, `shared/database/`, `shared/frontend/`):
|
||||
→ These are used by BOTH apps - be careful with changes!
|
||||
→ These are used by ALL modules - be careful with changes!
|
||||
→ `shared/frontend/` contains: LoginView.vue, auth store factory, login styles
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
### Microservices Structure
|
||||
> **Important Architecture Decisions:** See `docs/ARCHITECTURE-DECISIONS.md` for critical decisions about:
|
||||
> - Why single worker (`--workers 1`) is required for Telegram bot
|
||||
> - Ultrathin monolith vs microservices rationale
|
||||
> - IIS sub-application deployment strategy
|
||||
> - Performance characteristics and scaling considerations
|
||||
|
||||
### Ultrathin Monolith Structure
|
||||
```
|
||||
.
|
||||
├── shared/ # Shared components (DB pool, auth, frontend)
|
||||
│ ├── database/ # Oracle pool (used by both apps)
|
||||
│ ├── auth/ # JWT auth (used by both apps)
|
||||
│ └── frontend/ # Shared Vue components, stores, styles
|
||||
│ ├── components/ # LoginView.vue
|
||||
│ ├── stores/ # auth.js (Pinia store factory)
|
||||
│ └── styles/ # login.css
|
||||
├── backend/ # Unified FastAPI backend (port 8000/8001)
|
||||
│ ├── modules/ # Business logic modules
|
||||
│ │ ├── reports/ # Reports module (Oracle read-only)
|
||||
│ │ ├── data_entry/ # Data entry module (SQLite + workflow)
|
||||
│ │ ├── telegram/ # Telegram bot module
|
||||
│ │ └── data/ # Shared data (telegram_bot.db)
|
||||
│ ├── config.py # Centralized configuration
|
||||
│ └── main.py # FastAPI app entry point
|
||||
│
|
||||
├── reports-app/ # READ-ONLY reports from Oracle
|
||||
│ ├── backend/ # FastAPI API (port 8001)
|
||||
│ ├── frontend/ # Vue.js 3 UI (port 3000-3005)
|
||||
│ └── telegram-bot/ # Telegram bot (port 8002 internal)
|
||||
├── src/ # Unified Vue.js 3 frontend
|
||||
│ ├── modules/ # Feature modules
|
||||
│ │ ├── reports/ # Reports frontend
|
||||
│ │ └── data-entry/ # Data entry frontend
|
||||
│ ├── shared/ # Shared frontend components
|
||||
│ │ ├── components/ # Reusable Vue components
|
||||
│ │ └── stores/ # Pinia stores
|
||||
│ ├── assets/ # Global CSS, images
|
||||
│ ├── router/ # Vue Router
|
||||
│ └── App.vue # Root component
|
||||
│
|
||||
├── data-entry-app/ # DATA INPUT with approval workflow
|
||||
│ ├── backend/ # FastAPI API (port 8003) - SQLite + SQLModel
|
||||
│ ├── frontend/ # Vue.js 3 UI (port 3010)
|
||||
│ └── CLAUDE.md # ⚠️ READ THIS for data-entry work!
|
||||
├── shared/ # Shared backend components
|
||||
│ ├── database/ # Oracle pool
|
||||
│ ├── auth/ # JWT auth & middleware
|
||||
│ └── frontend/ # Shared frontend assets
|
||||
│ ├── components/ # LoginView.vue
|
||||
│ ├── stores/ # Auth store factory
|
||||
│ └── styles/ # Login CSS
|
||||
│
|
||||
├── docs/ # Architecture & style guides
|
||||
├── deployment/ # Production deployment scripts
|
||||
@@ -74,39 +92,49 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
### Starting Services
|
||||
|
||||
**Quick Start** (All services with parallel backend startup):
|
||||
**Quick Start** (Unified backend + frontend):
|
||||
```bash
|
||||
./start-dev.sh # Dev: Backend :8001, :8003, Bot :8002, Frontend :3000 (~11s)
|
||||
./start-test.sh # Test: Same ports (~33s - Oracle pool init takes longer)
|
||||
./start-prod.sh # Prod env: Backend :8001, Frontend :3000
|
||||
./start-test.sh # Test env: Backend :8001, Frontend :3000
|
||||
```
|
||||
|
||||
**Individual Service Control** (for quick development iterations):
|
||||
**Individual Service Control**:
|
||||
```bash
|
||||
./frontend.sh restart # Restart frontend only (~7s - fastest!)
|
||||
./backend-reports.sh start # Start Reports backend :8001
|
||||
./backend-data-entry.sh stop # Stop Data Entry backend :8003
|
||||
./bot.sh status # Check Telegram bot :8002 status
|
||||
./status.sh # Show all services status + health checks
|
||||
./start-frontend.sh restart # Restart frontend only (~7s - fastest!)
|
||||
./start-backend.sh # Start unified backend :8000 or :8001
|
||||
./status.sh # Show services status + health checks
|
||||
```
|
||||
|
||||
**Infrastructure**:
|
||||
```bash
|
||||
./ssh_tunnel.sh start # Oracle DB tunnel (production: 10.0.20.36)
|
||||
./ssh-tunnel-test.sh start # Oracle TEST tunnel (LXC: 10.0.20.121)
|
||||
./ssh-tunnel-prod.sh start # Oracle DB tunnel (production: 10.0.20.36)
|
||||
./ssh-tunnel-test.sh start # Oracle TEST tunnel (LXC: 10.0.20.121)
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- **87% faster frontend restart**: 7s vs 53s full restart
|
||||
- **38% faster full startup**: 33s vs 53s (test) via parallel backend init
|
||||
- **Granular control**: Restart individual services without affecting others
|
||||
- **Single backend process**: All modules in one FastAPI app
|
||||
- **Faster startup**: No multi-service coordination overhead
|
||||
- **Easier debugging**: Single process to monitor
|
||||
- **Shared resources**: Connection pools, cache, and middleware
|
||||
|
||||
### Key Architectural Decisions
|
||||
|
||||
**Core Architecture:**
|
||||
- **Ultrathin Monolith**: Single backend process with modular structure (`backend/modules/`)
|
||||
- **Single Worker Mode**: `--workers 1` (required for Telegram bot - see ADR-002 in `docs/ARCHITECTURE-DECISIONS.md`)
|
||||
- **Shared Database Pool**: Singleton `OraclePool` in `shared/database/oracle_pool.py` (python-oracledb with connection pooling)
|
||||
- **Centralized Auth**: JWT-based auth in `shared/auth/` with middleware auto-injecting `request.state.user`
|
||||
- **Two-Tier Cache System**: Hybrid L1 (Memory) + L2 (SQLite) cache in `backend/app/cache/` - **MANDATORY for all new endpoints**
|
||||
|
||||
**Module-Specific:**
|
||||
- **Module-Based Cache**: Each module can have its own cache strategy (Reports uses L1+L2 hybrid cache)
|
||||
- **Telegram Bot**: Integrated module with SQLite database (`backend/modules/data/telegram_bot.db`)
|
||||
- **FastAPI Structure**: Modules in `backend/modules/*/`, each with services, routers, models/schemas
|
||||
|
||||
**Infrastructure:**
|
||||
- **SSH Tunnel**: Required for Oracle DB connections (development/Linux) - see Database Setup
|
||||
- **FastAPI Structure**: Services in `backend/app/services/` with `@cached` decorator, routers in `backend/app/routers/`, models in `backend/app/models/` or `schemas/`
|
||||
- **Telegram Bot**: Standalone SQLite database for bot data, communicates with backend via HTTP API
|
||||
- **IIS Sub-Application**: Deployed at `/roa2web` path, not root (production)
|
||||
|
||||
> **For detailed rationale and trade-offs**, see `docs/ARCHITECTURE-DECISIONS.md`
|
||||
|
||||
---
|
||||
|
||||
@@ -117,14 +145,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
### SSH Tunnel (Development/Linux)
|
||||
```bash
|
||||
./ssh_tunnel.sh start # localhost:1526 → remote:1521
|
||||
./ssh_tunnel.sh status # Check tunnel
|
||||
./ssh_tunnel.sh stop # Stop tunnel
|
||||
./ssh-tunnel-prod.sh start # localhost:1526 → remote:1521
|
||||
./ssh-tunnel-prod.sh status # Check tunnel
|
||||
./ssh-tunnel-prod.sh stop # Stop tunnel
|
||||
```
|
||||
|
||||
**IMPORTANT**: Always ensure SSH tunnel is running before starting backend services.
|
||||
|
||||
### Environment Variables (`reports-app/backend/.env`)
|
||||
### Environment Variables (`backend/.env`)
|
||||
```bash
|
||||
# Oracle Database (through SSH tunnel)
|
||||
ORACLE_USER=CONTAFIN_ORACLE
|
||||
@@ -138,11 +166,14 @@ JWT_SECRET_KEY=your_secret_key
|
||||
JWT_ALGORITHM=HS256
|
||||
JWT_EXPIRE_MINUTES=30
|
||||
|
||||
# Telegram Bot Integration
|
||||
TELEGRAM_BOT_INTERNAL_API=http://localhost:8002
|
||||
# Module Configuration
|
||||
MODULE_REPORTS_ENABLED=true
|
||||
MODULE_DATA_ENTRY_ENABLED=true
|
||||
MODULE_TELEGRAM_ENABLED=true
|
||||
```
|
||||
|
||||
**Windows Production**: Direct Oracle connection, no SSH tunnel required. Ensure `TELEGRAM_BOT_INTERNAL_API` is set for auth code management.
|
||||
**Windows Production**: Direct Oracle connection, no SSH tunnel required.
|
||||
**Environment Files**: Multiple .env files available (.env.dev, .env.test, .env.prod) - see `backend/ENV-SETUP.md` for details.
|
||||
|
||||
---
|
||||
|
||||
@@ -156,7 +187,7 @@ TELEGRAM_BOT_INTERNAL_API=http://localhost:8002
|
||||
**Key Files**:
|
||||
- `shared/auth/middleware.py` - FastAPI middleware with rate limiting (5 req/5 min)
|
||||
- `shared/auth/jwt_handler.py` - Token creation/validation
|
||||
- `reports-app/backend/app/main.py` - Auth router inline definition
|
||||
- `backend/main.py` - Main FastAPI app with auth router registration
|
||||
|
||||
---
|
||||
|
||||
@@ -165,10 +196,10 @@ TELEGRAM_BOT_INTERNAL_API=http://localhost:8002
|
||||
### Adding a New API Endpoint
|
||||
**IMPORTANT**: Always use the cache system for database queries to improve performance.
|
||||
|
||||
1. Create **service** in `reports-app/backend/app/services/your_service.py` (NOT in router!)
|
||||
2. Define Pydantic schemas in `app/schemas/` or `app/models/`
|
||||
1. Create **service** in `backend/modules/reports/services/your_service.py` (NOT in router!)
|
||||
2. Define Pydantic schemas in `modules/*/schemas/` or `modules/*/models/`
|
||||
3. **Add caching** using `@cached` decorator in service methods
|
||||
4. Create router in `reports-app/backend/app/routers/your_router.py` (calls service)
|
||||
4. Create router in `backend/modules/reports/routers/your_router.py` (calls service)
|
||||
5. Register router in `app/main.py`: `app.include_router(your_router, prefix="/api/your_prefix")`
|
||||
|
||||
**Service Example with Caching** (RECOMMENDED):
|
||||
@@ -278,7 +309,7 @@ ttl_your_data: int = int(os.getenv('CACHE_TTL_YOUR_DATA', '600')) # 10 min defa
|
||||
### Adding a New Telegram Bot Command
|
||||
**IMPORTANT**: Follow established command patterns and formatting.
|
||||
|
||||
**Before coding**: Read **`reports-app/telegram-bot/TELEGRAM_COMMANDS.md`** for command patterns → See "Documentation Index" for complete guides.
|
||||
**Before coding**: Read **`docs/telegram/TELEGRAM_BUTTON_INTERFACE_PLAN.md`** for command patterns → See "Documentation Index" for complete guides.
|
||||
|
||||
**Standard Pattern** (add handler in `app/bot/handlers.py`):
|
||||
```python
|
||||
@@ -338,14 +369,12 @@ const response = await api.get('/endpoint');
|
||||
→ **`README.md`** - Project overview, setup, development commands, testing, deployment
|
||||
|
||||
### Data Entry App (Bonuri Fiscale)
|
||||
- **`data-entry-app/CLAUDE.md`** - ⚠️ **READ FIRST** when working on data-entry
|
||||
- `data-entry-app/README.md` - Quick start guide
|
||||
- **`docs/data-entry/CLAUDE.md`** - ⚠️ **READ FIRST** when working on data-entry
|
||||
- `docs/data-entry/README.md` - Quick start guide
|
||||
- `docs/data-entry/REQUIREMENTS.md` - Functional requirements
|
||||
- `docs/data-entry/ARCHITECTURE.md` - Technical architecture (SQLModel, workflow)
|
||||
|
||||
### Architecture & Planning
|
||||
- **`docs/ARCHITECTURE_SCHEMA.md`** - Architecture diagrams, cache system, and schemas
|
||||
- `docs/MICROSERVICES_GUIDE.md` - Microservices architecture details
|
||||
- `DEVELOPMENT_BLUEPRINT.md` - Detailed development plan
|
||||
|
||||
### Frontend Development
|
||||
@@ -355,20 +384,19 @@ const response = await api.get('/endpoint');
|
||||
- `docs/STYLING_GUIDELINES.md` - CSS best practices and conventions
|
||||
- `docs/COMPONENT_STYLING.md` - Component-specific styling guide
|
||||
- `docs/FORM_TEMPLATE.md` - Standardized form template
|
||||
- `reports-app/frontend/README.md` - Frontend architecture and setup
|
||||
- `reports-app/frontend/tests/README.md` - Playwright E2E testing guide
|
||||
- `Frontend module documentation in src/modules/` - Frontend architecture and setup
|
||||
- `Playwright E2E testing (see tests/ directory)` - Playwright E2E testing guide
|
||||
|
||||
### Backend Development
|
||||
- `reports-app/backend/README.md` - Backend architecture and API details
|
||||
- `Backend module documentation in backend/modules/` - Backend architecture and API details
|
||||
- API endpoints documented in `README.md` (Authentication, Companies, Dashboard, Invoices, Treasury, Telegram)
|
||||
|
||||
### Telegram Bot Development
|
||||
- **`reports-app/telegram-bot/README.md`** - Complete bot architecture and development guide (START HERE)
|
||||
- **`reports-app/telegram-bot/TELEGRAM_COMMANDS.md`** - Command reference and patterns
|
||||
- **`docs/telegram/README.md`** - Complete bot architecture and development guide (START HERE)
|
||||
- **`docs/telegram/TELEGRAM_BUTTON_INTERFACE_PLAN.md`** - Command reference and patterns
|
||||
- `tests/MANUAL_TESTING_CHECKLIST.md` - Manual testing procedures
|
||||
|
||||
### Deployment
|
||||
- **`DEPLOYMENT_GUIDE.md`** - Production deployment (Linux/Docker & Windows/IIS)
|
||||
- `deployment/windows/README.md` - Windows deployment quick start
|
||||
- `deployment/windows/docs/WINDOWS_DEPLOYMENT.md` - Complete Windows guide
|
||||
- `deployment/windows/docs/TELEGRAM_BOT_TROUBLESHOOTING.md` - Bot troubleshooting
|
||||
|
||||
@@ -1,386 +0,0 @@
|
||||
# Plan: Receipt Scanning Workflow Improvements
|
||||
|
||||
> **Context Handover Document** - Created for session continuity
|
||||
> **Date**: 2025-12-15
|
||||
> **Status**: Ready for implementation
|
||||
|
||||
## Overview
|
||||
Improve the data-entry-app receipt scanning to:
|
||||
1. Save supplier name, CUI, and OCR text in drafts
|
||||
2. Make supplier validation assistive (not blocking)
|
||||
3. Unify create/edit forms with OCR rescan capability
|
||||
4. Fix image resize bug (>4000px)
|
||||
5. **NEW: Extract payment methods (CARD/NUMERAR) from OCR**
|
||||
|
||||
## Requirements Summary
|
||||
- **Drafts**: Save `cui` + `partner_name` + `ocr_raw_text` + `payment_methods` from OCR
|
||||
- **Supplier match**: Auto-fill but editable (for assistance, not validation)
|
||||
- **No match**: Show warning only, allow saving draft
|
||||
- **Edit mode**: Allow OCR rescan on existing drafts
|
||||
- **Approval**: Requires valid `cui` only (NOT partner_id) - ROA has stored procedure for supplier lookup
|
||||
- **Image resize**: Cap at 4000px BEFORE upscaling
|
||||
- **Payment methods**: Extract CARD/NUMERAR amounts (after TOTAL LEI, before TOTAL TVA)
|
||||
|
||||
---
|
||||
|
||||
## Part 1: Backend Model & Database
|
||||
|
||||
### 1.1 Add Fields to Receipt Model
|
||||
**File**: `data-entry-app/backend/app/db/models/receipt.py`
|
||||
|
||||
Add after line 66 (after `partner_name`):
|
||||
```python
|
||||
cui: Optional[str] = Field(default=None, max_length=20) # Fiscal code from OCR
|
||||
ocr_raw_text: Optional[str] = Field(default=None) # Raw OCR text for debugging
|
||||
payment_methods: Optional[str] = Field(default=None, max_length=500) # JSON: [{"method":"CARD","amount":"50.00"}]
|
||||
```
|
||||
|
||||
### 1.2 Create Alembic Migration
|
||||
**File**: `data-entry-app/backend/migrations/versions/XXXX_add_ocr_fields.py`
|
||||
|
||||
```python
|
||||
def upgrade():
|
||||
with op.batch_alter_table('receipts') as batch_op:
|
||||
batch_op.add_column(sa.Column('cui', sa.String(20), nullable=True))
|
||||
batch_op.add_column(sa.Column('ocr_raw_text', sa.Text(), nullable=True))
|
||||
batch_op.add_column(sa.Column('payment_methods', sa.String(500), nullable=True))
|
||||
```
|
||||
|
||||
### 1.3 Update Pydantic Schemas
|
||||
**File**: `data-entry-app/backend/app/schemas/receipt.py`
|
||||
|
||||
**Add PaymentMethodSchema** (after TvaEntrySchema ~line 75):
|
||||
```python
|
||||
class PaymentMethodSchema(BaseModel):
|
||||
"""Payment method entry (CARD/NUMERAR)."""
|
||||
method: str = Field(description="Payment method: CARD or NUMERAR")
|
||||
amount: Decimal = Field(description="Amount paid with this method")
|
||||
```
|
||||
|
||||
**ReceiptBase** (after line 97):
|
||||
```python
|
||||
cui: Optional[str] = Field(default=None, max_length=20)
|
||||
ocr_raw_text: Optional[str] = Field(default=None)
|
||||
payment_methods: Optional[List[PaymentMethodSchema]] = Field(default=None, description="Payment methods from OCR")
|
||||
```
|
||||
|
||||
**ReceiptUpdate** (after line 125):
|
||||
```python
|
||||
cui: Optional[str] = Field(default=None, max_length=20)
|
||||
ocr_raw_text: Optional[str] = Field(default=None)
|
||||
payment_methods: Optional[List[PaymentMethodSchema]] = Field(default=None)
|
||||
```
|
||||
|
||||
**ReceiptResponse**: Add validator to parse `payment_methods` from JSON (similar to `parse_tva_breakdown`)
|
||||
|
||||
---
|
||||
|
||||
## Part 2: Fix Image Resize Bug
|
||||
|
||||
**File**: `data-entry-app/backend/app/services/image_preprocessor.py`
|
||||
|
||||
### 2.1 Update `preprocess_light()` (after line 55)
|
||||
Add downscale BEFORE upscale:
|
||||
```python
|
||||
# 2a. Scale DOWN if any side exceeds 4000px (PaddleOCR limit)
|
||||
height, width = gray.shape
|
||||
max_side = max(height, width)
|
||||
if max_side > 4000:
|
||||
scale = 4000 / max_side
|
||||
gray = cv2.resize(gray, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA)
|
||||
height, width = gray.shape
|
||||
|
||||
# 2b. Scale UP if too small
|
||||
if width < 1500:
|
||||
scale = 1500 / width
|
||||
gray = cv2.resize(gray, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC)
|
||||
```
|
||||
|
||||
### 2.2 Update `preprocess_heavy()` (after line 82)
|
||||
Same downscale logic before the existing upscale at lines 85-88.
|
||||
|
||||
---
|
||||
|
||||
## Part 3: Backend OCR Endpoint - Return Raw Text
|
||||
|
||||
**File**: `data-entry-app/backend/app/routers/ocr.py`
|
||||
|
||||
Ensure the OCR extraction endpoint returns `raw_text` in the response (verify this is already included in the OCR service output).
|
||||
|
||||
---
|
||||
|
||||
## Part 4: Frontend Form Unification
|
||||
|
||||
### 4.1 Unify OCR Zone for Create & Edit
|
||||
**File**: `data-entry-app/frontend/src/views/receipts/ReceiptCreateView.vue`
|
||||
|
||||
**Change line 19** from:
|
||||
```vue
|
||||
<div class="upload-section" v-if="!isEditMode">
|
||||
```
|
||||
to:
|
||||
```vue
|
||||
<div class="upload-section">
|
||||
```
|
||||
|
||||
**Update header text** (around line 23):
|
||||
```vue
|
||||
<h3>
|
||||
<i class="pi pi-camera"></i>
|
||||
{{ isEditMode ? 'Re-scanare OCR (opțional)' : 'Poză Bon (obligatoriu)' }}
|
||||
</h3>
|
||||
```
|
||||
|
||||
### 4.2 Add CUI Field to Form State
|
||||
**File**: `data-entry-app/frontend/src/views/receipts/ReceiptCreateView.vue`
|
||||
|
||||
Add to form ref initialization:
|
||||
```javascript
|
||||
cui: '',
|
||||
ocr_raw_text: '',
|
||||
```
|
||||
|
||||
### 4.3 Add CUI Display Field
|
||||
Add after Furnizor dropdown (around line 210):
|
||||
```vue
|
||||
<div class="form-field">
|
||||
<label>CUI (Cod Fiscal)</label>
|
||||
<InputText v-model="form.cui" placeholder="Ex: RO12345678" />
|
||||
<small v-if="form.cui && !form.partner_id" class="p-text-warning">
|
||||
<i class="pi pi-exclamation-triangle"></i>
|
||||
CUI negăsit în nomenclator
|
||||
</small>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 4.4 Change Supplier Dialog to Warning Banner
|
||||
**Current behavior** (lines 555-563): When CUI not found, opens blocking dialog.
|
||||
|
||||
**New behavior**: Show non-blocking warning message.
|
||||
|
||||
Replace the `else` block in `applyOCRData()`:
|
||||
```javascript
|
||||
} else {
|
||||
// Not found - show warning but allow continuing
|
||||
supplierWarning.value = {
|
||||
show: true,
|
||||
cui: data.cui,
|
||||
name: data.partner_name || ''
|
||||
}
|
||||
// Still set form values from OCR
|
||||
form.value.cui = data.cui
|
||||
form.value.partner_name = data.partner_name || ''
|
||||
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
summary: 'Furnizor negăsit',
|
||||
detail: `CUI ${data.cui} nu a fost găsit în nomenclator`,
|
||||
life: 5000
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Add ref for warning state:
|
||||
```javascript
|
||||
const supplierWarning = ref({ show: false, cui: '', name: '' })
|
||||
```
|
||||
|
||||
### 4.5 Update `applyOCRData()` to Save Raw Text
|
||||
Add to the function:
|
||||
```javascript
|
||||
if (data.cui) form.value.cui = data.cui
|
||||
if (data.raw_text) form.value.ocr_raw_text = data.raw_text
|
||||
```
|
||||
|
||||
### 4.6 Update `loadReceipt()` for Edit Mode
|
||||
Add to existing field mapping:
|
||||
```javascript
|
||||
cui: receipt.value.cui || '',
|
||||
ocr_raw_text: receipt.value.ocr_raw_text || '',
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 5: Backend Approval Validation
|
||||
|
||||
**File**: `data-entry-app/backend/app/services/receipt_service.py`
|
||||
|
||||
In `approve_receipt()` method, add validation:
|
||||
```python
|
||||
if not receipt.cui:
|
||||
return False, "Trebuie completat codul fiscal (CUI) pentru aprobare", None
|
||||
```
|
||||
|
||||
**Note**: At approval, only `cui` (fiscal code) is required, NOT `partner_id`.
|
||||
The ROA ERP has a stored procedure that searches/creates suppliers based on `cui`.
|
||||
The `partner_id` is only populated later during Oracle import phase.
|
||||
|
||||
---
|
||||
|
||||
## Part 6: OCR Payment Methods Extraction
|
||||
|
||||
### 6.1 Update ExtractionResult Dataclass
|
||||
**File**: `data-entry-app/backend/app/services/ocr_extractor.py`
|
||||
|
||||
Add to `ExtractionResult` (after line 24, after `items_count`):
|
||||
```python
|
||||
payment_methods: List[dict] = field(default_factory=list) # [{"method":"CARD","amount":Decimal}]
|
||||
```
|
||||
|
||||
### 6.2 Add Payment Method Patterns
|
||||
**File**: `data-entry-app/backend/app/services/ocr_extractor.py`
|
||||
|
||||
Add new patterns (after TVA_PATTERNS ~line 184):
|
||||
```python
|
||||
# Payment method patterns - appears after TOTAL LEI, before TOTAL TVA
|
||||
# Format: "CARD: 50.00" or "NUMERAR 100.00" or "PLATA CARD: 50.00"
|
||||
PAYMENT_METHOD_PATTERNS = [
|
||||
# CARD with amount
|
||||
(r'(?:PLATA\s+)?CARD\s*:?\s*([\d\s.,]+)', 'CARD', 0.95),
|
||||
# NUMERAR (cash) with amount
|
||||
(r'NUMERAR\s*:?\s*([\d\s.,]+)', 'NUMERAR', 0.95),
|
||||
# CASH alternative spelling
|
||||
(r'CASH\s*:?\s*([\d\s.,]+)', 'NUMERAR', 0.90),
|
||||
]
|
||||
```
|
||||
|
||||
### 6.3 Add Extraction Method
|
||||
**File**: `data-entry-app/backend/app/services/ocr_extractor.py`
|
||||
|
||||
Add new method `_extract_payment_methods()` (after `_extract_address` ~line 996):
|
||||
```python
|
||||
def _extract_payment_methods(self, text: str) -> List[dict]:
|
||||
"""
|
||||
Extract payment methods (CARD/NUMERAR) from receipt.
|
||||
These appear after TOTAL LEI and before TOTAL TVA section.
|
||||
|
||||
Returns list of: {'method': 'CARD'/'NUMERAR', 'amount': Decimal}
|
||||
"""
|
||||
payment_methods = []
|
||||
seen_methods = set()
|
||||
|
||||
# Normalize spaces in numbers
|
||||
normalized_text = re.sub(r'(\d+)[.,]\s+(\d{2})', r'\1.\2', text)
|
||||
|
||||
# Find the region between TOTAL LEI and TOTAL TVA
|
||||
total_lei_match = re.search(r'TOTAL\s+LEI\s*([\d\s.,]+)', normalized_text, re.IGNORECASE)
|
||||
total_tva_match = re.search(r'TOTAL\s+T[VU][AR]', normalized_text, re.IGNORECASE)
|
||||
|
||||
# Define search region (after TOTAL LEI, before TOTAL TVA if exists)
|
||||
if total_lei_match:
|
||||
start_pos = total_lei_match.end()
|
||||
end_pos = total_tva_match.start() if total_tva_match else len(normalized_text)
|
||||
search_region = normalized_text[start_pos:end_pos]
|
||||
else:
|
||||
search_region = normalized_text # Fallback to full text
|
||||
|
||||
for pattern, method, confidence in self.PAYMENT_METHOD_PATTERNS:
|
||||
for match in re.finditer(pattern, search_region, re.IGNORECASE):
|
||||
try:
|
||||
amount_str = match.group(1).replace(' ', '')
|
||||
amount_str = self._normalize_number(re.sub(r'[^\d.,]', '', amount_str))
|
||||
amount = Decimal(amount_str)
|
||||
if amount > 0 and method not in seen_methods:
|
||||
payment_methods.append({
|
||||
'method': method,
|
||||
'amount': amount
|
||||
})
|
||||
seen_methods.add(method)
|
||||
except (InvalidOperation, ValueError):
|
||||
continue
|
||||
|
||||
return payment_methods
|
||||
```
|
||||
|
||||
### 6.4 Call Extraction in `extract()` Method
|
||||
**File**: `data-entry-app/backend/app/services/ocr_extractor.py`
|
||||
|
||||
Add to `extract()` method (after line 255, after `result.address = ...`):
|
||||
```python
|
||||
result.payment_methods = self._extract_payment_methods(text_upper)
|
||||
```
|
||||
|
||||
### 6.5 Frontend - Add Payment Methods Display
|
||||
**File**: `data-entry-app/frontend/src/views/receipts/ReceiptCreateView.vue`
|
||||
|
||||
Add to form ref:
|
||||
```javascript
|
||||
payment_methods: [],
|
||||
```
|
||||
|
||||
Add to `applyOCRData()`:
|
||||
```javascript
|
||||
if (data.payment_methods) form.value.payment_methods = data.payment_methods
|
||||
```
|
||||
|
||||
Add UI display (after TVA breakdown section):
|
||||
```vue
|
||||
<!-- Payment Methods (from OCR) -->
|
||||
<div class="form-field" v-if="form.payment_methods && form.payment_methods.length > 0">
|
||||
<label>Modalități Plată</label>
|
||||
<div class="payment-methods-display">
|
||||
<Tag v-for="pm in form.payment_methods" :key="pm.method"
|
||||
:severity="pm.method === 'CARD' ? 'info' : 'success'"
|
||||
:value="`${pm.method}: ${formatCurrency(pm.amount)}`" />
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
| Step | Task | Files |
|
||||
|------|------|-------|
|
||||
| 1 | Add `cui`, `ocr_raw_text`, `payment_methods` to model | `models/receipt.py` |
|
||||
| 2 | Create migration | `migrations/versions/...` |
|
||||
| 3 | Update schemas | `schemas/receipt.py` |
|
||||
| 4 | Fix image resize | `services/image_preprocessor.py` |
|
||||
| 5 | Add payment methods extraction to OCR | `services/ocr_extractor.py` |
|
||||
| 6 | Unify frontend form + add new fields | `views/receipts/ReceiptCreateView.vue` |
|
||||
| 7 | Add approval validation | `services/receipt_service.py` |
|
||||
| 8 | Test full workflow | Manual testing |
|
||||
|
||||
---
|
||||
|
||||
## Files to Modify
|
||||
|
||||
### Backend
|
||||
- `data-entry-app/backend/app/db/models/receipt.py` - Add cui, ocr_raw_text, payment_methods fields
|
||||
- `data-entry-app/backend/app/schemas/receipt.py` - Add PaymentMethodSchema, update schemas
|
||||
- `data-entry-app/backend/app/services/image_preprocessor.py` - Fix resize bug (cap at 4000px)
|
||||
- `data-entry-app/backend/app/services/ocr_extractor.py` - Add payment methods extraction
|
||||
- `data-entry-app/backend/app/services/receipt_service.py` - Add approval validation
|
||||
- `data-entry-app/backend/migrations/versions/` - New migration
|
||||
|
||||
### Frontend
|
||||
- `data-entry-app/frontend/src/views/receipts/ReceiptCreateView.vue` - Unify form, add CUI + payment methods fields, change dialog to warning
|
||||
|
||||
---
|
||||
|
||||
## Expected Behavior After Implementation
|
||||
|
||||
1. **OCR Scan**: Extracts supplier name, CUI, raw text, payment methods → all saved to draft
|
||||
2. **Payment Methods**: CARD/NUMERAR amounts extracted (after TOTAL LEI, before TOTAL TVA)
|
||||
3. **CUI Match**: Auto-fills supplier name from ROA, user can edit
|
||||
4. **CUI No Match**: Shows warning toast, allows saving draft with OCR data
|
||||
5. **Edit Mode**: Can re-scan OCR to update extracted data
|
||||
6. **Approval**: Requires valid `cui` (fiscal code) - NOT partner_id
|
||||
7. **Oracle Import** (later): Uses `cui` to find/create supplier via ROA stored procedure
|
||||
8. **Large Images**: Automatically resized to max 4000px before OCR
|
||||
|
||||
---
|
||||
|
||||
## Romanian Receipt Structure Reference
|
||||
```
|
||||
NUME FIRMA S.R.L.
|
||||
CIF: RO12345678
|
||||
STR. EXEMPLU NR. 1
|
||||
|
||||
[Product lines...]
|
||||
|
||||
TOTAL LEI 150.00 ← Total amount
|
||||
CARD 50.00 ← Payment method 1 (NEW)
|
||||
NUMERAR 100.00 ← Payment method 2 (NEW)
|
||||
TOTAL TVA A-19% 23.95 ← TVA breakdown
|
||||
```
|
||||
@@ -1,285 +0,0 @@
|
||||
# Plan: Consolidare ROA2WEB - Pragmatic Monolith
|
||||
|
||||
> **Branch**: `feature/unified-app-pragmatic-monolith`
|
||||
> **Status**: APROBAT - Ready for Implementation
|
||||
> **Efort estimat**: ~2.5 zile
|
||||
|
||||
---
|
||||
|
||||
## Context și Problema
|
||||
|
||||
### Situația Curentă
|
||||
- 2 aplicații frontend separate în IIS: `/roa2web/` și `/data-entry/`
|
||||
- Deploy greoi (2 build-uri, 2 configurații IIS separate)
|
||||
- Componentele shared cauzează probleme CSS cross-app (text alb pe fundal alb)
|
||||
- Nu există meniu unificat între aplicații
|
||||
|
||||
### Obiectiv
|
||||
- Un singur meniu cu ambele aplicații (Reports + Data Entry)
|
||||
- Deploy simplificat (un build, un site IIS)
|
||||
- Izolare între module (bug în Reports să nu afecteze DataEntry)
|
||||
- URL-uri pe root: `/reports/*`, `/data-entry/*`
|
||||
|
||||
---
|
||||
|
||||
## Decizie Arhitecturală: Pragmatic Monolith
|
||||
|
||||
### De ce NU Micro-frontends?
|
||||
| Criteriu pentru MFE | ROA2WEB | Necesită MFE? |
|
||||
|---------------------|---------|---------------|
|
||||
| 20+ dezvoltatori | 1 dev | ❌ Nu |
|
||||
| Deploy de multe ori/zi | Săptămânal | ❌ Nu |
|
||||
| Milioane de utilizatori | 1-5 concurenți | ❌ Nu |
|
||||
| Framework-uri diferite | Vue only | ❌ Nu |
|
||||
|
||||
**Verdict**: Module Federation / Single-SPA = OVERKILL
|
||||
|
||||
### Abordare Aleasă: Monolith cu Mecanisme de Izolare
|
||||
- **Error Boundaries** per modul (bug în Reports nu strică DataEntry)
|
||||
- **Lazy Loading** (bundle-uri separate, încărcate la nevoie)
|
||||
- **Stores izolate** per modul
|
||||
- **Feature flags** pentru control
|
||||
|
||||
**Blast radius cu protecții: 50-70%** (aproape ca 2 apps separate!)
|
||||
|
||||
---
|
||||
|
||||
## Arhitectura Finală
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ROA2WEB Unified SPA │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
|
||||
│ │ Reports Module │ │ DataEntry Module │ │ Shared │ │
|
||||
│ │ /reports/* │ │ /data-entry/* │ │ - Auth │ │
|
||||
│ │ (lazy loaded) │ │ (lazy loaded) │ │ - Company │ │
|
||||
│ │ │ │ │ │ - Period │ │
|
||||
│ │ ErrorBoundary │ │ ErrorBoundary │ │ - Header │ │
|
||||
│ └────────┬────────┘ └────────┬─────────┘ └──────────────┘ │
|
||||
│ │ │ │
|
||||
│ ┌────────┴────────────────────┴─────────┐ │
|
||||
│ │ Vue Router + Global Error Handler │ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────┴─────────┐
|
||||
│ IIS Proxy │
|
||||
└─────────┬─────────┘
|
||||
┌───────────────┴───────────────┐
|
||||
│ │
|
||||
┌────────▼────────┐ ┌──────────▼────────┐
|
||||
│ Reports Backend │ │ DataEntry Backend │
|
||||
│ port 8001 │ │ port 8003 │
|
||||
│ (Oracle RO) │ │ (SQLite + Oracle) │
|
||||
└─────────────────┘ └───────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Structura Proiect Unified
|
||||
|
||||
```
|
||||
roa2web/
|
||||
├── src/
|
||||
│ ├── main.js
|
||||
│ ├── App.vue # Meniu unificat
|
||||
│ ├── router/index.js # Rute unificate cu lazy loading
|
||||
│ │
|
||||
│ ├── modules/
|
||||
│ │ ├── reports/ # MODUL IZOLAT
|
||||
│ │ │ ├── ReportsLayout.vue # Error boundary pentru modul
|
||||
│ │ │ ├── views/
|
||||
│ │ │ │ ├── DashboardView.vue
|
||||
│ │ │ │ ├── InvoicesView.vue
|
||||
│ │ │ │ ├── BankCashRegisterView.vue
|
||||
│ │ │ │ ├── TrialBalanceView.vue
|
||||
│ │ │ │ ├── TelegramView.vue
|
||||
│ │ │ │ └── CacheStatsView.vue
|
||||
│ │ │ ├── stores/
|
||||
│ │ │ └── services/
|
||||
│ │ │
|
||||
│ │ └── data-entry/ # MODUL IZOLAT
|
||||
│ │ ├── DataEntryLayout.vue
|
||||
│ │ ├── views/
|
||||
│ │ │ ├── ReceiptsListView.vue
|
||||
│ │ │ └── ReceiptCreateView.vue
|
||||
│ │ ├── stores/
|
||||
│ │ └── services/
|
||||
│ │
|
||||
│ ├── shared/ # Shared între module
|
||||
│ │ ├── components/ # AppHeader, SlideMenu, CompanySelector, etc.
|
||||
│ │ ├── stores/ # Auth, Company, Period
|
||||
│ │ └── styles/
|
||||
│ │
|
||||
│ └── config/
|
||||
│ ├── menu.js # Configurație meniu unificat
|
||||
│ └── features.js # Feature flags
|
||||
│
|
||||
├── vite.config.js
|
||||
└── package.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pași Implementare
|
||||
|
||||
### Faza 1: Setup Proiect (0.5 zile)
|
||||
1. Creează directorul `roa2web/`
|
||||
2. Setup `package.json` cu dependencies combinate din ambele apps
|
||||
3. Setup `vite.config.js` cu:
|
||||
- Dual proxy: `/api/reports/*` → `:8001`, `/api/data-entry/*` → `:8003`
|
||||
- Lazy loading chunks configuration
|
||||
- Base path `/`
|
||||
4. Setup `main.js` cu Pinia, PrimeVue, Router
|
||||
|
||||
### Faza 2: Migrare Module (1 zi)
|
||||
1. Copiază views din `reports-app/frontend/src/views/` → `modules/reports/views/`
|
||||
2. Copiază views din `data-entry-app/frontend/src/views/` → `modules/data-entry/views/`
|
||||
3. Copiază stores specifice fiecărui modul
|
||||
4. Creează `ReportsLayout.vue` și `DataEntryLayout.vue` cu error boundaries
|
||||
5. Setup router unificat:
|
||||
```javascript
|
||||
const routes = [
|
||||
{ path: '/login', component: LoginView },
|
||||
{
|
||||
path: '/reports',
|
||||
component: () => import('./modules/reports/ReportsLayout.vue'),
|
||||
children: [
|
||||
{ path: 'dashboard', component: () => import('./modules/reports/views/DashboardView.vue') },
|
||||
{ path: 'invoices', component: () => import('./modules/reports/views/InvoicesView.vue') },
|
||||
// ...
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/data-entry',
|
||||
component: () => import('./modules/data-entry/DataEntryLayout.vue'),
|
||||
children: [
|
||||
{ path: '', component: () => import('./modules/data-entry/views/ReceiptsListView.vue') },
|
||||
{ path: 'create', component: () => import('./modules/data-entry/views/ReceiptCreateView.vue') },
|
||||
]
|
||||
},
|
||||
{ path: '/', redirect: '/reports/dashboard' }
|
||||
]
|
||||
```
|
||||
|
||||
### Faza 3: Izolare și Resilience (0.5 zile)
|
||||
1. Implementează `ErrorBoundary.vue`:
|
||||
```vue
|
||||
<template>
|
||||
<div v-if="error" class="module-error">
|
||||
<h3>⚠️ {{ moduleName }} a întâmpinat o eroare</h3>
|
||||
<p>{{ error.message }}</p>
|
||||
<button @click="retry">Reîncearcă</button>
|
||||
</div>
|
||||
<slot v-else />
|
||||
</template>
|
||||
```
|
||||
2. Separă stores per modul (nu global)
|
||||
3. Adaugă feature flags în `config/features.js`
|
||||
4. Testează izolarea: introduce bug în Reports, verifică că DataEntry funcționează
|
||||
|
||||
### Faza 4: Build & Deploy (0.5 zile)
|
||||
1. Verifică bundle splitting cu `npm run build`:
|
||||
```
|
||||
dist/assets/
|
||||
├── index-[hash].js # Shell + shared (~150KB)
|
||||
├── reports-[hash].js # Reports module (~200KB)
|
||||
└── data-entry-[hash].js # DataEntry module (~100KB)
|
||||
```
|
||||
2. Update IIS web.config pentru SPA routing (toate rutele → index.html)
|
||||
3. Update deployment scripts pentru single app
|
||||
4. Test end-to-end pe server
|
||||
5. Deploy în producție
|
||||
|
||||
---
|
||||
|
||||
## Meniu Unificat
|
||||
|
||||
```javascript
|
||||
// config/menu.js
|
||||
export const menuSections = [
|
||||
{
|
||||
title: 'Rapoarte',
|
||||
items: [
|
||||
{ to: '/reports/dashboard', icon: 'pi pi-home', label: 'Dashboard' },
|
||||
{ to: '/reports/invoices', icon: 'pi pi-file', label: 'Facturi' },
|
||||
{ to: '/reports/bank-cash', icon: 'pi pi-money-bill', label: 'Casa și Banca' },
|
||||
{ to: '/reports/trial-balance', icon: 'pi pi-calculator', label: 'Balanță' },
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Introduceri Date',
|
||||
items: [
|
||||
{ to: '/data-entry', icon: 'pi pi-list', label: 'Lista Bonuri' },
|
||||
{ to: '/data-entry/create', icon: 'pi pi-plus', label: 'Bon Nou' },
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Sistem',
|
||||
items: [
|
||||
{ to: '/reports/telegram', icon: 'pi pi-telegram', label: 'Telegram' },
|
||||
{ to: '/reports/cache-stats', icon: 'pi pi-chart-bar', label: 'Cache Stats' },
|
||||
]
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fișiere de Referință
|
||||
|
||||
### Fișiere Existente (sursă pentru copiere)
|
||||
- `reports-app/frontend/src/views/*.vue` - Views Reports
|
||||
- `reports-app/frontend/src/stores/*.js` - Stores Reports
|
||||
- `reports-app/frontend/src/services/api.js` - API service Reports
|
||||
- `data-entry-app/frontend/src/views/*.vue` - Views DataEntry
|
||||
- `data-entry-app/frontend/src/stores/*.js` - Stores DataEntry
|
||||
- `data-entry-app/frontend/src/services/api.js` - API service DataEntry
|
||||
- `shared/frontend/components/` - Componente shared (AppHeader, SlideMenu, etc.)
|
||||
- `shared/frontend/stores/` - Stores shared (auth, companies, accountingPeriod)
|
||||
|
||||
### Configurații de Referință
|
||||
- `reports-app/frontend/vite.config.js` - Pentru proxy și build config
|
||||
- `reports-app/frontend/package.json` - Pentru dependencies
|
||||
- `data-entry-app/frontend/vite.config.js` - Pentru proxy config
|
||||
- `data-entry-app/frontend/package.json` - Pentru dependencies
|
||||
|
||||
---
|
||||
|
||||
## URL-uri Finale
|
||||
|
||||
| Path | Descriere |
|
||||
|------|-----------|
|
||||
| `/` | Redirect la `/reports/dashboard` |
|
||||
| `/login` | Pagină login |
|
||||
| `/reports/dashboard` | Dashboard principal |
|
||||
| `/reports/invoices` | Facturi |
|
||||
| `/reports/bank-cash` | Casa și Banca |
|
||||
| `/reports/trial-balance` | Balanță de Verificare |
|
||||
| `/reports/telegram` | Telegram Bot |
|
||||
| `/reports/cache-stats` | Statistici Cache |
|
||||
| `/data-entry` | Lista Bonuri |
|
||||
| `/data-entry/create` | Bon Nou |
|
||||
| `/data-entry/:id` | Detalii Bon |
|
||||
| `/data-entry/:id/edit` | Editare Bon |
|
||||
|
||||
---
|
||||
|
||||
## Note Importante
|
||||
|
||||
1. **Backend-urile rămân separate** - port 8001 (Reports) și port 8003 (DataEntry)
|
||||
2. **IIS proxy routing** - trebuie configurat pentru a ruta API calls corect
|
||||
3. **Error Boundaries** - critice pentru izolarea modulelor
|
||||
4. **Lazy Loading** - asigură bundle splitting corect
|
||||
5. **Feature flags** - permit dezactivarea unui modul fără redeploy
|
||||
|
||||
---
|
||||
|
||||
## Handover Notes
|
||||
|
||||
Această implementare va înlocui cele 2 aplicații separate cu o aplicație unificată.
|
||||
După implementare, directoarele `reports-app/frontend/` și `data-entry-app/frontend/`
|
||||
pot fi arhivate/șterse, păstrând doar `unified-app/`.
|
||||
|
||||
Backend-urile rămân neschimbate în `reports-app/backend/` și `data-entry-app/backend/`.
|
||||
140
QUICK-START.md
Normal file
140
QUICK-START.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# ROA2WEB Ultrathin Monolith - Quick Start 🚀
|
||||
|
||||
## Pornire Rapidă
|
||||
|
||||
### PROD Environment (server PRODUCȚIE 10.0.20.36)
|
||||
```bash
|
||||
./start-prod.sh # Pornește tot: SSH tunnel + backend + frontend
|
||||
./start-prod.sh stop # Oprește toate serviciile
|
||||
```
|
||||
|
||||
### TEST Environment (server TEST 10.0.20.121)
|
||||
```bash
|
||||
./start-test.sh # Pornește tot: SSH tunnel + backend + frontend
|
||||
./start-test.sh stop # Oprește toate serviciile
|
||||
```
|
||||
|
||||
## Verificare Status
|
||||
|
||||
```bash
|
||||
./status.sh # Arată status-ul tuturor serviciilor
|
||||
```
|
||||
|
||||
## Ce s-a Schimbat?
|
||||
|
||||
### Înainte (3 Backend-uri Separate)
|
||||
```
|
||||
Reports Backend → Port 8001
|
||||
Data Entry Backend → Port 8003
|
||||
Telegram Bot → Port 8002
|
||||
```
|
||||
|
||||
### Acum (1 Backend Unificat)
|
||||
```
|
||||
Unified Backend → Port 8000
|
||||
├── Reports API: /api/reports/*
|
||||
├── Data Entry: /api/data-entry/*
|
||||
├── Telegram: /api/telegram/*
|
||||
└── Bot: Running ca background task
|
||||
```
|
||||
|
||||
## Scripturi Disponibile
|
||||
|
||||
| Script | Descriere |
|
||||
|--------|-----------|
|
||||
| `./start-prod.sh` | Pornește tot pentru PROD (Oracle PROD: 10.0.20.36) |
|
||||
| `./start-test.sh` | Pornește tot pentru TEST (Oracle TEST: 10.0.20.121) |
|
||||
| `./status.sh` | Verifică status-ul serviciilor |
|
||||
| `./start-backend.sh start/stop/restart` | Control granular backend |
|
||||
| `./start-frontend.sh restart` | Restart rapid frontend (~7s) |
|
||||
| `./test-unified-backend.sh` | Rulează testele comprehensive |
|
||||
|
||||
## API Endpoints
|
||||
|
||||
**Backend Unificat**: http://localhost:8000
|
||||
|
||||
- **API Docs**: http://localhost:8000/docs
|
||||
- **Health Check**: http://localhost:8000/health
|
||||
- **Reports**: http://localhost:8000/api/reports/*
|
||||
- **Data Entry**: http://localhost:8000/api/data-entry/*
|
||||
- **Telegram**: http://localhost:8000/api/telegram/*
|
||||
|
||||
**Frontend**: http://localhost:3000
|
||||
|
||||
## Log Files
|
||||
|
||||
```bash
|
||||
# Backend logs
|
||||
tail -f /tmp/unified_backend_dev.log # DEV
|
||||
tail -f /tmp/unified_backend_test.log # TEST
|
||||
|
||||
# Frontend logs
|
||||
tail -f /tmp/unified_frontend_dev.log # DEV
|
||||
tail -f /tmp/unified_frontend_test.log # TEST
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Backend nu pornește
|
||||
```bash
|
||||
# Verifică logurile
|
||||
tail -n 50 /tmp/unified_backend_dev.log
|
||||
|
||||
# Verifică dacă portul este ocupat
|
||||
lsof -i :8000
|
||||
|
||||
# Oprește procesul vechi
|
||||
./start-prod.sh stop
|
||||
```
|
||||
|
||||
### Frontend nu pornește
|
||||
```bash
|
||||
# Verifică logurile
|
||||
tail -n 50 /tmp/unified_frontend_dev.log
|
||||
|
||||
# Reinstalează dependențele
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install
|
||||
```
|
||||
|
||||
### SSH Tunnel nu se conectează
|
||||
```bash
|
||||
# DEV (PRODUCȚIE)
|
||||
./ssh-tunnel-prod.sh stop
|
||||
./ssh-tunnel-prod.sh start
|
||||
|
||||
# TEST
|
||||
./ssh-tunnel-test.sh stop
|
||||
./ssh-tunnel-test.sh start
|
||||
```
|
||||
|
||||
## Configurare Inițială
|
||||
|
||||
1. **Backend**: Creează `backend/.env` din `backend/.env.example`
|
||||
2. **Configurează variabilele** pentru mediul dorit (DEV/TEST)
|
||||
3. **Pornește serviciile**: `./start-prod.sh` sau `./start-test.sh`
|
||||
|
||||
## Diferențe DEV vs TEST
|
||||
|
||||
| Aspect | DEV | TEST |
|
||||
|--------|-----|------|
|
||||
| SSH Tunnel | `./ssh-tunnel-prod.sh` | `./ssh-tunnel-test.sh` |
|
||||
| Server Oracle | 10.0.20.36 (PROD) | 10.0.20.121 (TEST) |
|
||||
| Schema Test | ROMFAST (id=114) | MARIUSM_AUTO (id=110) |
|
||||
| .env File | `backend/.env` | `backend/.env.test` → `backend/.env` |
|
||||
| Hot Reload | DA (--reload) | NU (pentru stabilitate) |
|
||||
|
||||
## Avantaje
|
||||
|
||||
✅ **1 comandă** în loc de 3 pentru pornire
|
||||
✅ **1 proces** în loc de 3 de monitorizat
|
||||
✅ **Loguri unificate** într-un singur fișier
|
||||
✅ **Debugging mai ușor** - tot în același proces
|
||||
✅ **Resurse optimizate** - Oracle pool partajat
|
||||
✅ **Pornire mai rapidă** - inițializare paralelă
|
||||
|
||||
## Documentație Completă
|
||||
|
||||
- **Implementare**: `ULTRATHIN-MONOLITH-IMPLEMENTATION.md`
|
||||
- **Teste**: `./test-unified-backend.sh`
|
||||
- **Plan Detaliat**: `.auto-build/specs/ultrathin-monolith/plan.md`
|
||||
110
README.md
110
README.md
@@ -1,20 +1,20 @@
|
||||
# ROA2WEB - Modern ERP Reports Application
|
||||
# ROA2WEB - Modern ERP Application
|
||||
|
||||
**FastAPI Backend + Vue.js 3 Frontend + Telegram Bot**
|
||||
|
||||
Modern microservices-based ERP reporting application for managing invoices, payments, and financial data with Oracle database integration.
|
||||
Modern ultrathin monolith ERP application for managing reports, data entry, and financial data with Oracle database integration.
|
||||
|
||||
---
|
||||
|
||||
## Project Overview
|
||||
|
||||
ROA2WEB is a comprehensive financial reporting platform built with modern technologies:
|
||||
ROA2WEB is a comprehensive financial platform built with modern technologies:
|
||||
|
||||
- **Backend**: FastAPI (Python) - High-performance async API
|
||||
- **Frontend**: Vue.js 3 + PrimeVue - Rich, responsive web interface
|
||||
- **Telegram Bot**: Alternative command-based interface
|
||||
- **Database**: Oracle Database with connection pooling
|
||||
- **Architecture**: Microservices with shared components
|
||||
- **Backend**: FastAPI (Python) - Unified async API with modular architecture
|
||||
- **Frontend**: Vue.js 3 + PrimeVue - Single-page application with lazy-loaded modules
|
||||
- **Telegram Bot**: Alternative command-based interface (integrated module)
|
||||
- **Database**: Oracle Database + SQLite (hybrid approach)
|
||||
- **Architecture**: Ultrathin monolith with clear module boundaries
|
||||
|
||||
---
|
||||
|
||||
@@ -35,16 +35,16 @@ git clone <repository-url>
|
||||
cd roa2web
|
||||
|
||||
# Start all services with one command
|
||||
./start-dev.sh
|
||||
./start-prod.sh
|
||||
```
|
||||
|
||||
This starts SSH tunnel, backend (port 8001), and frontend (port 3000-3005).
|
||||
This starts SSH tunnel, unified backend (port 8001), and frontend (port 3000).
|
||||
|
||||
**For individual service setup or troubleshooting**: See "Development & Testing" section below or component-specific READMEs.
|
||||
**For individual service setup or troubleshooting**: See "Development & Testing" section below.
|
||||
|
||||
### Access the Application
|
||||
|
||||
- **Frontend**: http://localhost:3000 (or 3001-3005 if 3000 is busy)
|
||||
- **Frontend**: http://localhost:3000
|
||||
- **Backend API Docs**: http://localhost:8001/docs (Swagger UI)
|
||||
- **Backend ReDoc**: http://localhost:8001/redoc
|
||||
- **Health Check**: http://localhost:8001/health
|
||||
@@ -53,29 +53,39 @@ This starts SSH tunnel, backend (port 8001), and frontend (port 3000-3005).
|
||||
|
||||
## Architecture
|
||||
|
||||
### Directory Structure
|
||||
### Ultrathin Monolith Structure
|
||||
|
||||
```
|
||||
|
||||
├── shared/ # Shared components
|
||||
│ ├── database/ # Oracle connection pool (singleton)
|
||||
│ ├── auth/ # JWT authentication & middleware
|
||||
│ └── utils/ # Common utilities
|
||||
.
|
||||
├── backend/ # Unified FastAPI backend (port 8001)
|
||||
│ ├── modules/ # Business logic modules
|
||||
│ │ ├── reports/ # Reports module (Oracle read-only)
|
||||
│ │ ├── data_entry/ # Data entry module (SQLite + workflow)
|
||||
│ │ └── telegram/ # Telegram bot module
|
||||
│ ├── config.py # Centralized configuration
|
||||
│ └── main.py # FastAPI app entry point
|
||||
│
|
||||
├── reports-app/ # Main reports application
|
||||
│ ├── backend/ # FastAPI backend (port 8001)
|
||||
│ ├── frontend/ # Vue.js 3 frontend (port 3000-3005)
|
||||
│ └── telegram-bot/ # Telegram bot (port 8002)
|
||||
├── src/ # Unified Vue.js 3 frontend
|
||||
│ ├── modules/ # Feature modules
|
||||
│ │ ├── reports/ # Reports frontend
|
||||
│ │ └── data-entry/ # Data entry frontend
|
||||
│ ├── shared/ # Shared frontend components
|
||||
│ ├── assets/ # Global CSS, images
|
||||
│ └── router/ # Vue Router
|
||||
│
|
||||
├── nginx/ # Nginx reverse proxy config
|
||||
├── ssh-tunnel/ # SSH tunnel for Oracle DB
|
||||
├── deployment/ # Deployment scripts (Linux & Windows)
|
||||
└── scripts/ # Utility scripts
|
||||
├── shared/ # Shared backend components
|
||||
│ ├── database/ # Oracle connection pool
|
||||
│ ├── auth/ # JWT authentication
|
||||
│ └── frontend/ # Shared frontend assets
|
||||
│
|
||||
├── docs/ # Documentation
|
||||
├── deployment/ # Deployment scripts
|
||||
└── ssh-tunnel/ # SSH tunnel for Oracle DB
|
||||
```
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Shared Database Pool**: Singleton Oracle connection pool shared across microservices
|
||||
- **Shared Database Pool**: Singleton Oracle connection pool shared across all modules
|
||||
- **Two-Tier Cache System**: Hybrid L1 (Memory) + L2 (SQLite) for optimal performance
|
||||
- **JWT Authentication**: Secure token-based auth with middleware
|
||||
- **Microservices**: Independent services with clear separation of concerns
|
||||
@@ -98,21 +108,21 @@ This starts SSH tunnel, backend (port 8001), and frontend (port 3000-3005).
|
||||
|
||||
## Development & Testing
|
||||
|
||||
**Quick Start**: Use `./start-dev.sh` to start all services (SSH tunnel + Backend + Frontend).
|
||||
**Quick Start**: Use `./start-prod.sh` to start all services (SSH tunnel + Backend + Frontend).
|
||||
|
||||
**For detailed development commands, testing procedures, and troubleshooting**: See `CLAUDE.md` and component-specific READMEs:
|
||||
- Backend: `reports-app/backend/README.md`
|
||||
- Frontend: `reports-app/frontend/README.md` & `reports-app/frontend/tests/README.md`
|
||||
- Backend: `backend/ modules and CLAUDE.md`
|
||||
- Frontend: `src/ and docs/MONOLITH_ARCHITECTURE.md` & `E2E testing guide in docs/`
|
||||
- Telegram Bot: `reports-app/telegram-bot/README.md`
|
||||
|
||||
**Key Commands**:
|
||||
```bash
|
||||
# Start All Services (FAST with parallel backend startup - ~11s dev, ~33s test)
|
||||
./start-dev.sh # Start all (SSH tunnel + Backends + Bot + Frontend)
|
||||
./start-prod.sh # Start all (SSH tunnel + Backends + Bot + Frontend)
|
||||
./start-test.sh # Start all (TEST environment)
|
||||
|
||||
# Individual Service Control (NEW - for quick restarts!)
|
||||
./frontend.sh start|stop|restart|status # Frontend only (~7s restart!)
|
||||
./start-frontend.sh start|stop|restart|status # Frontend only (~7s restart!)
|
||||
./backend-reports.sh start|stop|status # Reports backend only
|
||||
./backend-data-entry.sh start|stop|status # Data Entry backend only
|
||||
./bot.sh start|stop|status # Telegram bot only
|
||||
@@ -121,53 +131,53 @@ This starts SSH tunnel, backend (port 8001), and frontend (port 3000-3005).
|
||||
./status.sh # Show all services status + health checks
|
||||
|
||||
# Infrastructure Only
|
||||
./ssh_tunnel.sh start|stop|status # Oracle DB tunnel (production)
|
||||
./ssh-tunnel-prod.sh start|stop|status # Oracle DB tunnel (production)
|
||||
./ssh-tunnel-test.sh start|stop|status # Oracle TEST tunnel
|
||||
```
|
||||
|
||||
**💡 Pro Tips**:
|
||||
- **Frontend changes?** Use `./frontend.sh restart` instead of restarting everything (87% faster!)
|
||||
- **Frontend changes?** Use `./start-frontend.sh restart` instead of restarting everything (87% faster!)
|
||||
- **Check what's running:** `./status.sh` shows everything at a glance
|
||||
- **Backend-uri pornesc în paralel** în start-dev.sh și start-test.sh pentru pornire mai rapidă
|
||||
- **Backend-uri pornesc în paralel** în start-prod.sh și start-test.sh pentru pornire mai rapidă
|
||||
|
||||
### 📖 Usage Flow
|
||||
|
||||
**Individual scripts (`frontend.sh`, `backend-*.sh`, `bot.sh`) are environment-neutral:**
|
||||
**Individual scripts (`start-frontend.sh`, `start-backend.sh`, `backend-*.sh`, `bot.sh`) are environment-neutral:**
|
||||
- They DON'T change `.env` files
|
||||
- They use whatever `.env` is already present
|
||||
- Use them for **quick restarts** when working on a specific service
|
||||
|
||||
**Master scripts (`start-dev.sh`, `start-test.sh`) set the environment:**
|
||||
- `start-dev.sh` → uses existing `.env` files (DEV mode)
|
||||
**Master scripts (`start-prod.sh`, `start-test.sh`) set the environment:**
|
||||
- `start-prod.sh` → uses existing `.env` files (DEV mode)
|
||||
- `start-test.sh` → copies `.env.test` → `.env` (TEST mode)
|
||||
|
||||
**Recommended workflow:**
|
||||
|
||||
```bash
|
||||
# Morning: Start full stack with environment selection
|
||||
./start-dev.sh # DEV mode - sets up .env files
|
||||
./start-prod.sh # DEV mode - sets up .env files
|
||||
|
||||
# During development: Quick service restarts
|
||||
./frontend.sh restart # Frontend only (~7s)
|
||||
./start-frontend.sh restart # Frontend only (~7s)
|
||||
./backend-reports.sh restart # Reports backend only (~30s)
|
||||
# ⚠️ Individual scripts inherit the environment set by start-dev.sh
|
||||
# ⚠️ Individual scripts inherit the environment set by start-prod.sh
|
||||
|
||||
# End of day: Stop everything
|
||||
./start-dev.sh stop
|
||||
./start-prod.sh stop
|
||||
```
|
||||
|
||||
**Common scenarios:**
|
||||
|
||||
```bash
|
||||
# Scenario 1: Working on frontend only
|
||||
./start-dev.sh # Start everything once
|
||||
./frontend.sh restart # Restart frontend multiple times (fast!)
|
||||
./start-prod.sh # Start everything once
|
||||
./start-frontend.sh restart # Restart frontend multiple times (fast!)
|
||||
|
||||
# Scenario 2: Debugging a single backend
|
||||
./start-dev.sh stop # Stop all
|
||||
./ssh_tunnel.sh start # Infrastructure only
|
||||
./start-prod.sh stop # Stop all
|
||||
./ssh-tunnel-prod.sh start # Infrastructure only
|
||||
./backend-reports.sh start # Just the backend you need
|
||||
./frontend.sh start # Just the frontend
|
||||
./start-frontend.sh start # Just the frontend
|
||||
|
||||
# Scenario 3: Testing mode
|
||||
./start-test.sh # Starts everything in TEST mode
|
||||
@@ -295,9 +305,9 @@ BACKEND_API_URL=http://localhost:8001
|
||||
|
||||
### Component-Specific
|
||||
- `README.md` - Main application README
|
||||
- `reports-app/backend/README.md` - Backend specifics
|
||||
- `reports-app/frontend/README.md` - Frontend guide
|
||||
- `reports-app/frontend/tests/README.md` - Frontend testing
|
||||
- `backend/ modules and CLAUDE.md` - Backend specifics
|
||||
- `src/ and docs/MONOLITH_ARCHITECTURE.md` - Frontend guide
|
||||
- `E2E testing guide in docs/` - Frontend testing
|
||||
- `reports-app/telegram-bot/README.md` - Telegram bot guide
|
||||
- `reports-app/telegram-bot/TELEGRAM_COMMANDS.md` - Bot commands
|
||||
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
# ROA2WEB Unified App - Integration Testing Checklist
|
||||
|
||||
## Pre-Test Setup
|
||||
|
||||
- [ ] Stop any previously running services: `./start-test.sh stop`
|
||||
- [ ] Verify SSH tunnel is configured: `./ssh-tunnel-test.sh status`
|
||||
- [ ] Start all services: `./start-test.sh`
|
||||
- [ ] Wait for all services to start (check logs if needed)
|
||||
|
||||
## Service Health Checks
|
||||
|
||||
### Backend Services
|
||||
- [ ] Reports Backend (8001): http://localhost:8001/health
|
||||
- [ ] Reports API Docs: http://localhost:8001/docs
|
||||
- [ ] Data Entry Backend (8003): http://localhost:8003/health
|
||||
- [ ] Data Entry API Docs: http://localhost:8003/docs
|
||||
- [ ] Telegram Bot Internal API (8002): Should be running (check logs: `/tmp/telegram_bot.log`)
|
||||
|
||||
### Frontend
|
||||
- [ ] Unified Frontend loads: http://localhost:3000
|
||||
- [ ] No console errors in browser DevTools
|
||||
- [ ] Login page displays correctly
|
||||
|
||||
## Authentication Flow
|
||||
|
||||
### Login
|
||||
- [ ] Navigate to http://localhost:3000
|
||||
- [ ] Should redirect to `/login` automatically
|
||||
- [ ] Enter valid test credentials
|
||||
- [ ] Login succeeds and redirects to dashboard
|
||||
- [ ] User info displays in header (username, company selector)
|
||||
- [ ] Access token stored in localStorage
|
||||
- [ ] JWT contains correct user info and companies
|
||||
|
||||
### Session Persistence
|
||||
- [ ] Refresh page - user remains logged in
|
||||
- [ ] Close tab and reopen - user remains logged in
|
||||
- [ ] Open in new tab - user is already logged in
|
||||
|
||||
## Reports Module (http://localhost:3000/reports)
|
||||
|
||||
### Navigation
|
||||
- [ ] Click "Rapoarte" in menu
|
||||
- [ ] Dashboard loads at `/reports/dashboard`
|
||||
- [ ] No console errors
|
||||
- [ ] Company selector works (change company)
|
||||
- [ ] Period selector works (change accounting period)
|
||||
|
||||
### Dashboard Widgets
|
||||
- [ ] Metrics cards display (Sales, Purchases, etc.)
|
||||
- [ ] Charts render correctly
|
||||
- [ ] Data loads from Reports API (8001)
|
||||
- [ ] Check Network tab: requests go to `/api/reports/*`
|
||||
|
||||
### Reports Pages
|
||||
- [ ] Navigate to "Facturi Clienți" (`/reports/invoices/sales`)
|
||||
- [ ] Table loads with data
|
||||
- [ ] Filters work (date range, company, status)
|
||||
- [ ] Pagination works
|
||||
- [ ] Export buttons work (Excel, PDF)
|
||||
- [ ] Invoice details modal opens
|
||||
- [ ] Navigate to "Facturi Furnizori" (`/reports/invoices/purchases`)
|
||||
- [ ] Verify same functionality as sales invoices
|
||||
|
||||
### Treasury Reports
|
||||
- [ ] Navigate to "Trezorerie" (`/reports/treasury`)
|
||||
- [ ] Cash flow data loads
|
||||
- [ ] Charts display correctly
|
||||
- [ ] Date filters work
|
||||
|
||||
### Error Boundary Testing
|
||||
- [ ] Manually trigger an error in Reports module (e.g., bad API call)
|
||||
- [ ] ErrorBoundary catches the error
|
||||
- [ ] Error message displays: "A apărut o eroare în modulul Rapoarte"
|
||||
- [ ] Other modules (Data Entry) remain functional
|
||||
|
||||
## Data Entry Module (http://localhost:3000/data-entry)
|
||||
|
||||
### Navigation
|
||||
- [ ] Click "Introduceri" in menu
|
||||
- [ ] Receipts list loads at `/data-entry/receipts`
|
||||
- [ ] No console errors
|
||||
- [ ] Check Network tab: requests go to `/api/data-entry/*`
|
||||
|
||||
### Receipts List
|
||||
- [ ] Table displays receipts
|
||||
- [ ] Status badges display correctly (DRAFT, PENDING, APPROVED)
|
||||
- [ ] Filters work (date range, status, user)
|
||||
- [ ] Create new receipt button visible
|
||||
|
||||
### Create Receipt
|
||||
- [ ] Click "Adaugă Bon Fiscal"
|
||||
- [ ] Form displays at `/data-entry/receipts/new`
|
||||
- [ ] Partner/Supplier dropdown loads from Data Entry API
|
||||
- [ ] Expense type dropdown works
|
||||
- [ ] Date picker works
|
||||
- [ ] Amount fields accept input
|
||||
- [ ] File upload works (image/PDF)
|
||||
- [ ] Save as DRAFT works
|
||||
- [ ] Receipt appears in list with DRAFT status
|
||||
|
||||
### Edit Receipt
|
||||
- [ ] Click edit on a DRAFT receipt
|
||||
- [ ] Form loads with existing data
|
||||
- [ ] Modify fields
|
||||
- [ ] Save changes - updates successfully
|
||||
- [ ] Delete receipt - removes from list
|
||||
|
||||
### Submit for Review
|
||||
- [ ] Open a DRAFT receipt
|
||||
- [ ] Click "Trimite spre aprobare"
|
||||
- [ ] Status changes to PENDING_REVIEW
|
||||
- [ ] Accounting entries auto-generated
|
||||
- [ ] Receipt is read-only in PENDING state
|
||||
|
||||
### Approval Workflow (Accountant Role)
|
||||
- [ ] Login as accountant user
|
||||
- [ ] See pending receipts
|
||||
- [ ] Open PENDING receipt
|
||||
- [ ] Review accounting entries
|
||||
- [ ] Approve receipt - status changes to APPROVED
|
||||
- [ ] Receipt becomes fully read-only
|
||||
|
||||
### Error Boundary Testing
|
||||
- [ ] Manually trigger an error in Data Entry module
|
||||
- [ ] ErrorBoundary catches the error
|
||||
- [ ] Error message displays: "A apărut o eroare în modulul Introduceri"
|
||||
- [ ] Other modules (Reports) remain functional
|
||||
|
||||
## Cross-Module Testing
|
||||
|
||||
### Module Switching
|
||||
- [ ] Start in Reports module
|
||||
- [ ] Navigate to Data Entry module
|
||||
- [ ] Return to Reports module
|
||||
- [ ] Company selection persists across modules
|
||||
- [ ] No console errors during switching
|
||||
- [ ] No memory leaks (check DevTools Memory tab)
|
||||
|
||||
### Shared State
|
||||
- [ ] Login state shared (logout in one module logs out everywhere)
|
||||
- [ ] Company selection shared (change company affects both modules)
|
||||
- [ ] Period selection shared (for modules that use it)
|
||||
|
||||
### API Isolation
|
||||
- [ ] Reports module only calls `/api/reports/*`
|
||||
- [ ] Data Entry module only calls `/api/data-entry/*`
|
||||
- [ ] No cross-contamination of API calls
|
||||
- [ ] Auth headers included in all requests
|
||||
|
||||
## Logout Flow
|
||||
|
||||
- [ ] Click logout button in header
|
||||
- [ ] User redirected to `/login`
|
||||
- [ ] Access token removed from localStorage
|
||||
- [ ] Cannot access protected routes without re-login
|
||||
- [ ] Attempting to access `/reports` or `/data-entry` redirects to login
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
- [ ] Chrome/Edge (latest)
|
||||
- [ ] Firefox (latest)
|
||||
- [ ] Safari (if available)
|
||||
|
||||
## Responsive Design
|
||||
|
||||
- [ ] Desktop (1920x1080)
|
||||
- [ ] Laptop (1366x768)
|
||||
- [ ] Tablet (768x1024)
|
||||
- [ ] Mobile (375x667)
|
||||
|
||||
## Performance
|
||||
|
||||
- [ ] Initial page load < 3s
|
||||
- [ ] Navigation between modules smooth
|
||||
- [ ] No unnecessary re-renders (check React DevTools)
|
||||
- [ ] API responses cached appropriately
|
||||
- [ ] Bundle sizes reasonable (check Network tab)
|
||||
|
||||
## Build & Production
|
||||
|
||||
- [ ] Run `npm run build`
|
||||
- [ ] Build completes without errors
|
||||
- [ ] Dist folder created with proper structure
|
||||
- [ ] index.html exists
|
||||
- [ ] Assets folder has JS/CSS bundles
|
||||
- [ ] Chunk splitting works (separate bundles for modules)
|
||||
- [ ] Source maps generated (for debugging)
|
||||
|
||||
## Cleanup
|
||||
|
||||
- [ ] Stop all services: `./start-test.sh stop`
|
||||
- [ ] Verify all ports released (8001, 8003, 8002, 3000)
|
||||
- [ ] SSH tunnel stopped
|
||||
- [ ] No lingering processes
|
||||
|
||||
## Issues Found
|
||||
|
||||
Document any issues discovered during testing:
|
||||
|
||||
| Issue | Module | Severity | Description | Status |
|
||||
|-------|--------|----------|-------------|--------|
|
||||
| | | | | |
|
||||
|
||||
## Sign-Off
|
||||
|
||||
- **Tester**: _______________
|
||||
- **Date**: _______________
|
||||
- **Environment**: TEST / DEV / PROD
|
||||
- **Status**: PASS / FAIL / NEEDS FIXES
|
||||
|
||||
## Notes
|
||||
|
||||
Additional observations or comments:
|
||||
@@ -1,168 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Backend Data Entry Service Control Script for ROA2WEB Unified App
|
||||
# Manages the FastAPI Data Entry backend on port 8003
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$SCRIPT_DIR"
|
||||
|
||||
# Source helper functions
|
||||
source "$SCRIPT_DIR/scripts/service-helpers.sh"
|
||||
|
||||
# Service configuration
|
||||
SERVICE_NAME="Data Entry Backend"
|
||||
PORT=8003
|
||||
LOG_FILE="/tmp/data-entry-backend.log"
|
||||
BACKEND_DIR="$ROOT_DIR/data-entry-app/backend"
|
||||
VENV_DIR="$BACKEND_DIR/venv"
|
||||
ENV_FILE="$BACKEND_DIR/.env"
|
||||
|
||||
# Function to start backend
|
||||
start_backend() {
|
||||
print_header "Starting $SERVICE_NAME"
|
||||
|
||||
# Check if port is already in use
|
||||
if ! check_port_available $PORT "$SERVICE_NAME"; then
|
||||
print_warning "$SERVICE_NAME may already be running"
|
||||
print_info "Use './backend-data-entry.sh status' to check or './backend-data-entry.sh stop' to stop it"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check backend directory
|
||||
if [ ! -d "$BACKEND_DIR" ]; then
|
||||
print_error "Backend directory not found: $BACKEND_DIR"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check virtual environment
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
print_error "Virtual environment not found: $VENV_DIR"
|
||||
print_info "Please run setup first: cd data-entry-app/backend && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check .env file
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
print_warning ".env file not found: $ENV_FILE"
|
||||
print_info "Database connection may fail"
|
||||
fi
|
||||
|
||||
# Start backend
|
||||
print_info "Starting FastAPI server..."
|
||||
cd "$BACKEND_DIR"
|
||||
|
||||
# Activate venv and start uvicorn in background
|
||||
(
|
||||
source venv/bin/activate
|
||||
nohup uvicorn app.main:app --host 0.0.0.0 --port $PORT > "$LOG_FILE" 2>&1 &
|
||||
echo $! > /tmp/data-entry-backend.pid
|
||||
)
|
||||
|
||||
local pid=$(cat /tmp/data-entry-backend.pid 2>/dev/null)
|
||||
print_info "Started with PID: $pid"
|
||||
print_info "Log file: $LOG_FILE"
|
||||
|
||||
# Wait for port to be ready (Data Entry takes longer due to SQLite migrations)
|
||||
if wait_for_port $PORT "$SERVICE_NAME" 35; then
|
||||
# Check health endpoint
|
||||
print_info "Checking health endpoint..."
|
||||
sleep 2
|
||||
local health_status=$(curl -s http://localhost:$PORT/health 2>&1 | head -1)
|
||||
|
||||
if echo "$health_status" | grep -q "ok"; then
|
||||
print_success "$SERVICE_NAME started successfully and is healthy!"
|
||||
else
|
||||
print_success "$SERVICE_NAME started (health check inconclusive)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_info "🌐 API URLs:"
|
||||
echo " • API Docs: http://localhost:$PORT/docs"
|
||||
echo " • Health: http://localhost:$PORT/health"
|
||||
echo ""
|
||||
print_info "📄 View logs: tail -f $LOG_FILE"
|
||||
return 0
|
||||
else
|
||||
print_error "Failed to start $SERVICE_NAME (timeout waiting for port $PORT)"
|
||||
print_info "Check logs: tail -20 $LOG_FILE"
|
||||
rm -f /tmp/data-entry-backend.pid
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to stop backend
|
||||
stop_backend() {
|
||||
print_header "Stopping $SERVICE_NAME"
|
||||
|
||||
kill_port $PORT "$SERVICE_NAME"
|
||||
|
||||
# Clean up PID file
|
||||
if [ -f "/tmp/data-entry-backend.pid" ]; then
|
||||
rm /tmp/data-entry-backend.pid
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to show backend status
|
||||
status_backend() {
|
||||
print_header "$SERVICE_NAME Status"
|
||||
|
||||
if check_service_status $PORT "$SERVICE_NAME"; then
|
||||
echo ""
|
||||
|
||||
# Check health endpoint
|
||||
print_info "Health check:"
|
||||
local health=$(curl -s http://localhost:$PORT/health 2>&1)
|
||||
if echo "$health" | grep -q "ok"; then
|
||||
print_success "Health endpoint: OK"
|
||||
else
|
||||
print_warning "Health endpoint: Not responding"
|
||||
fi
|
||||
|
||||
# Show recent logs
|
||||
if [ -f "$LOG_FILE" ]; then
|
||||
echo ""
|
||||
tail_logs "$LOG_FILE" 10
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
echo ""
|
||||
print_warning "Service is not running"
|
||||
|
||||
# Show last logs if available
|
||||
if [ -f "$LOG_FILE" ]; then
|
||||
echo ""
|
||||
print_info "Last logs before shutdown:"
|
||||
tail_logs "$LOG_FILE" 10
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main script logic
|
||||
case "${1:-}" in
|
||||
start)
|
||||
start_backend
|
||||
;;
|
||||
stop)
|
||||
stop_backend
|
||||
;;
|
||||
status)
|
||||
status_backend
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|status}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " start - Start the Data Entry backend API server"
|
||||
echo " stop - Stop the Data Entry backend API server"
|
||||
echo " status - Show backend status and health check"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " ./backend-data-entry.sh start # Start backend"
|
||||
echo " ./backend-data-entry.sh status # Check if running"
|
||||
echo " ./backend-data-entry.sh stop # Stop backend"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,168 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Backend Reports Service Control Script for ROA2WEB Unified App
|
||||
# Manages the FastAPI Reports backend on port 8001
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$SCRIPT_DIR"
|
||||
|
||||
# Source helper functions
|
||||
source "$SCRIPT_DIR/scripts/service-helpers.sh"
|
||||
|
||||
# Service configuration
|
||||
SERVICE_NAME="Reports Backend"
|
||||
PORT=8001
|
||||
LOG_FILE="/tmp/reports-backend.log"
|
||||
BACKEND_DIR="$ROOT_DIR/reports-app/backend"
|
||||
VENV_DIR="$BACKEND_DIR/venv"
|
||||
ENV_FILE="$BACKEND_DIR/.env"
|
||||
|
||||
# Function to start backend
|
||||
start_backend() {
|
||||
print_header "Starting $SERVICE_NAME"
|
||||
|
||||
# Check if port is already in use
|
||||
if ! check_port_available $PORT "$SERVICE_NAME"; then
|
||||
print_warning "$SERVICE_NAME may already be running"
|
||||
print_info "Use './backend-reports.sh status' to check or './backend-reports.sh stop' to stop it"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check backend directory
|
||||
if [ ! -d "$BACKEND_DIR" ]; then
|
||||
print_error "Backend directory not found: $BACKEND_DIR"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check virtual environment
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
print_error "Virtual environment not found: $VENV_DIR"
|
||||
print_info "Please run setup first: cd reports-app/backend && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check .env file
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
print_warning ".env file not found: $ENV_FILE"
|
||||
print_info "Database connection may fail"
|
||||
fi
|
||||
|
||||
# Start backend
|
||||
print_info "Starting FastAPI server..."
|
||||
cd "$BACKEND_DIR"
|
||||
|
||||
# Activate venv and start uvicorn in background
|
||||
(
|
||||
source venv/bin/activate
|
||||
nohup uvicorn app.main:app --host 0.0.0.0 --port $PORT > "$LOG_FILE" 2>&1 &
|
||||
echo $! > /tmp/reports-backend.pid
|
||||
)
|
||||
|
||||
local pid=$(cat /tmp/reports-backend.pid 2>/dev/null)
|
||||
print_info "Started with PID: $pid"
|
||||
print_info "Log file: $LOG_FILE"
|
||||
|
||||
# Wait for port to be ready
|
||||
if wait_for_port $PORT "$SERVICE_NAME" 30; then
|
||||
# Check health endpoint
|
||||
print_info "Checking health endpoint..."
|
||||
sleep 2
|
||||
local health_status=$(curl -s http://localhost:$PORT/health 2>&1 | head -1)
|
||||
|
||||
if echo "$health_status" | grep -q "ok"; then
|
||||
print_success "$SERVICE_NAME started successfully and is healthy!"
|
||||
else
|
||||
print_success "$SERVICE_NAME started (health check inconclusive)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_info "🌐 API URLs:"
|
||||
echo " • API Docs: http://localhost:$PORT/docs"
|
||||
echo " • Health: http://localhost:$PORT/health"
|
||||
echo ""
|
||||
print_info "📄 View logs: tail -f $LOG_FILE"
|
||||
return 0
|
||||
else
|
||||
print_error "Failed to start $SERVICE_NAME (timeout waiting for port $PORT)"
|
||||
print_info "Check logs: tail -20 $LOG_FILE"
|
||||
rm -f /tmp/reports-backend.pid
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to stop backend
|
||||
stop_backend() {
|
||||
print_header "Stopping $SERVICE_NAME"
|
||||
|
||||
kill_port $PORT "$SERVICE_NAME"
|
||||
|
||||
# Clean up PID file
|
||||
if [ -f "/tmp/reports-backend.pid" ]; then
|
||||
rm /tmp/reports-backend.pid
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to show backend status
|
||||
status_backend() {
|
||||
print_header "$SERVICE_NAME Status"
|
||||
|
||||
if check_service_status $PORT "$SERVICE_NAME"; then
|
||||
echo ""
|
||||
|
||||
# Check health endpoint
|
||||
print_info "Health check:"
|
||||
local health=$(curl -s http://localhost:$PORT/health 2>&1)
|
||||
if echo "$health" | grep -q "ok"; then
|
||||
print_success "Health endpoint: OK"
|
||||
else
|
||||
print_warning "Health endpoint: Not responding"
|
||||
fi
|
||||
|
||||
# Show recent logs
|
||||
if [ -f "$LOG_FILE" ]; then
|
||||
echo ""
|
||||
tail_logs "$LOG_FILE" 10
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
echo ""
|
||||
print_warning "Service is not running"
|
||||
|
||||
# Show last logs if available
|
||||
if [ -f "$LOG_FILE" ]; then
|
||||
echo ""
|
||||
print_info "Last logs before shutdown:"
|
||||
tail_logs "$LOG_FILE" 10
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main script logic
|
||||
case "${1:-}" in
|
||||
start)
|
||||
start_backend
|
||||
;;
|
||||
stop)
|
||||
stop_backend
|
||||
;;
|
||||
status)
|
||||
status_backend
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|status}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " start - Start the Reports backend API server"
|
||||
echo " stop - Stop the Reports backend API server"
|
||||
echo " status - Show backend status and health check"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " ./backend-reports.sh start # Start backend"
|
||||
echo " ./backend-reports.sh status # Check if running"
|
||||
echo " ./backend-reports.sh stop # Stop backend"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
147
backend/.env.dev.example
Normal file
147
backend/.env.dev.example
Normal file
@@ -0,0 +1,147 @@
|
||||
# ============================================================================
|
||||
# ROA2WEB Unified Backend - Environment Configuration (Development)
|
||||
# ============================================================================
|
||||
# Single backend process serving Reports, Data Entry, and Telegram modules
|
||||
# IMPORTANT: Never commit this file to git!
|
||||
|
||||
# ============================================================================
|
||||
# ORACLE DATABASE CONFIGURATION (REQUIRED - Shared by all modules)
|
||||
# ============================================================================
|
||||
# Connection to CONTAFIN_ORACLE schema for authentication and user management
|
||||
# Each company is a separate schema in Oracle Database
|
||||
# Development: Through SSH tunnel (localhost:1526)
|
||||
|
||||
ORACLE_USER=CONTAFIN_ORACLE
|
||||
ORACLE_PASSWORD=your_oracle_password_here
|
||||
ORACLE_HOST=localhost
|
||||
ORACLE_PORT=1526
|
||||
ORACLE_SID=ROA
|
||||
|
||||
# Development: Start SSH tunnel before running backend
|
||||
# ./ssh_tunnel.sh start (production) or ./ssh-tunnel-test.sh start (test)
|
||||
|
||||
# ============================================================================
|
||||
# JWT AUTHENTICATION (REQUIRED - Shared by all modules)
|
||||
# ============================================================================
|
||||
# Used for JWT token generation and validation (shared/auth/jwt_handler.py)
|
||||
|
||||
JWT_SECRET_KEY=generate_with_secrets_token_urlsafe_32
|
||||
JWT_ALGORITHM=HS256
|
||||
|
||||
# Token expiration settings (used by shared/auth/jwt_handler.py)
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||
|
||||
# ============================================================================
|
||||
# SESSION SECURITY - EMAIL 2FA (REQUIRED for Telegram email login)
|
||||
# ============================================================================
|
||||
# Used by Telegram module for session token validation
|
||||
# Must match between backend and Telegram bot
|
||||
|
||||
AUTH_SESSION_SECRET=generate_with_secrets_token_urlsafe_32
|
||||
|
||||
# ============================================================================
|
||||
# SERVER CONFIGURATION
|
||||
# ============================================================================
|
||||
# Unified backend server settings
|
||||
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=8000
|
||||
DEBUG=true
|
||||
|
||||
# CORS Origins (comma-separated, includes both old and new frontend ports)
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:3010,http://localhost:5173
|
||||
|
||||
# ============================================================================
|
||||
# REPORTS MODULE - CACHE CONFIGURATION (OPTIONAL - defaults provided)
|
||||
# ============================================================================
|
||||
# Two-tier hybrid cache system (L1: in-memory LRU, L2: SQLite persistent)
|
||||
# Used by backend/modules/reports/cache/config.py
|
||||
|
||||
# Core Settings
|
||||
CACHE_ENABLED=True
|
||||
CACHE_TYPE=hybrid
|
||||
CACHE_SQLITE_PATH=./data/cache/roa2web_cache.db
|
||||
CACHE_MEMORY_MAX_SIZE=1000
|
||||
CACHE_DEFAULT_TTL=900
|
||||
|
||||
# TTL per Cache Type (seconds)
|
||||
CACHE_TTL_SCHEMA=86400
|
||||
CACHE_TTL_COMPANIES=1800
|
||||
CACHE_TTL_DASHBOARD_SUMMARY=1800
|
||||
CACHE_TTL_DASHBOARD_TRENDS=1800
|
||||
CACHE_TTL_INVOICES=600
|
||||
CACHE_TTL_INVOICES_SUMMARY=900
|
||||
CACHE_TTL_TREASURY=600
|
||||
|
||||
# Maintenance
|
||||
CACHE_CLEANUP_INTERVAL=3600
|
||||
|
||||
# Event-Based Invalidation (experimental)
|
||||
CACHE_AUTO_INVALIDATE=False
|
||||
CACHE_CHECK_INTERVAL=300
|
||||
|
||||
# Performance Tracking
|
||||
CACHE_TRACK_PERFORMANCE=True
|
||||
CACHE_BENCHMARK_ON_STARTUP=False
|
||||
|
||||
# ============================================================================
|
||||
# DATA ENTRY MODULE - CONFIGURATION
|
||||
# ============================================================================
|
||||
# Data Entry module settings (receipts, OCR, etc.)
|
||||
|
||||
# Environment identifier (dev/test/prod)
|
||||
ORACLE_ENV=dev
|
||||
|
||||
# SQLite Database (development)
|
||||
SQLITE_DATABASE_PATH=data/receipts/receipts_dev.db
|
||||
|
||||
# File uploads
|
||||
UPLOAD_PATH=data/receipts/uploads
|
||||
MAX_UPLOAD_SIZE_MB=10
|
||||
|
||||
# Test company (for development testing)
|
||||
TEST_COMPANY_ID=110
|
||||
TEST_COMPANY_SCHEMA=MARIUSM_AUTO
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - BOT CONFIGURATION (REQUIRED for Telegram features)
|
||||
# ============================================================================
|
||||
# Obtain bot token from @BotFather on Telegram
|
||||
|
||||
TELEGRAM_BOT_TOKEN=your_bot_token_from_botfather
|
||||
|
||||
# Backend URL for bot to communicate with API
|
||||
BACKEND_URL=http://localhost:8000
|
||||
|
||||
# Internal API port (bot's internal API for backend callbacks)
|
||||
INTERNAL_API_PORT=8002
|
||||
|
||||
# Enable internal API documentation (development only)
|
||||
ENABLE_DOCS=false
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - EMAIL AUTHENTICATION (SMTP) (REQUIRED for email 2FA)
|
||||
# ============================================================================
|
||||
# Required for email-based 2FA authentication flow
|
||||
# Users can login with email + password instead of web app linking
|
||||
|
||||
# SMTP Server Configuration
|
||||
SMTP_HOST=mail.romfast.ro
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=ups@romfast.ro
|
||||
SMTP_PASSWORD=your_smtp_password_here
|
||||
SMTP_FROM_EMAIL=ups@romfast.ro
|
||||
SMTP_FROM_NAME=ROA2WEB
|
||||
SMTP_USE_TLS=true
|
||||
|
||||
# Email Retry Settings
|
||||
EMAIL_MAX_RETRIES=3
|
||||
EMAIL_RETRY_DELAY=2.0
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - DATABASE (SQLite for bot data)
|
||||
# ============================================================================
|
||||
# Separate SQLite database for Telegram bot auth codes and sessions
|
||||
|
||||
TELEGRAM_SQLITE_DATABASE_PATH=data/telegram/telegram.db
|
||||
146
backend/.env.example
Normal file
146
backend/.env.example
Normal file
@@ -0,0 +1,146 @@
|
||||
# ============================================================================
|
||||
# ROA2WEB Unified Backend - Environment Configuration Template
|
||||
# ============================================================================
|
||||
# Single backend process serving Reports, Data Entry, and Telegram modules
|
||||
#
|
||||
# SETUP INSTRUCTIONS:
|
||||
# 1. Copy this template: cp .env.example .env.dev
|
||||
# 2. Fill in your actual values in .env.dev
|
||||
# 3. Run: ./start-dev.sh (auto-copies .env.dev to .env)
|
||||
#
|
||||
# ENVIRONMENT FILES:
|
||||
# - .env.dev → Development config (committed to git with real values)
|
||||
# - .env.test → Test config (committed to git)
|
||||
# - .env.prod → Production config template (committed, use placeholders!)
|
||||
# - .env → Active config (auto-generated, NOT committed)
|
||||
#
|
||||
# IMPORTANT: Never manually edit .env - edit .env.dev instead!
|
||||
|
||||
# ============================================================================
|
||||
# ORACLE DATABASE CONFIGURATION (REQUIRED - Shared by all modules)
|
||||
# ============================================================================
|
||||
# Connection to CONTAFIN_ORACLE schema for authentication and user management
|
||||
# Each company is a separate schema in Oracle Database
|
||||
# Development: Through SSH tunnel (localhost:1526)
|
||||
# Windows Production: Direct connection to Oracle server
|
||||
|
||||
ORACLE_USER=CONTAFIN_ORACLE
|
||||
ORACLE_PASSWORD=SET_IN_PRODUCTION_ENV
|
||||
ORACLE_HOST=localhost
|
||||
ORACLE_PORT=1526
|
||||
ORACLE_SID=ROA
|
||||
|
||||
# Development Only: Start SSH tunnel before running backend
|
||||
# ./ssh_tunnel.sh start
|
||||
# ./ssh_tunnel.sh status
|
||||
|
||||
# ============================================================================
|
||||
# JWT AUTHENTICATION (REQUIRED - Shared by all modules)
|
||||
# ============================================================================
|
||||
# Used for JWT token generation and validation (shared/auth/jwt_handler.py)
|
||||
# Generate strong secret: python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
|
||||
JWT_SECRET_KEY=GENERATE_STRONG_SECRET_IN_PRODUCTION
|
||||
JWT_ALGORITHM=HS256
|
||||
|
||||
# Token expiration settings (used by shared/auth/jwt_handler.py)
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||
|
||||
# ============================================================================
|
||||
# SESSION SECURITY - EMAIL 2FA (REQUIRED for Telegram email login)
|
||||
# ============================================================================
|
||||
# Used by Telegram module for session token validation
|
||||
# Generate with: python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
|
||||
AUTH_SESSION_SECRET=your-secure-random-secret-here-min-32-chars
|
||||
|
||||
# ============================================================================
|
||||
# SERVER CONFIGURATION
|
||||
# ============================================================================
|
||||
# Unified backend server settings
|
||||
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=8000
|
||||
DEBUG=false
|
||||
|
||||
# CORS Origins (comma-separated)
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:5173
|
||||
|
||||
# ============================================================================
|
||||
# REPORTS MODULE - CACHE CONFIGURATION (OPTIONAL - defaults provided)
|
||||
# ============================================================================
|
||||
# Two-tier hybrid cache system (L1: in-memory LRU, L2: SQLite persistent)
|
||||
# Used by backend/modules/reports/cache/config.py
|
||||
|
||||
# Core Settings
|
||||
REPORTS_CACHE_ENABLED=True
|
||||
REPORTS_CACHE_TYPE=hybrid
|
||||
REPORTS_CACHE_SQLITE_PATH=./data/cache/roa2web_cache.db
|
||||
REPORTS_CACHE_MEMORY_MAX_SIZE=1000
|
||||
REPORTS_CACHE_DEFAULT_TTL=900
|
||||
|
||||
# TTL per Cache Type (seconds)
|
||||
REPORTS_CACHE_TTL_SCHEMA=86400
|
||||
REPORTS_CACHE_TTL_COMPANIES=1800
|
||||
REPORTS_CACHE_TTL_DASHBOARD_SUMMARY=1800
|
||||
REPORTS_CACHE_TTL_DASHBOARD_TRENDS=1800
|
||||
REPORTS_CACHE_TTL_INVOICES=600
|
||||
REPORTS_CACHE_TTL_INVOICES_SUMMARY=900
|
||||
REPORTS_CACHE_TTL_TREASURY=600
|
||||
|
||||
# Maintenance
|
||||
REPORTS_CACHE_CLEANUP_INTERVAL=3600
|
||||
|
||||
# Event-Based Invalidation (experimental)
|
||||
REPORTS_CACHE_AUTO_INVALIDATE=False
|
||||
REPORTS_CACHE_CHECK_INTERVAL=300
|
||||
|
||||
# Performance Tracking
|
||||
REPORTS_CACHE_TRACK_PERFORMANCE=True
|
||||
REPORTS_CACHE_BENCHMARK_ON_STARTUP=False
|
||||
|
||||
# ============================================================================
|
||||
# DATA ENTRY MODULE - CONFIGURATION
|
||||
# ============================================================================
|
||||
# Data Entry module settings (receipts, OCR, etc.)
|
||||
|
||||
# SQLite Database
|
||||
DATA_ENTRY_SQLITE_DATABASE_PATH=data/receipts/receipts.db
|
||||
|
||||
# File uploads
|
||||
DATA_ENTRY_UPLOAD_PATH=data/receipts/uploads
|
||||
DATA_ENTRY_MAX_UPLOAD_SIZE_MB=10
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - BOT CONFIGURATION (REQUIRED for Telegram features)
|
||||
# ============================================================================
|
||||
# Obtain bot token from @BotFather on Telegram
|
||||
|
||||
TELEGRAM_BOT_TOKEN=your_bot_token_here
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - EMAIL AUTHENTICATION (SMTP) (REQUIRED for email 2FA)
|
||||
# ============================================================================
|
||||
# Required for email-based 2FA authentication flow
|
||||
# Users can login with email + password instead of web app linking
|
||||
|
||||
# SMTP Server Configuration
|
||||
TELEGRAM_SMTP_HOST=mail.romfast.ro
|
||||
TELEGRAM_SMTP_PORT=587
|
||||
TELEGRAM_SMTP_USER=ups@romfast.ro
|
||||
TELEGRAM_SMTP_PASSWORD=your_smtp_password_here
|
||||
TELEGRAM_SMTP_FROM_EMAIL=ups@romfast.ro
|
||||
TELEGRAM_SMTP_FROM_NAME=ROA2WEB
|
||||
TELEGRAM_SMTP_USE_TLS=true
|
||||
|
||||
# Email Retry Settings
|
||||
TELEGRAM_EMAIL_MAX_RETRIES=3
|
||||
TELEGRAM_EMAIL_RETRY_DELAY=2.0
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - DATABASE (SQLite for bot data)
|
||||
# ============================================================================
|
||||
# Separate SQLite database for Telegram bot auth codes and sessions
|
||||
|
||||
TELEGRAM_SQLITE_DATABASE_PATH=data/telegram/telegram.db
|
||||
139
backend/.env.prod.example
Normal file
139
backend/.env.prod.example
Normal file
@@ -0,0 +1,139 @@
|
||||
# ============================================================================
|
||||
# ROA2WEB Unified Backend - Environment Configuration (PRODUCTION)
|
||||
# ============================================================================
|
||||
# Single backend process serving Reports, Data Entry, and Telegram modules
|
||||
# IMPORTANT: This is a TEMPLATE - fill in production values before deploying!
|
||||
|
||||
# ============================================================================
|
||||
# ORACLE DATABASE CONFIGURATION (REQUIRED - Shared by all modules)
|
||||
# ============================================================================
|
||||
# Connection to CONTAFIN_ORACLE schema for authentication and user management
|
||||
# PRODUCTION: Direct connection to Oracle server (no SSH tunnel)
|
||||
|
||||
ORACLE_USER=CONTAFIN_ORACLE
|
||||
ORACLE_PASSWORD=CHANGE_IN_PRODUCTION
|
||||
ORACLE_HOST=your_oracle_server_ip_or_hostname
|
||||
ORACLE_PORT=1521
|
||||
ORACLE_SID=ROA
|
||||
|
||||
# ============================================================================
|
||||
# JWT AUTHENTICATION (REQUIRED - Shared by all modules)
|
||||
# ============================================================================
|
||||
# CRITICAL: Generate new secrets for production!
|
||||
# python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
|
||||
JWT_SECRET_KEY=GENERATE_NEW_SECRET_FOR_PRODUCTION
|
||||
JWT_ALGORITHM=HS256
|
||||
|
||||
# Token expiration settings
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||
|
||||
# ============================================================================
|
||||
# SESSION SECURITY - EMAIL 2FA (REQUIRED for Telegram email login)
|
||||
# ============================================================================
|
||||
# CRITICAL: Generate new secret for production!
|
||||
# python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
|
||||
AUTH_SESSION_SECRET=GENERATE_NEW_SECRET_FOR_PRODUCTION
|
||||
|
||||
# ============================================================================
|
||||
# SERVER CONFIGURATION
|
||||
# ============================================================================
|
||||
# Unified backend server settings
|
||||
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=8000
|
||||
DEBUG=false
|
||||
|
||||
# CORS Origins (comma-separated) - Update with production frontend URL
|
||||
CORS_ORIGINS=https://your-production-domain.com,http://localhost:3000
|
||||
|
||||
# ============================================================================
|
||||
# REPORTS MODULE - CACHE CONFIGURATION (OPTIONAL - defaults provided)
|
||||
# ============================================================================
|
||||
# Two-tier hybrid cache system (L1: in-memory LRU, L2: SQLite persistent)
|
||||
|
||||
# Core Settings
|
||||
CACHE_ENABLED=True
|
||||
CACHE_TYPE=hybrid
|
||||
CACHE_SQLITE_PATH=./data/cache/roa2web_cache_prod.db
|
||||
CACHE_MEMORY_MAX_SIZE=1000
|
||||
CACHE_DEFAULT_TTL=900
|
||||
|
||||
# TTL per Cache Type (seconds)
|
||||
CACHE_TTL_SCHEMA=86400
|
||||
CACHE_TTL_COMPANIES=1800
|
||||
CACHE_TTL_DASHBOARD_SUMMARY=1800
|
||||
CACHE_TTL_DASHBOARD_TRENDS=1800
|
||||
CACHE_TTL_INVOICES=600
|
||||
CACHE_TTL_INVOICES_SUMMARY=900
|
||||
CACHE_TTL_TREASURY=600
|
||||
|
||||
# Maintenance
|
||||
CACHE_CLEANUP_INTERVAL=3600
|
||||
|
||||
# Event-Based Invalidation (experimental)
|
||||
CACHE_AUTO_INVALIDATE=False
|
||||
CACHE_CHECK_INTERVAL=300
|
||||
|
||||
# Performance Tracking
|
||||
CACHE_TRACK_PERFORMANCE=True
|
||||
CACHE_BENCHMARK_ON_STARTUP=False
|
||||
|
||||
# ============================================================================
|
||||
# DATA ENTRY MODULE - CONFIGURATION
|
||||
# ============================================================================
|
||||
# Data Entry module settings (receipts, OCR, etc.)
|
||||
|
||||
# Environment identifier
|
||||
ORACLE_ENV=prod
|
||||
|
||||
# SQLite Database (production)
|
||||
SQLITE_DATABASE_PATH=data/receipts/receipts_prod.db
|
||||
|
||||
# File uploads
|
||||
UPLOAD_PATH=data/receipts/uploads
|
||||
MAX_UPLOAD_SIZE_MB=10
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - BOT CONFIGURATION (REQUIRED for Telegram features)
|
||||
# ============================================================================
|
||||
# Obtain bot token from @BotFather on Telegram
|
||||
# CRITICAL: Use production bot token, not development!
|
||||
|
||||
TELEGRAM_BOT_TOKEN=your_bot_token_from_botfather
|
||||
|
||||
# Backend URL for bot to communicate with API
|
||||
BACKEND_URL=http://localhost:8000
|
||||
|
||||
# Internal API port (bot's internal API for backend callbacks)
|
||||
INTERNAL_API_PORT=8002
|
||||
|
||||
# Enable internal API documentation (DISABLE in production!)
|
||||
ENABLE_DOCS=false
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - EMAIL AUTHENTICATION (SMTP) (REQUIRED for email 2FA)
|
||||
# ============================================================================
|
||||
# CRITICAL: Update with production SMTP credentials
|
||||
|
||||
# SMTP Server Configuration
|
||||
SMTP_HOST=mail.romfast.ro
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=ups@romfast.ro
|
||||
SMTP_PASSWORD=CHANGE_IN_PRODUCTION
|
||||
SMTP_FROM_EMAIL=ups@romfast.ro
|
||||
SMTP_FROM_NAME=ROA2WEB
|
||||
SMTP_USE_TLS=true
|
||||
|
||||
# Email Retry Settings
|
||||
EMAIL_MAX_RETRIES=3
|
||||
EMAIL_RETRY_DELAY=2.0
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - DATABASE (SQLite for bot data)
|
||||
# ============================================================================
|
||||
# Separate SQLite database for Telegram bot auth codes and sessions
|
||||
|
||||
TELEGRAM_SQLITE_DATABASE_PATH=data/telegram/telegram_prod.db
|
||||
147
backend/.env.test.example
Normal file
147
backend/.env.test.example
Normal file
@@ -0,0 +1,147 @@
|
||||
# ============================================================================
|
||||
# ROA2WEB Unified Backend - Environment Configuration (TEST)
|
||||
# ============================================================================
|
||||
# TEST environment using Oracle TEST server (10.0.20.121)
|
||||
# Single backend process serving Reports, Data Entry, and Telegram modules
|
||||
# IMPORTANT: Never commit this file to git!
|
||||
|
||||
# ============================================================================
|
||||
# ORACLE DATABASE CONFIGURATION (REQUIRED - Shared by all modules)
|
||||
# ============================================================================
|
||||
# Connection to CONTAFIN_ORACLE schema for authentication and user management
|
||||
# TEST: Through SSH tunnel to 10.0.20.121 (localhost:1526)
|
||||
|
||||
ORACLE_USER=CONTAFIN_ORACLE
|
||||
ORACLE_PASSWORD=your_oracle_password_here
|
||||
ORACLE_HOST=localhost
|
||||
ORACLE_PORT=1526
|
||||
ORACLE_SID=roa
|
||||
|
||||
# TEST: Start SSH tunnel before running backend
|
||||
# ./ssh-tunnel-test.sh start
|
||||
|
||||
# ============================================================================
|
||||
# JWT AUTHENTICATION (REQUIRED - Shared by all modules)
|
||||
# ============================================================================
|
||||
# Used for JWT token generation and validation (shared/auth/jwt_handler.py)
|
||||
|
||||
JWT_SECRET_KEY=generate_with_secrets_token_urlsafe_32
|
||||
JWT_ALGORITHM=HS256
|
||||
|
||||
# Token expiration settings (used by shared/auth/jwt_handler.py)
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=480
|
||||
REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||
|
||||
# ============================================================================
|
||||
# SESSION SECURITY - EMAIL 2FA (REQUIRED for Telegram email login)
|
||||
# ============================================================================
|
||||
# Used by Telegram module for session token validation
|
||||
# Must match between backend and Telegram bot
|
||||
|
||||
AUTH_SESSION_SECRET=generate_with_secrets_token_urlsafe_32
|
||||
|
||||
# ============================================================================
|
||||
# SERVER CONFIGURATION
|
||||
# ============================================================================
|
||||
# Unified backend server settings
|
||||
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=8000
|
||||
DEBUG=true
|
||||
|
||||
# CORS Origins (comma-separated, includes both old and new frontend ports)
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:3010,http://localhost:5173
|
||||
|
||||
# ============================================================================
|
||||
# REPORTS MODULE - CACHE CONFIGURATION (OPTIONAL - defaults provided)
|
||||
# ============================================================================
|
||||
# Two-tier hybrid cache system (L1: in-memory LRU, L2: SQLite persistent)
|
||||
# Used by backend/modules/reports/cache/config.py
|
||||
|
||||
# Core Settings
|
||||
CACHE_ENABLED=True
|
||||
CACHE_TYPE=hybrid
|
||||
CACHE_SQLITE_PATH=./data/cache/roa2web_cache_test.db
|
||||
CACHE_MEMORY_MAX_SIZE=1000
|
||||
CACHE_DEFAULT_TTL=900
|
||||
|
||||
# TTL per Cache Type (seconds)
|
||||
CACHE_TTL_SCHEMA=86400
|
||||
CACHE_TTL_COMPANIES=1800
|
||||
CACHE_TTL_DASHBOARD_SUMMARY=1800
|
||||
CACHE_TTL_DASHBOARD_TRENDS=1800
|
||||
CACHE_TTL_INVOICES=600
|
||||
CACHE_TTL_INVOICES_SUMMARY=900
|
||||
CACHE_TTL_TREASURY=600
|
||||
|
||||
# Maintenance
|
||||
CACHE_CLEANUP_INTERVAL=3600
|
||||
|
||||
# Event-Based Invalidation (experimental)
|
||||
CACHE_AUTO_INVALIDATE=False
|
||||
CACHE_CHECK_INTERVAL=300
|
||||
|
||||
# Performance Tracking
|
||||
CACHE_TRACK_PERFORMANCE=True
|
||||
CACHE_BENCHMARK_ON_STARTUP=False
|
||||
|
||||
# ============================================================================
|
||||
# DATA ENTRY MODULE - CONFIGURATION
|
||||
# ============================================================================
|
||||
# Data Entry module settings (receipts, OCR, etc.)
|
||||
|
||||
# Environment identifier (dev/test/prod)
|
||||
ORACLE_ENV=test
|
||||
|
||||
# SQLite Database (test)
|
||||
SQLITE_DATABASE_PATH=data/receipts/receipts_test.db
|
||||
|
||||
# File uploads
|
||||
UPLOAD_PATH=data/receipts/uploads
|
||||
MAX_UPLOAD_SIZE_MB=10
|
||||
|
||||
# Test company (for testing)
|
||||
TEST_COMPANY_ID=110
|
||||
TEST_COMPANY_SCHEMA=MARIUSM_AUTO
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - BOT CONFIGURATION (REQUIRED for Telegram features)
|
||||
# ============================================================================
|
||||
# Obtain bot token from @BotFather on Telegram
|
||||
|
||||
TELEGRAM_BOT_TOKEN=your_bot_token_from_botfather
|
||||
|
||||
# Backend URL for bot to communicate with API
|
||||
BACKEND_URL=http://localhost:8000
|
||||
|
||||
# Internal API port (bot's internal API for backend callbacks)
|
||||
INTERNAL_API_PORT=8002
|
||||
|
||||
# Enable internal API documentation (development only)
|
||||
ENABLE_DOCS=false
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - EMAIL AUTHENTICATION (SMTP) (REQUIRED for email 2FA)
|
||||
# ============================================================================
|
||||
# Required for email-based 2FA authentication flow
|
||||
# Users can login with email + password instead of web app linking
|
||||
|
||||
# SMTP Server Configuration
|
||||
SMTP_HOST=mail.romfast.ro
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=ups@romfast.ro
|
||||
SMTP_PASSWORD=your_smtp_password_here
|
||||
SMTP_FROM_EMAIL=ups@romfast.ro
|
||||
SMTP_FROM_NAME=ROA2WEB
|
||||
SMTP_USE_TLS=true
|
||||
|
||||
# Email Retry Settings
|
||||
EMAIL_MAX_RETRIES=3
|
||||
EMAIL_RETRY_DELAY=2.0
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - DATABASE (SQLite for bot data)
|
||||
# ============================================================================
|
||||
# Separate SQLite database for Telegram bot auth codes and sessions
|
||||
|
||||
TELEGRAM_SQLITE_DATABASE_PATH=data/telegram/telegram_test.db
|
||||
212
backend/ENV-SETUP.md
Normal file
212
backend/ENV-SETUP.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# Environment Configuration Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The unified backend uses environment-specific configuration files that are automatically loaded by startup scripts.
|
||||
|
||||
**SECURITY**: All `.env*` files (except `.env*.example`) contain real credentials and are **NEVER committed to git**.
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
backend/
|
||||
├── .env.prod.example # Production template (COMMITTED - no credentials)
|
||||
├── .env.test.example # Test template (COMMITTED - no credentials)
|
||||
├── .env.prod.example # Production template (COMMITTED - no credentials)
|
||||
├── .env.example # Generic template (COMMITTED)
|
||||
├── .env.prod # Production config (IGNORED - real credentials)
|
||||
├── .env.test # Test config (IGNORED - real credentials)
|
||||
├── .env.prod # Production config (IGNORED - real credentials)
|
||||
└── .env # Active config (IGNORED - auto-generated)
|
||||
```
|
||||
|
||||
## First-Time Setup
|
||||
|
||||
### Production
|
||||
```bash
|
||||
# 1. Copy template
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
|
||||
# 2. Edit with your credentials
|
||||
vim backend/.env.prod
|
||||
|
||||
# 3. Fill in:
|
||||
# - ORACLE_PASSWORD
|
||||
# - JWT_SECRET_KEY (generate with: python3 -c "import secrets; print(secrets.token_urlsafe(32))")
|
||||
# - AUTH_SESSION_SECRET (generate with: python3 -c "import secrets; print(secrets.token_urlsafe(32))")
|
||||
# - TELEGRAM_BOT_TOKEN (from @BotFather)
|
||||
# - SMTP_PASSWORD
|
||||
|
||||
# 4. Start
|
||||
./start-prod.sh
|
||||
```
|
||||
|
||||
### Test
|
||||
```bash
|
||||
# Same process with .env.test
|
||||
cp backend/.env.test.example backend/.env.test
|
||||
vim backend/.env.test
|
||||
# Fill in TEST credentials (separate from dev!)
|
||||
./start-test.sh
|
||||
```
|
||||
|
||||
### Production
|
||||
```bash
|
||||
# Same process with .env.prod
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
vim backend/.env.prod
|
||||
# Fill in PRODUCTION credentials (generate NEW secrets!)
|
||||
./start-backend.sh start
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Production
|
||||
```bash
|
||||
./start-prod.sh # Checks for .env.prod → copies to .env → starts backend
|
||||
```
|
||||
|
||||
### Test
|
||||
```bash
|
||||
./start-test.sh # Checks for .env.test → copies to .env → starts backend
|
||||
```
|
||||
|
||||
### Production
|
||||
```bash
|
||||
# Manual setup (one-time)
|
||||
cp .env.prod.example .env.prod
|
||||
vim .env.prod # Fill in credentials
|
||||
# Then start
|
||||
./start-backend.sh start
|
||||
```
|
||||
|
||||
## Important Rules
|
||||
|
||||
### ✅ DO
|
||||
- Copy `.env.*.example` to `.env.*` and fill in real credentials
|
||||
- Edit `.env.prod` for production changes
|
||||
- Edit `.env.test` for test environment changes
|
||||
- Edit `.env.prod` for production
|
||||
- Generate **new** secrets for each environment
|
||||
- Keep `.env.prod`, `.env.test`, `.env.prod` **local only** (never commit!)
|
||||
|
||||
### ❌ DON'T
|
||||
- Don't commit `.env`, `.env.prod`, `.env.test`, or `.env.prod` (they're in .gitignore)
|
||||
- Don't manually edit `.env` (it's auto-generated!)
|
||||
- Don't use same secrets across environments
|
||||
- Don't share credentials via git (use secure channels)
|
||||
- Don't put real credentials in `.env*.example` files
|
||||
|
||||
## Environment Differences
|
||||
|
||||
| Setting | .env.prod | .env.test | .env.prod |
|
||||
|---------|----------|-----------|-----------|
|
||||
| Oracle SID | `ROA` | `roa` | `ROA` |
|
||||
| JWT Expire | 30 min | 480 min | 30 min |
|
||||
| DEBUG | `true` | `true` | `false` |
|
||||
| Cache DB | `roa2web_cache.db` | `roa2web_cache_test.db` | `roa2web_cache_prod.db` |
|
||||
| Receipts DB | `receipts_dev.db` | `receipts_test.db` | `receipts_prod.db` |
|
||||
| Telegram DB | `telegram.db` | `telegram_test.db` | `telegram_prod.db` |
|
||||
|
||||
## Security Notes
|
||||
|
||||
### Template Files (.env.*.example)
|
||||
These contain **placeholders only**:
|
||||
- ✅ Safe to commit to git
|
||||
- ✅ Shared across team
|
||||
- ✅ No real credentials
|
||||
- 📖 Used as reference for first-time setup
|
||||
|
||||
### Actual Config Files (.env.prod, .env.test, .env.prod)
|
||||
These contain **real credentials**:
|
||||
- ❌ **NEVER commit to git** (in .gitignore)
|
||||
- ❌ Never share via email/chat
|
||||
- ✅ Keep local only
|
||||
- ✅ Generate unique secrets per environment
|
||||
- 🔐 Share securely if needed (encrypted vault, 1Password, etc.)
|
||||
|
||||
### Active Config (.env)
|
||||
This is **auto-generated** and **ignored by git**:
|
||||
- ❌ Never commit to git
|
||||
- 🔄 Auto-overwritten by startup scripts
|
||||
- 📝 Edit source files (.env.prod, .env.test) instead
|
||||
|
||||
## Generating Secrets
|
||||
|
||||
For `JWT_SECRET_KEY` and `AUTH_SESSION_SECRET`:
|
||||
```bash
|
||||
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
```
|
||||
|
||||
Generate **different** secrets for dev, test, and production!
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### First Time Setup
|
||||
```bash
|
||||
# 1. Copy template
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
|
||||
# 2. Fill credentials
|
||||
vim backend/.env.prod
|
||||
|
||||
# 3. Start
|
||||
./start-prod.sh
|
||||
```
|
||||
|
||||
### Changing Configuration
|
||||
```bash
|
||||
# 1. Edit source file
|
||||
vim backend/.env.prod
|
||||
|
||||
# 2. Restart to apply
|
||||
./start-prod.sh
|
||||
```
|
||||
|
||||
### Production Deployment
|
||||
```bash
|
||||
# 1. Copy template
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
|
||||
# 2. Fill in PRODUCTION values
|
||||
vim backend/.env.prod
|
||||
|
||||
# 3. Generate NEW secrets
|
||||
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
|
||||
# 4. Start backend
|
||||
./start-backend.sh start
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Wrong database" error
|
||||
Check that you're using the correct startup script:
|
||||
- Production: `./start-prod.sh` (uses `.env.prod`)
|
||||
- Test: `./start-test.sh` (uses `.env.test`)
|
||||
|
||||
### ".env.prod not found" error
|
||||
First-time setup required:
|
||||
```bash
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
vim backend/.env.prod # Fill in your credentials
|
||||
```
|
||||
|
||||
### Changes not taking effect
|
||||
The `.env` file is regenerated on each start. Edit the source file (`.env.prod` or `.env.test`) instead.
|
||||
|
||||
### Checking what will be committed
|
||||
```bash
|
||||
git status backend/.env*
|
||||
# Should show:
|
||||
# modified: .env.prod.example (if you changed template)
|
||||
# nothing else!
|
||||
```
|
||||
|
||||
## Team Sharing
|
||||
|
||||
**Templates only** are committed to git:
|
||||
- Share configuration structure via `.env*.example`
|
||||
- Each developer creates their own `.env.prod` from template
|
||||
- Never commit actual credentials
|
||||
- Use secure channels for sharing sensitive values (1Password, encrypted vault, etc.)
|
||||
102
backend/QUICK-ENV-REFERENCE.md
Normal file
102
backend/QUICK-ENV-REFERENCE.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Quick Environment Reference
|
||||
|
||||
## 🔒 SECURITY FIRST
|
||||
|
||||
**All `.env*` files (except `.env*.example`) contain real credentials and are NEVER committed to git!**
|
||||
|
||||
## 🚀 First-Time Setup
|
||||
|
||||
```bash
|
||||
# 1. Copy template with real credentials
|
||||
cp backend/.env.prod.example backend/.env.prod
|
||||
|
||||
# 2. Edit with YOUR credentials
|
||||
vim backend/.env.prod
|
||||
|
||||
# 3. Fill in the placeholders:
|
||||
# - ORACLE_PASSWORD
|
||||
# - JWT_SECRET_KEY
|
||||
# - AUTH_SESSION_SECRET
|
||||
# - TELEGRAM_BOT_TOKEN
|
||||
# - SMTP_PASSWORD
|
||||
|
||||
# 4. Start production
|
||||
./start-prod.sh
|
||||
```
|
||||
|
||||
## 📋 Daily Usage
|
||||
|
||||
```bash
|
||||
# Production (uses .env.prod automatically)
|
||||
./start-prod.sh
|
||||
|
||||
# Test Environment (uses .env.test automatically)
|
||||
./start-test.sh
|
||||
|
||||
# Quick Restart (uses existing .env)
|
||||
./start-backend.sh restart
|
||||
```
|
||||
|
||||
## ✏️ Changing Configuration
|
||||
|
||||
```bash
|
||||
# 1. Edit the source file (NOT .env!)
|
||||
vim backend/.env.prod # Production
|
||||
vim backend/.env.test # Test
|
||||
|
||||
# 2. Restart to apply changes
|
||||
./start-prod.sh
|
||||
```
|
||||
|
||||
## 📁 Which File to Edit?
|
||||
|
||||
| You Want To... | Edit This File |
|
||||
|----------------|----------------|
|
||||
| Change dev database password | `backend/.env.prod` |
|
||||
| Update test server settings | `backend/.env.test` |
|
||||
| Add new environment variable | Templates: `.env*.example` + your `.env.prod`/`.env.test` |
|
||||
| Create production config | Copy `.env.prod.example` to `.env.prod` and fill secrets |
|
||||
|
||||
## 🔑 Generating Secrets
|
||||
|
||||
```bash
|
||||
# For JWT_SECRET_KEY and AUTH_SESSION_SECRET
|
||||
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
```
|
||||
|
||||
**Generate DIFFERENT secrets for each environment (dev, test, prod)!**
|
||||
|
||||
## ⚠️ Important
|
||||
|
||||
- **Never edit** `backend/.env` directly (it's auto-generated!)
|
||||
- **Always edit** `backend/.env.prod` or `.env.test`
|
||||
- **Never commit** `.env`, `.env.prod`, `.env.test`, `.env.prod`
|
||||
- **Only commit** `.env*.example` (templates with placeholders)
|
||||
- Restart after changes for them to take effect
|
||||
|
||||
## 🛡️ Git Behavior
|
||||
|
||||
| File | Git Status | Contains |
|
||||
|------|-----------|----------|
|
||||
| `.env.prod.example` | ✅ Committed | Template (placeholders) |
|
||||
| `.env.test.example` | ✅ Committed | Template (placeholders) |
|
||||
| `.env.prod.example` | ✅ Committed | Template (placeholders) |
|
||||
| `.env.example` | ✅ Committed | Generic template |
|
||||
| `.env.prod` | ❌ Ignored | **Real dev credentials** |
|
||||
| `.env.test` | ❌ Ignored | **Real test credentials** |
|
||||
| `.env.prod` | ❌ Ignored | **Real prod credentials** |
|
||||
| `.env` | ❌ Ignored | Auto-generated (current) |
|
||||
|
||||
## ✅ Quick Check
|
||||
|
||||
```bash
|
||||
# See what git will commit
|
||||
git status backend/.env*
|
||||
|
||||
# Should show ONLY .env*.example files
|
||||
# If .env.prod or .env.test appear, they're NOT properly ignored!
|
||||
```
|
||||
|
||||
## 📖 More Info
|
||||
|
||||
See `backend/ENV-SETUP.md` for complete documentation.
|
||||
173
backend/config.py
Normal file
173
backend/config.py
Normal file
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
Unified Configuration for ROA2WEB Backend
|
||||
Consolidates settings from Reports, Data Entry, and Telegram modules
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
from pydantic_settings import BaseSettings
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
class UnifiedSettings(BaseSettings):
|
||||
"""Unified application settings for all modules."""
|
||||
|
||||
# ============================================================================
|
||||
# GENERAL APPLICATION SETTINGS
|
||||
# ============================================================================
|
||||
app_name: str = "ROA2WEB Unified Backend"
|
||||
app_version: str = "1.0.0"
|
||||
debug: bool = False
|
||||
api_host: str = "0.0.0.0"
|
||||
api_port: int = 8000
|
||||
|
||||
# ============================================================================
|
||||
# ORACLE DATABASE (Shared by all modules)
|
||||
# ============================================================================
|
||||
oracle_user: str = ""
|
||||
oracle_password: str = ""
|
||||
oracle_host: str = "localhost"
|
||||
oracle_port: int = 1526
|
||||
oracle_sid: str = "ROA"
|
||||
|
||||
# ============================================================================
|
||||
# JWT AUTHENTICATION (Shared by all modules)
|
||||
# ============================================================================
|
||||
jwt_secret_key: str = "change-me-in-production"
|
||||
jwt_algorithm: str = "HS256"
|
||||
access_token_expire_minutes: int = 30
|
||||
refresh_token_expire_days: int = 7
|
||||
|
||||
# ============================================================================
|
||||
# SESSION SECURITY - EMAIL 2FA (Telegram module)
|
||||
# ============================================================================
|
||||
auth_session_secret: str = "change-me-in-production"
|
||||
|
||||
# ============================================================================
|
||||
# CORS
|
||||
# ============================================================================
|
||||
cors_origins: str = "http://localhost:3000,http://localhost:5173"
|
||||
|
||||
# ============================================================================
|
||||
# REPORTS MODULE - CACHE CONFIGURATION
|
||||
# ============================================================================
|
||||
reports_cache_enabled: bool = True
|
||||
reports_cache_type: str = "hybrid"
|
||||
reports_cache_sqlite_path: str = "./data/cache/roa2web_cache.db"
|
||||
reports_cache_memory_max_size: int = 1000
|
||||
reports_cache_default_ttl: int = 900
|
||||
|
||||
# Cache TTL per type (seconds)
|
||||
reports_cache_ttl_schema: int = 86400
|
||||
reports_cache_ttl_companies: int = 1800
|
||||
reports_cache_ttl_dashboard_summary: int = 1800
|
||||
reports_cache_ttl_dashboard_trends: int = 1800
|
||||
reports_cache_ttl_invoices: int = 600
|
||||
reports_cache_ttl_invoices_summary: int = 900
|
||||
reports_cache_ttl_treasury: int = 600
|
||||
|
||||
# Cache maintenance
|
||||
reports_cache_cleanup_interval: int = 3600
|
||||
reports_cache_auto_invalidate: bool = False
|
||||
reports_cache_check_interval: int = 300
|
||||
reports_cache_track_performance: bool = True
|
||||
reports_cache_benchmark_on_startup: bool = False
|
||||
|
||||
# ============================================================================
|
||||
# DATA ENTRY MODULE - CONFIGURATION
|
||||
# ============================================================================
|
||||
data_entry_sqlite_database_path: str = "data/receipts/receipts.db"
|
||||
data_entry_upload_path: str = "data/receipts/uploads"
|
||||
data_entry_max_upload_size_mb: int = 10
|
||||
data_entry_allowed_mime_types: List[str] = [
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
"application/pdf",
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# TELEGRAM MODULE - BOT CONFIGURATION
|
||||
# ============================================================================
|
||||
telegram_bot_token: str = ""
|
||||
telegram_smtp_host: str = ""
|
||||
telegram_smtp_port: int = 587
|
||||
telegram_smtp_user: str = ""
|
||||
telegram_smtp_password: str = ""
|
||||
telegram_smtp_from_email: str = ""
|
||||
telegram_smtp_from_name: str = "ROA2WEB"
|
||||
telegram_smtp_use_tls: bool = True
|
||||
telegram_email_max_retries: int = 3
|
||||
telegram_email_retry_delay: float = 2.0
|
||||
telegram_sqlite_database_path: str = "data/telegram/telegram.db"
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
env_file_encoding = "utf-8"
|
||||
extra = "ignore"
|
||||
case_sensitive = False
|
||||
|
||||
# ============================================================================
|
||||
# COMPUTED PROPERTIES
|
||||
# ============================================================================
|
||||
|
||||
@property
|
||||
def oracle_dsn(self) -> str:
|
||||
"""Get Oracle DSN string."""
|
||||
return f"{self.oracle_host}:{self.oracle_port}/{self.oracle_sid}"
|
||||
|
||||
@property
|
||||
def cors_origins_list(self) -> List[str]:
|
||||
"""Get CORS origins as list."""
|
||||
return [origin.strip() for origin in self.cors_origins.split(",")]
|
||||
|
||||
# Data Entry properties
|
||||
@property
|
||||
def data_entry_database_url(self) -> str:
|
||||
"""Get SQLite database URL for async (Data Entry)."""
|
||||
return f"sqlite+aiosqlite:///{self.data_entry_sqlite_database_path}"
|
||||
|
||||
@property
|
||||
def data_entry_sync_database_url(self) -> str:
|
||||
"""Get SQLite database URL for sync operations (Alembic)."""
|
||||
return f"sqlite:///{self.data_entry_sqlite_database_path}"
|
||||
|
||||
@property
|
||||
def data_entry_upload_path_resolved(self) -> Path:
|
||||
"""Get resolved upload path."""
|
||||
path = Path(self.data_entry_upload_path)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
@property
|
||||
def data_entry_max_upload_size_bytes(self) -> int:
|
||||
"""Get max upload size in bytes."""
|
||||
return self.data_entry_max_upload_size_mb * 1024 * 1024
|
||||
|
||||
# Reports cache properties
|
||||
@property
|
||||
def reports_cache_sqlite_path_resolved(self) -> Path:
|
||||
"""Get resolved cache SQLite path."""
|
||||
path = Path(self.reports_cache_sqlite_path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
# Telegram properties
|
||||
@property
|
||||
def telegram_sqlite_path_resolved(self) -> Path:
|
||||
"""Get resolved Telegram SQLite path."""
|
||||
path = Path(self.telegram_sqlite_database_path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def get_settings() -> UnifiedSettings:
|
||||
"""Get cached settings instance."""
|
||||
return UnifiedSettings()
|
||||
|
||||
|
||||
# Convenience instance
|
||||
settings = get_settings()
|
||||
45
backend/data/README.md
Normal file
45
backend/data/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Backend Runtime Data
|
||||
|
||||
This directory contains runtime data generated by the unified backend.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
data/
|
||||
├── cache/ # Reports module cache (hybrid L1+L2)
|
||||
│ └── *.db # SQLite L2 cache database
|
||||
├── receipts/ # Data Entry module data
|
||||
│ ├── *.db # SQLite receipts database
|
||||
│ └── uploads/ # User-uploaded files (receipts, attachments)
|
||||
└── telegram/ # Telegram bot data
|
||||
└── *.db # SQLite bot auth/session database
|
||||
```
|
||||
|
||||
## Git Behavior
|
||||
|
||||
- **Ignored**: All `*.db` files and `uploads/` contents
|
||||
- **Committed**: Only `.gitkeep` files to preserve directory structure
|
||||
|
||||
## Environment-Specific Databases
|
||||
|
||||
Different environments use separate databases:
|
||||
|
||||
- **Development** (`.env.prod`):
|
||||
- Cache: `roa2web_cache.db`
|
||||
- Receipts: `receipts_dev.db`
|
||||
- Telegram: `telegram.db`
|
||||
|
||||
- **Test** (`.env.test`):
|
||||
- Cache: `roa2web_cache_test.db`
|
||||
- Receipts: `receipts_test.db`
|
||||
- Telegram: `telegram_test.db`
|
||||
|
||||
- **Production** (`.env.prod`):
|
||||
- Cache: `roa2web_cache_prod.db`
|
||||
- Receipts: `receipts_prod.db`
|
||||
- Telegram: `telegram_prod.db`
|
||||
|
||||
## Auto-Created
|
||||
|
||||
All databases and directories are created automatically on first run.
|
||||
No manual setup required.
|
||||
428
backend/main.py
Normal file
428
backend/main.py
Normal file
@@ -0,0 +1,428 @@
|
||||
"""
|
||||
ROA2WEB Unified Backend - Single FastAPI Application
|
||||
Consolidates Reports, Data Entry, and Telegram modules into one process
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Add project root and shared modules to path
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root)) # Enable 'from backend.xxx import yyy'
|
||||
sys.path.insert(0, str(project_root / "shared")) # Enable 'from shared.xxx import yyy'
|
||||
|
||||
# Import configuration
|
||||
from backend.config import settings
|
||||
|
||||
# Import shared infrastructure
|
||||
from shared.database.oracle_pool import oracle_pool
|
||||
from shared.auth.middleware import AuthenticationMiddleware
|
||||
from shared.auth.routes import create_auth_router
|
||||
from shared.routes.companies import create_companies_router
|
||||
from shared.routes.calendar import create_calendar_router
|
||||
|
||||
# Import module router factories
|
||||
from backend.modules.reports.routers import create_reports_router
|
||||
from backend.modules.data_entry.routers import create_data_entry_router
|
||||
from backend.modules.telegram.routers import create_telegram_router
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
datefmt='%H:%M:%S'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Global variables for background tasks
|
||||
telegram_bot_task = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# INITIALIZATION FUNCTIONS
|
||||
# ============================================================================
|
||||
|
||||
async def init_oracle_pool():
|
||||
"""Initialize Oracle connection pool (shared by all modules)."""
|
||||
logger.info("[ORACLE] Initializing connection pool...")
|
||||
await oracle_pool.initialize()
|
||||
logger.info("[ORACLE] ✅ Pool initialized successfully")
|
||||
|
||||
|
||||
async def init_reports_cache():
|
||||
"""Initialize Reports cache system."""
|
||||
logger.info("[REPORTS] Initializing cache system...")
|
||||
try:
|
||||
from backend.modules.reports.cache import init_cache, init_event_monitor, get_cache
|
||||
from backend.modules.reports.cache.config import CacheConfig
|
||||
|
||||
cache_config = CacheConfig.from_env()
|
||||
await init_cache(cache_config)
|
||||
logger.info(f"[REPORTS] ✅ Cache initialized: type={cache_config.cache_type}, enabled={cache_config.enabled}")
|
||||
|
||||
# Initialize event monitor
|
||||
cache = get_cache()
|
||||
await init_event_monitor(cache, cache_config)
|
||||
if cache_config.auto_invalidate_enabled:
|
||||
logger.info("[REPORTS] Event-based auto-invalidation ENABLED")
|
||||
else:
|
||||
logger.info("[REPORTS] Event-based auto-invalidation DISABLED")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[REPORTS] ⚠️ Cache initialization error: {e}", exc_info=True)
|
||||
logger.warning("[REPORTS] Continuing without cache")
|
||||
|
||||
|
||||
async def init_data_entry_db():
|
||||
"""Initialize Data Entry SQLite database."""
|
||||
logger.info("[DATA-ENTRY] Initializing SQLite database...")
|
||||
try:
|
||||
from backend.modules.data_entry.db.database import init_db
|
||||
await init_db()
|
||||
logger.info(f"[DATA-ENTRY] ✅ Database initialized: {settings.data_entry_sqlite_database_path}")
|
||||
|
||||
# Ensure upload directory exists
|
||||
settings.data_entry_upload_path_resolved
|
||||
logger.info(f"[DATA-ENTRY] Upload path: {settings.data_entry_upload_path_resolved}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[DATA-ENTRY] ❌ Database initialization error: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
async def init_telegram_db():
|
||||
"""Initialize Telegram SQLite database."""
|
||||
logger.info("[TELEGRAM] Initializing SQLite database...")
|
||||
try:
|
||||
from backend.modules.telegram.db import init_database, cleanup_expired_codes, cleanup_expired_sessions, cleanup_expired_email_codes
|
||||
|
||||
await init_database()
|
||||
logger.info(f"[TELEGRAM] ✅ Database initialized: {settings.telegram_sqlite_database_path}")
|
||||
|
||||
# Cleanup expired data
|
||||
expired_codes = await cleanup_expired_codes()
|
||||
expired_sessions = await cleanup_expired_sessions()
|
||||
expired_email_codes = await cleanup_expired_email_codes()
|
||||
logger.info(f"[TELEGRAM] Cleanup: {expired_codes} codes, {expired_sessions} sessions, {expired_email_codes} email codes removed")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[TELEGRAM] ❌ Database initialization error: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
def init_paddle_ocr_background():
|
||||
"""Initialize PaddleOCR in background thread (takes 15-20s)."""
|
||||
try:
|
||||
logger.info("[DATA-ENTRY] Pre-loading OCR engine (background)...")
|
||||
from backend.modules.data_entry.services.ocr_service import ocr_service
|
||||
ocr_service.ocr_engine._init_paddle_lazy()
|
||||
logger.info("[DATA-ENTRY] ✅ OCR engine ready")
|
||||
except Exception as e:
|
||||
logger.warning(f"[DATA-ENTRY] ⚠️ OCR engine pre-load failed: {e}")
|
||||
|
||||
|
||||
async def run_telegram_bot():
|
||||
"""Run Telegram bot as background task."""
|
||||
logger.info("[TELEGRAM] Starting bot...")
|
||||
try:
|
||||
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, MessageHandler, filters
|
||||
from backend.modules.telegram.bot.handlers import (
|
||||
start_command, help_command, clear_command, companies_command,
|
||||
unlink_command, selectcompany_command, dashboard_command, sold_command,
|
||||
facturi_command, trezorerie_command, menu_command, trezorerie_casa_command,
|
||||
trezorerie_banca_command, clienti_command, furnizori_command, evolutie_command,
|
||||
clearcache_command, togglecache_command, handle_text_message, button_callback,
|
||||
error_handler
|
||||
)
|
||||
from backend.modules.telegram.bot.email_handlers import email_login_handler
|
||||
|
||||
# Create Telegram application
|
||||
application = Application.builder().token(settings.telegram_bot_token).build()
|
||||
|
||||
# Register handlers
|
||||
application.add_handler(email_login_handler)
|
||||
application.add_handler(CommandHandler("start", start_command))
|
||||
application.add_handler(CommandHandler("menu", menu_command))
|
||||
application.add_handler(CommandHandler("help", help_command))
|
||||
application.add_handler(CommandHandler("unlink", unlink_command))
|
||||
application.add_handler(CommandHandler("clear", clear_command))
|
||||
application.add_handler(CommandHandler("companies", companies_command))
|
||||
application.add_handler(CommandHandler("selectcompany", selectcompany_command))
|
||||
application.add_handler(CommandHandler("dashboard", dashboard_command))
|
||||
application.add_handler(CommandHandler("sold", sold_command))
|
||||
application.add_handler(CommandHandler("facturi", facturi_command))
|
||||
application.add_handler(CommandHandler("trezorerie", trezorerie_command))
|
||||
application.add_handler(CommandHandler("trezorerie_casa", trezorerie_casa_command))
|
||||
application.add_handler(CommandHandler("trezorerie_banca", trezorerie_banca_command))
|
||||
application.add_handler(CommandHandler("clienti", clienti_command))
|
||||
application.add_handler(CommandHandler("furnizori", furnizori_command))
|
||||
application.add_handler(CommandHandler("evolutie", evolutie_command))
|
||||
application.add_handler(CommandHandler("clearcache", clearcache_command))
|
||||
application.add_handler(CommandHandler("togglecache", togglecache_command))
|
||||
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_text_message))
|
||||
application.add_handler(CallbackQueryHandler(button_callback))
|
||||
application.add_error_handler(error_handler)
|
||||
|
||||
# Initialize and start
|
||||
await application.initialize()
|
||||
await application.start()
|
||||
await application.updater.start_polling(drop_pending_updates=True)
|
||||
|
||||
bot_info = await application.bot.get_me()
|
||||
logger.info(f"[TELEGRAM] ✅ Bot running: @{bot_info.username}")
|
||||
|
||||
# Keep bot running
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
logger.info("[TELEGRAM] Bot task cancelled, stopping...")
|
||||
if 'application' in locals():
|
||||
await application.updater.stop()
|
||||
await application.stop()
|
||||
await application.shutdown()
|
||||
logger.info("[TELEGRAM] ✅ Bot stopped")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"[TELEGRAM] ❌ Bot error: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# FASTAPI APPLICATION
|
||||
# ============================================================================
|
||||
|
||||
app = FastAPI(
|
||||
title="ROA2WEB Unified Backend",
|
||||
description="Unified FastAPI backend for Reports, Data Entry, and Telegram modules",
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# STARTUP/SHUTDOWN EVENT HANDLERS
|
||||
# ============================================================================
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
"""Application startup - Initialize all resources."""
|
||||
global telegram_bot_task
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info("[STARTUP] ROA2WEB Unified Backend")
|
||||
logger.info("=" * 80)
|
||||
|
||||
try:
|
||||
# Step 1: Initialize Oracle pool (shared by all modules)
|
||||
await init_oracle_pool()
|
||||
|
||||
# Step 2: Parallel initialization of module-specific resources
|
||||
logger.info("[STARTUP] Initializing module resources in parallel...")
|
||||
await asyncio.gather(
|
||||
init_reports_cache(),
|
||||
init_data_entry_db(),
|
||||
init_telegram_db(),
|
||||
)
|
||||
|
||||
# Step 3: Start PaddleOCR initialization in background thread
|
||||
import threading
|
||||
threading.Thread(target=init_paddle_ocr_background, daemon=True).start()
|
||||
|
||||
# Step 4: Start Telegram bot as background task
|
||||
if settings.telegram_bot_token:
|
||||
telegram_bot_task = asyncio.create_task(run_telegram_bot())
|
||||
logger.info("[STARTUP] ✅ Telegram bot task created")
|
||||
else:
|
||||
logger.warning("[STARTUP] ⚠️ TELEGRAM_BOT_TOKEN not set, bot disabled")
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info("[STARTUP] ✅ All modules initialized successfully")
|
||||
logger.info(f"[STARTUP] ✅ Server running on http://{settings.api_host}:{settings.api_port}")
|
||||
logger.info("=" * 80)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[STARTUP] ❌ Initialization failed: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown_event():
|
||||
"""Application shutdown - Cleanup resources."""
|
||||
global telegram_bot_task
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info("[SHUTDOWN] Stopping ROA2WEB Unified Backend...")
|
||||
logger.info("=" * 80)
|
||||
|
||||
try:
|
||||
# Stop Telegram bot
|
||||
if telegram_bot_task and not telegram_bot_task.done():
|
||||
logger.info("[SHUTDOWN] Stopping Telegram bot...")
|
||||
telegram_bot_task.cancel()
|
||||
try:
|
||||
await telegram_bot_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
# Stop Reports cache event monitor
|
||||
try:
|
||||
from backend.modules.reports.cache import close_cache, get_event_monitor
|
||||
monitor = get_event_monitor()
|
||||
if monitor:
|
||||
await monitor.stop()
|
||||
logger.info("[SHUTDOWN] Reports cache monitor stopped")
|
||||
|
||||
await close_cache()
|
||||
logger.info("[SHUTDOWN] Reports cache closed")
|
||||
except Exception as e:
|
||||
logger.error(f"[SHUTDOWN] Cache error: {e}")
|
||||
|
||||
# Close Oracle pool
|
||||
await oracle_pool.close_pool()
|
||||
logger.info("[SHUTDOWN] Oracle pool closed")
|
||||
|
||||
logger.info("=" * 80)
|
||||
logger.info("[SHUTDOWN] ✅ Shutdown complete")
|
||||
logger.info("=" * 80)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[SHUTDOWN] Error during shutdown: {e}", exc_info=True)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# MIDDLEWARE
|
||||
# ============================================================================
|
||||
|
||||
# CORS middleware
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # Allow all origins for production deployment
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Authentication middleware
|
||||
app.add_middleware(
|
||||
AuthenticationMiddleware,
|
||||
excluded_paths=[
|
||||
"/", "/docs", "/health", "/redoc", "/openapi.json",
|
||||
"/api/auth/login", "/api/auth/refresh",
|
||||
"/api/telegram/auth/verify-user",
|
||||
"/api/telegram/auth/verify-email",
|
||||
"/api/telegram/auth/login-with-email",
|
||||
"/api/telegram/auth/refresh-token",
|
||||
"/api/telegram/health"
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ROUTER REGISTRATION
|
||||
# ============================================================================
|
||||
|
||||
# Module routers with prefixes
|
||||
app.include_router(create_reports_router(), prefix="/api/reports", tags=["reports"])
|
||||
app.include_router(create_data_entry_router(), prefix="/api/data-entry", tags=["data-entry"])
|
||||
app.include_router(create_telegram_router(), prefix="/api/telegram", tags=["telegram"])
|
||||
|
||||
# Shared routers
|
||||
auth_router = create_auth_router(prefix="", tags=["authentication"])
|
||||
app.include_router(auth_router, prefix="/api/auth")
|
||||
|
||||
companies_router = create_companies_router(oracle_pool, tags=["companies"])
|
||||
app.include_router(companies_router, prefix="/api/companies")
|
||||
|
||||
calendar_router = create_calendar_router(oracle_pool, tags=["calendar"])
|
||||
app.include_router(calendar_router, prefix="/api/calendar")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ROOT & HEALTH ENDPOINTS
|
||||
# ============================================================================
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""Root endpoint - API information."""
|
||||
return {
|
||||
"name": settings.app_name,
|
||||
"version": settings.app_version,
|
||||
"status": "running",
|
||||
"modules": ["reports", "data-entry", "telegram"],
|
||||
"docs": "/docs",
|
||||
"health": "/health"
|
||||
}
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
"""Health check endpoint with module status."""
|
||||
health_status = {
|
||||
"api": "healthy",
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"modules": {}
|
||||
}
|
||||
|
||||
# Check Oracle connection
|
||||
try:
|
||||
async with oracle_pool.get_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute("SELECT 1 FROM DUAL")
|
||||
health_status["modules"]["oracle"] = "connected"
|
||||
except Exception as e:
|
||||
health_status["modules"]["oracle"] = f"error: {str(e)}"
|
||||
|
||||
# Check Reports cache
|
||||
try:
|
||||
from backend.modules.reports.cache import get_cache
|
||||
cache = get_cache()
|
||||
health_status["modules"]["reports_cache"] = "initialized" if cache else "disabled"
|
||||
except Exception as e:
|
||||
health_status["modules"]["reports_cache"] = f"error: {str(e)}"
|
||||
|
||||
# Check Data Entry DB
|
||||
try:
|
||||
db_path = Path(settings.data_entry_sqlite_database_path)
|
||||
health_status["modules"]["data_entry_db"] = "exists" if db_path.exists() else "missing"
|
||||
except Exception as e:
|
||||
health_status["modules"]["data_entry_db"] = f"error: {str(e)}"
|
||||
|
||||
# Check Telegram bot
|
||||
global telegram_bot_task
|
||||
if telegram_bot_task:
|
||||
if telegram_bot_task.done():
|
||||
health_status["modules"]["telegram_bot"] = "stopped"
|
||||
else:
|
||||
health_status["modules"]["telegram_bot"] = "running"
|
||||
else:
|
||||
health_status["modules"]["telegram_bot"] = "disabled"
|
||||
|
||||
return health_status
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# MAIN ENTRY POINT
|
||||
# ============================================================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(
|
||||
"backend.main:app",
|
||||
host=settings.api_host,
|
||||
port=settings.api_port,
|
||||
reload=False,
|
||||
log_level="info"
|
||||
)
|
||||
@@ -20,7 +20,7 @@ class Settings(BaseSettings):
|
||||
api_port: int = 8003
|
||||
|
||||
# SQLite Database
|
||||
sqlite_database_path: str = "data/receipts.db"
|
||||
sqlite_database_path: str = "data/receipts/receipts.db"
|
||||
|
||||
# File uploads
|
||||
upload_path: str = "data/uploads"
|
||||
@@ -6,8 +6,8 @@ from typing import Optional, List
|
||||
from sqlalchemy import select, delete
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db.models.accounting_entry import AccountingEntry, EntryType
|
||||
from app.schemas.receipt import AccountingEntryCreate, AccountingEntryUpdate
|
||||
from backend.modules.data_entry.db.models.accounting_entry import AccountingEntry, EntryType
|
||||
from backend.modules.data_entry.schemas.receipt import AccountingEntryCreate, AccountingEntryUpdate
|
||||
|
||||
|
||||
class AccountingEntryCRUD:
|
||||
@@ -11,8 +11,8 @@ from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from fastapi import UploadFile
|
||||
|
||||
from app.db.models.receipt import ReceiptAttachment
|
||||
from app.config import settings
|
||||
from backend.modules.data_entry.db.models.receipt import ReceiptAttachment
|
||||
from backend.modules.data_entry.config import settings
|
||||
|
||||
|
||||
class AttachmentCRUD:
|
||||
@@ -8,8 +8,8 @@ from sqlalchemy import select, func, or_
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.db.models.receipt import Receipt, ReceiptStatus
|
||||
from app.schemas.receipt import ReceiptCreate, ReceiptUpdate, ReceiptFilter
|
||||
from backend.modules.data_entry.db.models.receipt import Receipt, ReceiptStatus
|
||||
from backend.modules.data_entry.schemas.receipt import ReceiptCreate, ReceiptUpdate, ReceiptFilter
|
||||
|
||||
|
||||
def _serialize_tva_breakdown(tva_breakdown: Optional[List[Any]]) -> Optional[str]:
|
||||
@@ -6,7 +6,7 @@ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
from app.config import settings
|
||||
from backend.modules.data_entry.config import settings
|
||||
|
||||
|
||||
# Create async engine
|
||||
@@ -14,16 +14,16 @@ from sqlmodel import SQLModel
|
||||
load_dotenv()
|
||||
|
||||
# Import all models to ensure they're registered with SQLModel
|
||||
from app.db.models.receipt import Receipt, ReceiptAttachment
|
||||
from app.db.models.accounting_entry import AccountingEntry
|
||||
from app.db.models.nomenclature import SyncedSupplier, LocalSupplier, SyncedCashRegister
|
||||
from backend.modules.data_entry.db.models.receipt import Receipt, ReceiptAttachment
|
||||
from backend.modules.data_entry.db.models.accounting_entry import AccountingEntry
|
||||
from backend.modules.data_entry.db.models.nomenclature import SyncedSupplier, LocalSupplier, SyncedCashRegister
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Override sqlalchemy.url from environment variable if set
|
||||
db_path = os.getenv("SQLITE_DATABASE_PATH", "data/receipts.db")
|
||||
db_path = os.getenv("SQLITE_DATABASE_PATH", "data/receipts/receipts.db")
|
||||
config.set_main_option("sqlalchemy.url", f"sqlite:///{db_path}")
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
30
backend/modules/data_entry/routers/__init__.py
Normal file
30
backend/modules/data_entry/routers/__init__.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""Data Entry module router factory."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
|
||||
def create_data_entry_router() -> APIRouter:
|
||||
"""
|
||||
Create and configure Data Entry module router.
|
||||
|
||||
Includes all data entry endpoints:
|
||||
- /receipts - Receipt CRUD and workflow
|
||||
- /ocr - OCR processing for receipts
|
||||
- /nomenclature - Nomenclature syncing from Oracle
|
||||
|
||||
Returns:
|
||||
APIRouter: Configured router for data entry module
|
||||
"""
|
||||
router = APIRouter()
|
||||
|
||||
# Import routers here to avoid circular imports
|
||||
from .receipts import router as receipts_router
|
||||
from .ocr import router as ocr_router
|
||||
from .nomenclature import router as nomenclature_router
|
||||
|
||||
# Include all sub-routers (no prefix - already prefixed in main.py with /api/data-entry)
|
||||
router.include_router(receipts_router, prefix="/receipts", tags=["data-entry-receipts"])
|
||||
router.include_router(ocr_router, prefix="/ocr", tags=["data-entry-ocr"])
|
||||
router.include_router(nomenclature_router, prefix="/nomenclature", tags=["data-entry-nomenclature"])
|
||||
|
||||
return router
|
||||
@@ -5,17 +5,18 @@ from fastapi import APIRouter, Depends, HTTPException, Header
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.db.database import get_session
|
||||
from app.services.sync_service import SyncService
|
||||
from backend.modules.data_entry.db.database import get_session
|
||||
from backend.modules.data_entry.services.sync_service import SyncService
|
||||
|
||||
# Import auth dependencies
|
||||
import sys
|
||||
from pathlib import Path
|
||||
project_root = Path(__file__).parent.parent.parent.parent.parent
|
||||
sys.path.insert(0, str(project_root / "shared"))
|
||||
# Path setup handled by main.py - this is redundant
|
||||
# project_root = Path(__file__).parent.parent.parent.parent.parent
|
||||
# sys.path.insert(0, str(project_root / "shared"))
|
||||
|
||||
from auth.dependencies import get_current_user
|
||||
from auth.models import CurrentUser
|
||||
from shared.auth.dependencies import get_current_user
|
||||
from shared.auth.models import CurrentUser
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -7,15 +7,15 @@ from pathlib import Path
|
||||
from fastapi import APIRouter, HTTPException, UploadFile, File, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db.database import get_session
|
||||
from app.db.crud.attachment import AttachmentCRUD
|
||||
from app.services.ocr_service import ocr_service
|
||||
from app.services.ocr_engine import OCREngine
|
||||
from app.schemas.ocr import OCRResponse, OCRStatusResponse, ExtractionData, TvaEntry, PaymentMethod
|
||||
from backend.modules.data_entry.db.database import get_session
|
||||
from backend.modules.data_entry.db.crud.attachment import AttachmentCRUD
|
||||
from backend.modules.data_entry.services.ocr_service import ocr_service
|
||||
from backend.modules.data_entry.services.ocr_engine import OCREngine
|
||||
from backend.modules.data_entry.schemas.ocr import OCRResponse, OCRStatusResponse, ExtractionData, TvaEntry, PaymentMethod
|
||||
|
||||
# Auth integration (will be protected by middleware)
|
||||
from auth.dependencies import get_current_user
|
||||
from auth.models import CurrentUser
|
||||
from shared.auth.dependencies import get_current_user
|
||||
from shared.auth.models import CurrentUser
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -7,13 +7,13 @@ from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Query,
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db.database import get_session
|
||||
from app.db.crud.receipt import ReceiptCRUD
|
||||
from app.db.crud.attachment import AttachmentCRUD
|
||||
from app.db.crud.accounting_entry import AccountingEntryCRUD
|
||||
from app.services.receipt_service import ReceiptService
|
||||
from app.services.nomenclature_service import NomenclatureService
|
||||
from app.schemas.receipt import (
|
||||
from backend.modules.data_entry.db.database import get_session
|
||||
from backend.modules.data_entry.db.crud.receipt import ReceiptCRUD
|
||||
from backend.modules.data_entry.db.crud.attachment import AttachmentCRUD
|
||||
from backend.modules.data_entry.db.crud.accounting_entry import AccountingEntryCRUD
|
||||
from backend.modules.data_entry.services.receipt_service import ReceiptService
|
||||
from backend.modules.data_entry.services.nomenclature_service import NomenclatureService
|
||||
from backend.modules.data_entry.schemas.receipt import (
|
||||
ReceiptCreate,
|
||||
ReceiptUpdate,
|
||||
ReceiptResponse,
|
||||
@@ -29,11 +29,11 @@ from app.schemas.receipt import (
|
||||
CashRegisterOption,
|
||||
ExpenseTypeOption,
|
||||
)
|
||||
from app.db.models.receipt import ReceiptStatus, ReceiptDirection
|
||||
from backend.modules.data_entry.db.models.receipt import ReceiptStatus, ReceiptDirection
|
||||
|
||||
# Auth integration
|
||||
from auth.dependencies import get_current_user
|
||||
from auth.models import CurrentUser
|
||||
from shared.auth.dependencies import get_current_user
|
||||
from shared.auth.models import CurrentUser
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
@@ -6,8 +6,8 @@ from decimal import Decimal
|
||||
from typing import Optional, List, Any, Union
|
||||
from pydantic import BaseModel, Field, ConfigDict, field_validator
|
||||
|
||||
from app.db.models.receipt import ReceiptType, ReceiptDirection, ReceiptStatus
|
||||
from app.db.models.accounting_entry import EntryType
|
||||
from backend.modules.data_entry.db.models.receipt import ReceiptType, ReceiptDirection, ReceiptStatus
|
||||
from backend.modules.data_entry.db.models.accounting_entry import EntryType
|
||||
|
||||
|
||||
# ============ Accounting Entry Schemas ============
|
||||
@@ -6,14 +6,14 @@ from decimal import Decimal
|
||||
from sqlmodel import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.schemas.receipt import (
|
||||
from backend.modules.data_entry.schemas.receipt import (
|
||||
PartnerOption,
|
||||
AccountOption,
|
||||
CashRegisterOption,
|
||||
ExpenseTypeOption,
|
||||
)
|
||||
from app.services.expense_types import EXPENSE_TYPES
|
||||
from app.db.models.nomenclature import SyncedSupplier, LocalSupplier, SyncedCashRegister
|
||||
from backend.modules.data_entry.services.expense_types import EXPENSE_TYPES
|
||||
from backend.modules.data_entry.db.models.nomenclature import SyncedSupplier, LocalSupplier, SyncedCashRegister
|
||||
|
||||
|
||||
class NomenclatureService:
|
||||
@@ -14,9 +14,9 @@ from decimal import Decimal
|
||||
from pathlib import Path
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from app.services.ocr_engine import OCREngine
|
||||
from app.services.ocr_extractor import ReceiptExtractor, ExtractionResult
|
||||
from app.services.image_preprocessor import ImagePreprocessor
|
||||
from backend.modules.data_entry.services.ocr_engine import OCREngine
|
||||
from backend.modules.data_entry.services.ocr_extractor import ReceiptExtractor, ExtractionResult
|
||||
from backend.modules.data_entry.services.image_preprocessor import ImagePreprocessor
|
||||
|
||||
# Setup logging
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -5,11 +5,11 @@ from typing import List, Optional, Tuple
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db.models.receipt import Receipt, ReceiptStatus, ReceiptDirection
|
||||
from app.db.models.accounting_entry import EntryType
|
||||
from app.db.crud.receipt import ReceiptCRUD
|
||||
from app.db.crud.accounting_entry import AccountingEntryCRUD
|
||||
from app.schemas.receipt import (
|
||||
from backend.modules.data_entry.db.models.receipt import Receipt, ReceiptStatus, ReceiptDirection
|
||||
from backend.modules.data_entry.db.models.accounting_entry import EntryType
|
||||
from backend.modules.data_entry.db.crud.receipt import ReceiptCRUD
|
||||
from backend.modules.data_entry.db.crud.accounting_entry import AccountingEntryCRUD
|
||||
from backend.modules.data_entry.schemas.receipt import (
|
||||
ReceiptCreate,
|
||||
ReceiptUpdate,
|
||||
ReceiptFilter,
|
||||
@@ -17,7 +17,7 @@ from app.schemas.receipt import (
|
||||
ReceiptListResponse,
|
||||
AccountingEntryCreate,
|
||||
)
|
||||
from app.services.expense_types import EXPENSE_TYPES, get_expense_type
|
||||
from backend.modules.data_entry.services.expense_types import EXPENSE_TYPES, get_expense_type
|
||||
|
||||
|
||||
# Payment mode to accounting account mapping
|
||||
@@ -9,12 +9,12 @@ import logging
|
||||
from sqlmodel import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
# Add shared modules path
|
||||
project_root = Path(__file__).parent.parent.parent.parent.parent
|
||||
sys.path.insert(0, str(project_root / "shared"))
|
||||
# Path setup handled by main.py - this is redundant
|
||||
# project_root = Path(__file__).parent.parent.parent.parent.parent
|
||||
# sys.path.insert(0, str(project_root / "shared"))
|
||||
|
||||
from database.oracle_pool import oracle_pool
|
||||
from app.db.models.nomenclature import SyncedSupplier, LocalSupplier, SyncedCashRegister
|
||||
from shared.database.oracle_pool import oracle_pool
|
||||
from backend.modules.data_entry.db.models.nomenclature import SyncedSupplier, LocalSupplier, SyncedCashRegister
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -46,7 +46,7 @@ class CacheConfig:
|
||||
# Core Settings
|
||||
enabled=os.getenv('CACHE_ENABLED', 'True').lower() == 'true',
|
||||
cache_type=os.getenv('CACHE_TYPE', 'hybrid'),
|
||||
sqlite_path=os.getenv('CACHE_SQLITE_PATH', './cache_data/roa2web_cache.db'),
|
||||
sqlite_path=os.getenv('CACHE_SQLITE_PATH', './data/cache/roa2web_cache.db'),
|
||||
memory_max_size=int(os.getenv('CACHE_MEMORY_MAX_SIZE', '1000')),
|
||||
default_ttl=int(os.getenv('CACHE_DEFAULT_TTL', '900')),
|
||||
|
||||
@@ -9,8 +9,8 @@ import sys
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
# Add shared to path for Oracle pool access
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../..')))
|
||||
# Path setup handled by main.py - this is redundant but kept for module isolation
|
||||
# sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../..')))
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -69,7 +69,7 @@ class TrialBalanceResponse(BaseModel):
|
||||
data: dict = Field(description="Trial balance data with items, pagination, and filters")
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"success": True,
|
||||
"data": {
|
||||
36
backend/modules/reports/routers/__init__.py
Normal file
36
backend/modules/reports/routers/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""Reports module router factory."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
|
||||
def create_reports_router() -> APIRouter:
|
||||
"""
|
||||
Create and configure Reports module router.
|
||||
|
||||
Includes all report-related endpoints:
|
||||
- /invoices - Invoice management
|
||||
- /dashboard - Dashboard and metrics
|
||||
- /treasury - Treasury operations
|
||||
- /trial-balance - Trial balance reports
|
||||
- /cache - Cache management
|
||||
|
||||
Returns:
|
||||
APIRouter: Configured router for reports module
|
||||
"""
|
||||
router = APIRouter()
|
||||
|
||||
# Import routers here to avoid circular imports
|
||||
from .invoices import router as invoices_router
|
||||
from .dashboard import router as dashboard_router
|
||||
from .treasury import router as treasury_router
|
||||
from .trial_balance import router as trial_balance_router
|
||||
from .cache import router as cache_router
|
||||
|
||||
# Include all sub-routers (no prefix - already prefixed in main.py with /api/reports)
|
||||
router.include_router(invoices_router, prefix="/invoices", tags=["reports-invoices"])
|
||||
router.include_router(dashboard_router, prefix="/dashboard", tags=["reports-dashboard"])
|
||||
router.include_router(treasury_router, prefix="/treasury", tags=["reports-treasury"])
|
||||
router.include_router(trial_balance_router, prefix="/trial-balance", tags=["reports-trial-balance"])
|
||||
router.include_router(cache_router, prefix="/cache", tags=["reports-cache"])
|
||||
|
||||
return router
|
||||
@@ -4,15 +4,14 @@ API Router pentru managementul cache-ului
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, Dict, Any
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
from auth.dependencies import get_current_user
|
||||
from auth.models import CurrentUser
|
||||
from shared.auth.dependencies import get_current_user
|
||||
from shared.auth.models import CurrentUser
|
||||
from ..cache import get_cache, get_event_monitor, toggle_event_monitor
|
||||
|
||||
router = APIRouter(prefix="/cache", tags=["cache"])
|
||||
@@ -1,11 +1,10 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||
from typing import Optional
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
from auth.dependencies import get_current_user
|
||||
from auth.models import CurrentUser
|
||||
from shared.auth.dependencies import get_current_user
|
||||
from shared.auth.models import CurrentUser
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -4,12 +4,11 @@ API Router pentru facturi
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import List, Optional
|
||||
from datetime import date
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
from auth.dependencies import get_current_user, require_company_access
|
||||
from auth.models import CurrentUser
|
||||
from shared.auth.dependencies import get_current_user, require_company_access
|
||||
from shared.auth.models import CurrentUser
|
||||
from ..models.invoice import InvoiceFilter, InvoiceListResponse, InvoiceSummary
|
||||
from ..services.invoice_service import InvoiceService
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import Optional, List
|
||||
from datetime import date
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
from auth.dependencies import get_current_user
|
||||
from auth.models import CurrentUser
|
||||
from shared.auth.dependencies import get_current_user
|
||||
from shared.auth.models import CurrentUser
|
||||
from ..models.treasury import RegisterFilter, RegisterListResponse
|
||||
from ..services.treasury_service import TreasuryService
|
||||
|
||||
@@ -5,12 +5,11 @@ Refactored to use service layer with caching
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import Optional
|
||||
from datetime import date
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
from auth.dependencies import get_current_user
|
||||
from auth.models import CurrentUser
|
||||
from shared.auth.dependencies import get_current_user
|
||||
from shared.auth.models import CurrentUser
|
||||
from ..models.trial_balance import TrialBalanceResponse
|
||||
from ..services.trial_balance_service import TrialBalanceService
|
||||
import logging
|
||||
@@ -1,11 +1,10 @@
|
||||
"""
|
||||
Calendar service for fetching available accounting periods
|
||||
"""
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
from database.oracle_pool import oracle_pool
|
||||
from shared.database.oracle_pool import oracle_pool
|
||||
from ..models.calendar import CalendarPeriod, CalendarPeriodsResponse
|
||||
from ..cache.decorators import cached
|
||||
import logging
|
||||
@@ -1,8 +1,7 @@
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
from database.oracle_pool import oracle_pool
|
||||
from shared.database.oracle_pool import oracle_pool
|
||||
from ..models.dashboard import DashboardSummary, TreasuryAccount, TrendData
|
||||
from ..cache.decorators import cached
|
||||
from decimal import Decimal
|
||||
@@ -1,11 +1,10 @@
|
||||
"""
|
||||
Service pentru logica facturi - Portează query-urile din aplicația Flask
|
||||
"""
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
from database.oracle_pool import oracle_pool
|
||||
from shared.database.oracle_pool import oracle_pool
|
||||
from typing import List, Tuple
|
||||
from ..models.invoice import Invoice, InvoiceFilter, InvoiceListResponse, InvoiceSummary
|
||||
from ..cache.decorators import cached
|
||||
@@ -1,9 +1,8 @@
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
import oracledb
|
||||
from database.oracle_pool import oracle_pool
|
||||
from shared.database.oracle_pool import oracle_pool
|
||||
from ..models.treasury import BankCashRegister, RegisterFilter, RegisterListResponse, AccountingPeriod
|
||||
from ..cache.decorators import cached
|
||||
from decimal import Decimal
|
||||
@@ -2,11 +2,10 @@
|
||||
Service pentru Trial Balance (Balanță de Verificare) - Query VBAL VIEW
|
||||
Refactored to use caching system for optimal performance
|
||||
"""
|
||||
import sys
|
||||
# import sys # Removed - no longer needed
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
||||
|
||||
from database.oracle_pool import oracle_pool
|
||||
from shared.database.oracle_pool import oracle_pool
|
||||
from typing import Dict, Any
|
||||
from ..models.trial_balance import (
|
||||
TrialBalanceItem,
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user