From a604a3cc4454dfdff203260df4ab7b55e50162f7 Mon Sep 17 00:00:00 2001 From: Christian Date: Sat, 20 Jun 2026 03:29:51 +0200 Subject: [PATCH] Add comprehensive test suite for vTiger integration and ticket module - Implemented test scripts for vTiger account retrieval, contact data, and modules. - Created a detailed test suite for the ticket module, covering database schema validation, ticket number generation, prepaid card constraints, and service logic. - Added tests for vTiger field inspection and various queries related to accounts and sales orders. - Introduced SQL migration scripts for the ALSO Cloud Billing foundation, including import jobs, lines, and mapping tables with necessary constraints and indices. - Enhanced workflow columns in the import lines table to support matching and validation timestamps. --- .env.example | 13 + .env.prod.example | 14 + ... Copilot Instructions - BMC Webs.prompt.md | 0 .../BACKUP_RESTORE_TEST_PLAN.md | 0 .../DEPLOYMENT_CHECKLIST.md | 0 DEVELOPMENT.md => MDfile/DEVELOPMENT.md | 0 ...LOCATION_MODULE_IMPLEMENTATION_COMPLETE.md | 0 .../MIGRATION_GUIDE_v2.0.0.md | 0 .../NEXTCLOUD_MODULE_PLAN.md | 0 .../PHASE_3_TASK_3_1_COMPLETE.md | 0 .../PRODUCTION_DEPLOYMENT.md | 0 .../PRODUCTION_QUICK_START.md | 0 QUICK_UPDATE.md => MDfile/QUICK_UPDATE.md | 0 README.md => MDfile/README.md | 0 .../RELEASE_NOTES_v1.0.0.md | 0 .../RELEASE_NOTES_v1.3.5.md | 0 .../RELEASE_NOTES_v1.3.75.md | 0 .../RELEASE_NOTES_v1.3.76.md | 0 .../RELEASE_NOTES_v1.3.84.md | 0 .../RELEASE_NOTES_v2.0.0.md | 0 .../RELEASE_NOTES_v2.0.1.md | 0 .../RELEASE_NOTES_v2.0.2.md | 0 .../RELEASE_NOTES_v2.0.3.md | 0 .../RELEASE_NOTES_v2.0.4.md | 0 .../RELEASE_NOTES_v2.0.5.md | 0 .../RELEASE_NOTES_v2.0.6.md | 0 .../RELEASE_NOTES_v2.1.0.md | 0 .../RELEASE_NOTES_v2.1.1.md | 0 .../RELEASE_NOTES_v2.2.2.md | 0 .../RELEASE_NOTES_v2.2.3.md | 0 .../RELEASE_NOTES_v2.2.36.md | 0 .../RELEASE_NOTES_v2.2.39.md | 0 .../RELEASE_NOTES_v2.2.40.md | 0 .../RELEASE_NOTES_v2.2.41.md | 0 .../RELEASE_NOTES_v2.2.42.md | 0 .../RELEASE_NOTES_v2.2.43.md | 0 .../RELEASE_NOTES_v2.2.44.md | 0 .../RELEASE_NOTES_v2.2.45.md | 0 .../RELEASE_NOTES_v2.2.46.md | 0 .../RELEASE_NOTES_v2.2.47.md | 0 .../RELEASE_NOTES_v2.2.48.md | 0 .../RELEASE_NOTES_v2.2.49.md | 0 .../RELEASE_NOTES_v2.2.50.md | 0 .../RELEASE_NOTES_v2.2.51.md | 0 .../RELEASE_NOTES_v2.2.52.md | 0 .../RELEASE_NOTES_v2.2.53.md | 0 .../RELEASE_NOTES_v2.2.54.md | 0 .../RELEASE_NOTES_v2.2.56.md | 0 .../RELEASE_NOTES_v2.2.57.md | 0 .../RELEASE_NOTES_v2.2.58.md | 0 .../RELEASE_NOTES_v2.2.59.md | 0 .../RELEASE_NOTES_v2.2.60.md | 0 .../RELEASE_NOTES_v2.2.61.md | 0 .../RELEASE_NOTES_v2.2.62.md | 0 .../RELEASE_NOTES_v2.2.63.md | 0 .../RELEASE_NOTES_v2.2.64.md | 0 .../RELEASE_NOTES_v2.2.81.md | 0 .../RELEASE_NOTES_v2.2.82.md | 0 .../RELEASE_NOTES_v2.2.83.md | 0 .../RELEASE_NOTES_v2.2.84.md | 0 .../RELEASE_NOTES_v2.2.85.md | 0 .../RELEASE_NOTES_v2.2.86.md | 0 .../RELEASE_NOTES_v2.2.87.md | 0 .../RELEASE_NOTES_v2.3.1.md | 0 .../RELEASE_NOTES_v2.3.2.md | 0 .../RELEASE_NOTES_v2.3.3.md | 0 .../RELEASE_NOTES_v2.3.4.md | 0 .../RELEASE_NOTES_v2.3.5.md | 0 .../RELEASE_NOTES_v2.3.6.md | 0 .../RELEASE_NOTES_v2.3.7.md | 0 .../RELEASE_NOTES_v2.3.8.md | 0 .../RELEASE_NOTES_v2.3.md | 0 .../REMINDER_SYSTEM_IMPLEMENTATION.md | 0 .../REMINDER_SYSTEM_QUICKSTART.md | 0 .../SAG_MODULE_COMPLETION_REPORT.md | 0 .../SAG_MODULE_IMPLEMENTATION_PLAN.md | 0 .../SAG_MODULE_PLAN.md | 0 .../SALES_AND_AGGREGATION_PLAN.md | 0 .../SERVICE_CONTRACT_WIZARD_README.md | 0 .../TEMPLATES_FINAL_VERIFICATION.md | 0 .../TEMPLATES_QUICK_REFERENCE.md | 0 .../TEMPLATE_IMPLEMENTATION_SUMMARY.md | 0 app/core/config.py | 9 + app/customers/backend/router.py | 138 ++- app/customers/frontend/customer_detail.html | 505 +++++++++- fix_js2.py => app/modules/also/__init__.py | 0 app/modules/also/backend/__init__.py | 0 app/modules/also/backend/router.py | 114 +++ app/modules/also/backend/service.py | 907 ++++++++++++++++++ app/modules/also/models/__init__.py | 0 app/modules/also/models/schemas.py | 148 +++ app/modules/sag/frontend/views.py | 13 +- .env.bak => keepforlater/.env.bak | 0 .env.bak2 => keepforlater/.env.bak2 | 0 .../design_forslag_1_sidebar.html | 0 .../design_forslag_2_kompakt.html | 0 .../design_forslag_3_kort.html | 0 .../docker-compose.yml.bak2 | 0 .../docker-compose.yml.bak3 | 0 fix_cols.py => keepforlater/fix_cols.py | 0 fix_desc2.py => keepforlater/fix_desc2.py | 0 .../fix_domcontent.py | 0 .../fix_getTimeV1EmployeeId.py | 0 .../fix_hero_desc.py | 0 .../fix_hide_logic2.py | 0 keepforlater/fix_js2.py | 0 .../fix_relations_center.py | 0 fix_tab.py => keepforlater/fix_tab.py | 0 .../fix_telefoni_per_krag_numbers.sql | 0 .../fix_time_modal.py | 0 .../fix_timeline_clean.py | 0 .../fix_timeline_colors.py | 0 fix_top.py => keepforlater/fix_top.py | 0 .../forslag_kommentar.html | 0 get_js.py => keepforlater/get_js.py | 0 .../get_saveTime.py | 0 .../mine_3_anbefalinger.html | 0 old_js.txt => keepforlater/old_js.txt | 0 .../opgavebeskrivelse_mockup_3_forsog.html | 0 parse_html.py => keepforlater/parse_html.py | 0 parse_test.py => keepforlater/parse_test.py | 0 .../patch_backend_router.py | 0 patch_base.py => keepforlater/patch_base.py | 0 .../patch_base_html.py | 0 .../patch_bb_chat.py | 0 .../patch_bb_chat2.py | 0 .../patch_bb_chat3.py | 0 .../patch_bb_ver2.py | 0 .../patch_bottombar_router.py | 0 .../patch_chip_filter.py | 0 .../patch_detail.py | 0 .../patch_everything.py | 0 .../patch_frontend.py | 0 .../patch_frontend_cache.py | 0 .../patch_hw_help.py | 0 .../patch_js_events.py | 0 .../patch_js_events2.py | 0 .../patch_js_icons.py | 0 .../patch_js_rich_ui.py | 0 .../patch_lækker.py | 0 .../patch_mail_help.py | 0 .../patch_manual_backend.py | 0 .../patch_next_task.py | 0 .../patch_overview_logic.py | 0 .../patch_sag_help.py | 0 .../patch_time_form.py | 0 .../patch_time_modal.py | 0 patch_urls.py => keepforlater/patch_urls.py | 0 .../patch_views_fastapi.py | 0 .../print_saveTime.py | 0 .../refactor_detail.py | 0 .../rewrite_bottom_bar.py | 0 .../rewrite_detail.py | 0 run_rewrite.py => keepforlater/run_rewrite.py | 0 .../test_ai_analyze.py | 0 .../test_anydesk.py | 0 .../test_billed_field.py | 0 .../test_bulk_customer_update.py | 0 .../test_contact_relation.py | 0 .../test_cvr_filter.py | 0 .../test_location_module_qa.py | 0 test_manual.py => keepforlater/test_manual.py | 0 .../test_quick_analysis.py | 0 test_routes.py => keepforlater/test_routes.py | 0 .../test_sag_new_endpoints.sh | 0 .../test_sag_query.py | 0 .../test_service_contract_wizard.py | 0 test_sftp.py => keepforlater/test_sftp.py | 0 .../test_subscription_processing.py | 0 .../test_subscription_singular.py | 0 .../test_subscriptions.py | 0 .../test_ticket_module.py | 0 .../test_vtiger_account.py | 0 .../test_vtiger_accounts.py | 0 .../test_vtiger_contact.py | 0 .../test_vtiger_fields.py | 0 .../test_vtiger_modules.py | 0 .../test_wrapper.py | 0 .../test_wrapper2.py | 0 main.py | 2 + .../192_also_cloud_billing_foundation.sql | 108 +++ ...93_also_cloud_billing_workflow_columns.sql | 13 + 182 files changed, 1936 insertions(+), 48 deletions(-) rename # GitHub Copilot Instructions - BMC Webs.prompt.md => MDfile/# GitHub Copilot Instructions - BMC Webs.prompt.md (100%) rename BACKUP_RESTORE_TEST_PLAN.md => MDfile/BACKUP_RESTORE_TEST_PLAN.md (100%) rename DEPLOYMENT_CHECKLIST.md => MDfile/DEPLOYMENT_CHECKLIST.md (100%) rename DEVELOPMENT.md => MDfile/DEVELOPMENT.md (100%) rename LOCATION_MODULE_IMPLEMENTATION_COMPLETE.md => MDfile/LOCATION_MODULE_IMPLEMENTATION_COMPLETE.md (100%) rename MIGRATION_GUIDE_v2.0.0.md => MDfile/MIGRATION_GUIDE_v2.0.0.md (100%) rename NEXTCLOUD_MODULE_PLAN.md => MDfile/NEXTCLOUD_MODULE_PLAN.md (100%) rename PHASE_3_TASK_3_1_COMPLETE.md => MDfile/PHASE_3_TASK_3_1_COMPLETE.md (100%) rename PRODUCTION_DEPLOYMENT.md => MDfile/PRODUCTION_DEPLOYMENT.md (100%) rename PRODUCTION_QUICK_START.md => MDfile/PRODUCTION_QUICK_START.md (100%) rename QUICK_UPDATE.md => MDfile/QUICK_UPDATE.md (100%) rename README.md => MDfile/README.md (100%) rename RELEASE_NOTES_v1.0.0.md => MDfile/RELEASE_NOTES_v1.0.0.md (100%) rename RELEASE_NOTES_v1.3.5.md => MDfile/RELEASE_NOTES_v1.3.5.md (100%) rename RELEASE_NOTES_v1.3.75.md => MDfile/RELEASE_NOTES_v1.3.75.md (100%) rename RELEASE_NOTES_v1.3.76.md => MDfile/RELEASE_NOTES_v1.3.76.md (100%) rename RELEASE_NOTES_v1.3.84.md => MDfile/RELEASE_NOTES_v1.3.84.md (100%) rename RELEASE_NOTES_v2.0.0.md => MDfile/RELEASE_NOTES_v2.0.0.md (100%) rename RELEASE_NOTES_v2.0.1.md => MDfile/RELEASE_NOTES_v2.0.1.md (100%) rename RELEASE_NOTES_v2.0.2.md => MDfile/RELEASE_NOTES_v2.0.2.md (100%) rename RELEASE_NOTES_v2.0.3.md => MDfile/RELEASE_NOTES_v2.0.3.md (100%) rename RELEASE_NOTES_v2.0.4.md => MDfile/RELEASE_NOTES_v2.0.4.md (100%) rename RELEASE_NOTES_v2.0.5.md => MDfile/RELEASE_NOTES_v2.0.5.md (100%) rename RELEASE_NOTES_v2.0.6.md => MDfile/RELEASE_NOTES_v2.0.6.md (100%) rename RELEASE_NOTES_v2.1.0.md => MDfile/RELEASE_NOTES_v2.1.0.md (100%) rename RELEASE_NOTES_v2.1.1.md => MDfile/RELEASE_NOTES_v2.1.1.md (100%) rename RELEASE_NOTES_v2.2.2.md => MDfile/RELEASE_NOTES_v2.2.2.md (100%) rename RELEASE_NOTES_v2.2.3.md => MDfile/RELEASE_NOTES_v2.2.3.md (100%) rename RELEASE_NOTES_v2.2.36.md => MDfile/RELEASE_NOTES_v2.2.36.md (100%) rename RELEASE_NOTES_v2.2.39.md => MDfile/RELEASE_NOTES_v2.2.39.md (100%) rename RELEASE_NOTES_v2.2.40.md => MDfile/RELEASE_NOTES_v2.2.40.md (100%) rename RELEASE_NOTES_v2.2.41.md => MDfile/RELEASE_NOTES_v2.2.41.md (100%) rename RELEASE_NOTES_v2.2.42.md => MDfile/RELEASE_NOTES_v2.2.42.md (100%) rename RELEASE_NOTES_v2.2.43.md => MDfile/RELEASE_NOTES_v2.2.43.md (100%) rename RELEASE_NOTES_v2.2.44.md => MDfile/RELEASE_NOTES_v2.2.44.md (100%) rename RELEASE_NOTES_v2.2.45.md => MDfile/RELEASE_NOTES_v2.2.45.md (100%) rename RELEASE_NOTES_v2.2.46.md => MDfile/RELEASE_NOTES_v2.2.46.md (100%) rename RELEASE_NOTES_v2.2.47.md => MDfile/RELEASE_NOTES_v2.2.47.md (100%) rename RELEASE_NOTES_v2.2.48.md => MDfile/RELEASE_NOTES_v2.2.48.md (100%) rename RELEASE_NOTES_v2.2.49.md => MDfile/RELEASE_NOTES_v2.2.49.md (100%) rename RELEASE_NOTES_v2.2.50.md => MDfile/RELEASE_NOTES_v2.2.50.md (100%) rename RELEASE_NOTES_v2.2.51.md => MDfile/RELEASE_NOTES_v2.2.51.md (100%) rename RELEASE_NOTES_v2.2.52.md => MDfile/RELEASE_NOTES_v2.2.52.md (100%) rename RELEASE_NOTES_v2.2.53.md => MDfile/RELEASE_NOTES_v2.2.53.md (100%) rename RELEASE_NOTES_v2.2.54.md => MDfile/RELEASE_NOTES_v2.2.54.md (100%) rename RELEASE_NOTES_v2.2.56.md => MDfile/RELEASE_NOTES_v2.2.56.md (100%) rename RELEASE_NOTES_v2.2.57.md => MDfile/RELEASE_NOTES_v2.2.57.md (100%) rename RELEASE_NOTES_v2.2.58.md => MDfile/RELEASE_NOTES_v2.2.58.md (100%) rename RELEASE_NOTES_v2.2.59.md => MDfile/RELEASE_NOTES_v2.2.59.md (100%) rename RELEASE_NOTES_v2.2.60.md => MDfile/RELEASE_NOTES_v2.2.60.md (100%) rename RELEASE_NOTES_v2.2.61.md => MDfile/RELEASE_NOTES_v2.2.61.md (100%) rename RELEASE_NOTES_v2.2.62.md => MDfile/RELEASE_NOTES_v2.2.62.md (100%) rename RELEASE_NOTES_v2.2.63.md => MDfile/RELEASE_NOTES_v2.2.63.md (100%) rename RELEASE_NOTES_v2.2.64.md => MDfile/RELEASE_NOTES_v2.2.64.md (100%) rename RELEASE_NOTES_v2.2.81.md => MDfile/RELEASE_NOTES_v2.2.81.md (100%) rename RELEASE_NOTES_v2.2.82.md => MDfile/RELEASE_NOTES_v2.2.82.md (100%) rename RELEASE_NOTES_v2.2.83.md => MDfile/RELEASE_NOTES_v2.2.83.md (100%) rename RELEASE_NOTES_v2.2.84.md => MDfile/RELEASE_NOTES_v2.2.84.md (100%) rename RELEASE_NOTES_v2.2.85.md => MDfile/RELEASE_NOTES_v2.2.85.md (100%) rename RELEASE_NOTES_v2.2.86.md => MDfile/RELEASE_NOTES_v2.2.86.md (100%) rename RELEASE_NOTES_v2.2.87.md => MDfile/RELEASE_NOTES_v2.2.87.md (100%) rename RELEASE_NOTES_v2.3.1.md => MDfile/RELEASE_NOTES_v2.3.1.md (100%) rename RELEASE_NOTES_v2.3.2.md => MDfile/RELEASE_NOTES_v2.3.2.md (100%) rename RELEASE_NOTES_v2.3.3.md => MDfile/RELEASE_NOTES_v2.3.3.md (100%) rename RELEASE_NOTES_v2.3.4.md => MDfile/RELEASE_NOTES_v2.3.4.md (100%) rename RELEASE_NOTES_v2.3.5.md => MDfile/RELEASE_NOTES_v2.3.5.md (100%) rename RELEASE_NOTES_v2.3.6.md => MDfile/RELEASE_NOTES_v2.3.6.md (100%) rename RELEASE_NOTES_v2.3.7.md => MDfile/RELEASE_NOTES_v2.3.7.md (100%) rename RELEASE_NOTES_v2.3.8.md => MDfile/RELEASE_NOTES_v2.3.8.md (100%) rename RELEASE_NOTES_v2.3.md => MDfile/RELEASE_NOTES_v2.3.md (100%) rename REMINDER_SYSTEM_IMPLEMENTATION.md => MDfile/REMINDER_SYSTEM_IMPLEMENTATION.md (100%) rename REMINDER_SYSTEM_QUICKSTART.md => MDfile/REMINDER_SYSTEM_QUICKSTART.md (100%) rename SAG_MODULE_COMPLETION_REPORT.md => MDfile/SAG_MODULE_COMPLETION_REPORT.md (100%) rename SAG_MODULE_IMPLEMENTATION_PLAN.md => MDfile/SAG_MODULE_IMPLEMENTATION_PLAN.md (100%) rename SAG_MODULE_PLAN.md => MDfile/SAG_MODULE_PLAN.md (100%) rename SALES_AND_AGGREGATION_PLAN.md => MDfile/SALES_AND_AGGREGATION_PLAN.md (100%) rename SERVICE_CONTRACT_WIZARD_README.md => MDfile/SERVICE_CONTRACT_WIZARD_README.md (100%) rename TEMPLATES_FINAL_VERIFICATION.md => MDfile/TEMPLATES_FINAL_VERIFICATION.md (100%) rename TEMPLATES_QUICK_REFERENCE.md => MDfile/TEMPLATES_QUICK_REFERENCE.md (100%) rename TEMPLATE_IMPLEMENTATION_SUMMARY.md => MDfile/TEMPLATE_IMPLEMENTATION_SUMMARY.md (100%) rename fix_js2.py => app/modules/also/__init__.py (100%) create mode 100644 app/modules/also/backend/__init__.py create mode 100644 app/modules/also/backend/router.py create mode 100644 app/modules/also/backend/service.py create mode 100644 app/modules/also/models/__init__.py create mode 100644 app/modules/also/models/schemas.py rename .env.bak => keepforlater/.env.bak (100%) rename .env.bak2 => keepforlater/.env.bak2 (100%) rename design_forslag_1_sidebar.html => keepforlater/design_forslag_1_sidebar.html (100%) rename design_forslag_2_kompakt.html => keepforlater/design_forslag_2_kompakt.html (100%) rename design_forslag_3_kort.html => keepforlater/design_forslag_3_kort.html (100%) rename docker-compose.yml.bak2 => keepforlater/docker-compose.yml.bak2 (100%) rename docker-compose.yml.bak3 => keepforlater/docker-compose.yml.bak3 (100%) rename fix_cols.py => keepforlater/fix_cols.py (100%) rename fix_desc2.py => keepforlater/fix_desc2.py (100%) rename fix_domcontent.py => keepforlater/fix_domcontent.py (100%) rename fix_getTimeV1EmployeeId.py => keepforlater/fix_getTimeV1EmployeeId.py (100%) rename fix_hero_desc.py => keepforlater/fix_hero_desc.py (100%) rename fix_hide_logic2.py => keepforlater/fix_hide_logic2.py (100%) create mode 100644 keepforlater/fix_js2.py rename fix_relations_center.py => keepforlater/fix_relations_center.py (100%) rename fix_tab.py => keepforlater/fix_tab.py (100%) rename fix_telefoni_per_krag_numbers.sql => keepforlater/fix_telefoni_per_krag_numbers.sql (100%) rename fix_time_modal.py => keepforlater/fix_time_modal.py (100%) rename fix_timeline_clean.py => keepforlater/fix_timeline_clean.py (100%) rename fix_timeline_colors.py => keepforlater/fix_timeline_colors.py (100%) rename fix_top.py => keepforlater/fix_top.py (100%) rename forslag_kommentar.html => keepforlater/forslag_kommentar.html (100%) rename get_js.py => keepforlater/get_js.py (100%) rename get_saveTime.py => keepforlater/get_saveTime.py (100%) rename mine_3_anbefalinger.html => keepforlater/mine_3_anbefalinger.html (100%) rename old_js.txt => keepforlater/old_js.txt (100%) rename opgavebeskrivelse_mockup_3_forsog.html => keepforlater/opgavebeskrivelse_mockup_3_forsog.html (100%) rename parse_html.py => keepforlater/parse_html.py (100%) rename parse_test.py => keepforlater/parse_test.py (100%) rename patch_backend_router.py => keepforlater/patch_backend_router.py (100%) rename patch_base.py => keepforlater/patch_base.py (100%) rename patch_base_html.py => keepforlater/patch_base_html.py (100%) rename patch_bb_chat.py => keepforlater/patch_bb_chat.py (100%) rename patch_bb_chat2.py => keepforlater/patch_bb_chat2.py (100%) rename patch_bb_chat3.py => keepforlater/patch_bb_chat3.py (100%) rename patch_bb_ver2.py => keepforlater/patch_bb_ver2.py (100%) rename patch_bottombar_router.py => keepforlater/patch_bottombar_router.py (100%) rename patch_chip_filter.py => keepforlater/patch_chip_filter.py (100%) rename patch_detail.py => keepforlater/patch_detail.py (100%) rename patch_everything.py => keepforlater/patch_everything.py (100%) rename patch_frontend.py => keepforlater/patch_frontend.py (100%) rename patch_frontend_cache.py => keepforlater/patch_frontend_cache.py (100%) rename patch_hw_help.py => keepforlater/patch_hw_help.py (100%) rename patch_js_events.py => keepforlater/patch_js_events.py (100%) rename patch_js_events2.py => keepforlater/patch_js_events2.py (100%) rename patch_js_icons.py => keepforlater/patch_js_icons.py (100%) rename patch_js_rich_ui.py => keepforlater/patch_js_rich_ui.py (100%) rename patch_lækker.py => keepforlater/patch_lækker.py (100%) rename patch_mail_help.py => keepforlater/patch_mail_help.py (100%) rename patch_manual_backend.py => keepforlater/patch_manual_backend.py (100%) rename patch_next_task.py => keepforlater/patch_next_task.py (100%) rename patch_overview_logic.py => keepforlater/patch_overview_logic.py (100%) rename patch_sag_help.py => keepforlater/patch_sag_help.py (100%) rename patch_time_form.py => keepforlater/patch_time_form.py (100%) rename patch_time_modal.py => keepforlater/patch_time_modal.py (100%) rename patch_urls.py => keepforlater/patch_urls.py (100%) rename patch_views_fastapi.py => keepforlater/patch_views_fastapi.py (100%) rename print_saveTime.py => keepforlater/print_saveTime.py (100%) rename refactor_detail.py => keepforlater/refactor_detail.py (100%) rename rewrite_bottom_bar.py => keepforlater/rewrite_bottom_bar.py (100%) rename rewrite_detail.py => keepforlater/rewrite_detail.py (100%) rename run_rewrite.py => keepforlater/run_rewrite.py (100%) rename test_ai_analyze.py => keepforlater/test_ai_analyze.py (100%) rename test_anydesk.py => keepforlater/test_anydesk.py (100%) rename test_billed_field.py => keepforlater/test_billed_field.py (100%) rename test_bulk_customer_update.py => keepforlater/test_bulk_customer_update.py (100%) rename test_contact_relation.py => keepforlater/test_contact_relation.py (100%) rename test_cvr_filter.py => keepforlater/test_cvr_filter.py (100%) rename test_location_module_qa.py => keepforlater/test_location_module_qa.py (100%) rename test_manual.py => keepforlater/test_manual.py (100%) rename test_quick_analysis.py => keepforlater/test_quick_analysis.py (100%) rename test_routes.py => keepforlater/test_routes.py (100%) rename test_sag_new_endpoints.sh => keepforlater/test_sag_new_endpoints.sh (100%) rename test_sag_query.py => keepforlater/test_sag_query.py (100%) rename test_service_contract_wizard.py => keepforlater/test_service_contract_wizard.py (100%) rename test_sftp.py => keepforlater/test_sftp.py (100%) rename test_subscription_processing.py => keepforlater/test_subscription_processing.py (100%) rename test_subscription_singular.py => keepforlater/test_subscription_singular.py (100%) rename test_subscriptions.py => keepforlater/test_subscriptions.py (100%) rename test_ticket_module.py => keepforlater/test_ticket_module.py (100%) rename test_vtiger_account.py => keepforlater/test_vtiger_account.py (100%) rename test_vtiger_accounts.py => keepforlater/test_vtiger_accounts.py (100%) rename test_vtiger_contact.py => keepforlater/test_vtiger_contact.py (100%) rename test_vtiger_fields.py => keepforlater/test_vtiger_fields.py (100%) rename test_vtiger_modules.py => keepforlater/test_vtiger_modules.py (100%) rename test_wrapper.py => keepforlater/test_wrapper.py (100%) rename test_wrapper2.py => keepforlater/test_wrapper2.py (100%) create mode 100644 migrations/192_also_cloud_billing_foundation.sql create mode 100644 migrations/193_also_cloud_billing_workflow_columns.sql diff --git a/.env.example b/.env.example index 2d7b0c2..d8025d1 100644 --- a/.env.example +++ b/.env.example @@ -70,6 +70,19 @@ ECONOMIC_AGREEMENT_GRANT_TOKEN=your_agreement_grant_token_here ECONOMIC_READ_ONLY=true # Set to false ONLY after testing ECONOMIC_DRY_RUN=true # Set to false ONLY when ready for production writes +# ===================================================== +# ALSO Cloud Marketplace Integration (Optional) +# ===================================================== +ALSO_ENABLED=false +ALSO_API_BASE_URL= +ALSO_API_KEY= +ALSO_API_SECRET= +ALSO_TIMEOUT_SECONDS=20 + +# 🚨 SAFETY SWITCHES - Beskytter mod utilsigtede importer/sync +ALSO_READ_ONLY=true +ALSO_DRY_RUN=true + # ===================================================== # FedEx Integration (Optional) # ===================================================== diff --git a/.env.prod.example b/.env.prod.example index 0ee8ddf..cf3454e 100644 --- a/.env.prod.example +++ b/.env.prod.example @@ -87,6 +87,20 @@ ECONOMIC_AGREEMENT_GRANT_TOKEN=your_production_grant_here ECONOMIC_READ_ONLY=true ECONOMIC_DRY_RUN=true +# ===================================================== +# ALSO Cloud Marketplace Integration - Production (Optional) +# ===================================================== +ALSO_ENABLED=false +ALSO_API_BASE_URL= +ALSO_API_KEY= +ALSO_API_SECRET= +ALSO_TIMEOUT_SECONDS=20 + +# 🚨 SAFETY SWITCHES +# Start ALTID med begge sat til true i ny production deployment! +ALSO_READ_ONLY=true +ALSO_DRY_RUN=true + # ===================================================== # FedEx Integration - Production # ===================================================== diff --git a/# GitHub Copilot Instructions - BMC Webs.prompt.md b/MDfile/# GitHub Copilot Instructions - BMC Webs.prompt.md similarity index 100% rename from # GitHub Copilot Instructions - BMC Webs.prompt.md rename to MDfile/# GitHub Copilot Instructions - BMC Webs.prompt.md diff --git a/BACKUP_RESTORE_TEST_PLAN.md b/MDfile/BACKUP_RESTORE_TEST_PLAN.md similarity index 100% rename from BACKUP_RESTORE_TEST_PLAN.md rename to MDfile/BACKUP_RESTORE_TEST_PLAN.md diff --git a/DEPLOYMENT_CHECKLIST.md b/MDfile/DEPLOYMENT_CHECKLIST.md similarity index 100% rename from DEPLOYMENT_CHECKLIST.md rename to MDfile/DEPLOYMENT_CHECKLIST.md diff --git a/DEVELOPMENT.md b/MDfile/DEVELOPMENT.md similarity index 100% rename from DEVELOPMENT.md rename to MDfile/DEVELOPMENT.md diff --git a/LOCATION_MODULE_IMPLEMENTATION_COMPLETE.md b/MDfile/LOCATION_MODULE_IMPLEMENTATION_COMPLETE.md similarity index 100% rename from LOCATION_MODULE_IMPLEMENTATION_COMPLETE.md rename to MDfile/LOCATION_MODULE_IMPLEMENTATION_COMPLETE.md diff --git a/MIGRATION_GUIDE_v2.0.0.md b/MDfile/MIGRATION_GUIDE_v2.0.0.md similarity index 100% rename from MIGRATION_GUIDE_v2.0.0.md rename to MDfile/MIGRATION_GUIDE_v2.0.0.md diff --git a/NEXTCLOUD_MODULE_PLAN.md b/MDfile/NEXTCLOUD_MODULE_PLAN.md similarity index 100% rename from NEXTCLOUD_MODULE_PLAN.md rename to MDfile/NEXTCLOUD_MODULE_PLAN.md diff --git a/PHASE_3_TASK_3_1_COMPLETE.md b/MDfile/PHASE_3_TASK_3_1_COMPLETE.md similarity index 100% rename from PHASE_3_TASK_3_1_COMPLETE.md rename to MDfile/PHASE_3_TASK_3_1_COMPLETE.md diff --git a/PRODUCTION_DEPLOYMENT.md b/MDfile/PRODUCTION_DEPLOYMENT.md similarity index 100% rename from PRODUCTION_DEPLOYMENT.md rename to MDfile/PRODUCTION_DEPLOYMENT.md diff --git a/PRODUCTION_QUICK_START.md b/MDfile/PRODUCTION_QUICK_START.md similarity index 100% rename from PRODUCTION_QUICK_START.md rename to MDfile/PRODUCTION_QUICK_START.md diff --git a/QUICK_UPDATE.md b/MDfile/QUICK_UPDATE.md similarity index 100% rename from QUICK_UPDATE.md rename to MDfile/QUICK_UPDATE.md diff --git a/README.md b/MDfile/README.md similarity index 100% rename from README.md rename to MDfile/README.md diff --git a/RELEASE_NOTES_v1.0.0.md b/MDfile/RELEASE_NOTES_v1.0.0.md similarity index 100% rename from RELEASE_NOTES_v1.0.0.md rename to MDfile/RELEASE_NOTES_v1.0.0.md diff --git a/RELEASE_NOTES_v1.3.5.md b/MDfile/RELEASE_NOTES_v1.3.5.md similarity index 100% rename from RELEASE_NOTES_v1.3.5.md rename to MDfile/RELEASE_NOTES_v1.3.5.md diff --git a/RELEASE_NOTES_v1.3.75.md b/MDfile/RELEASE_NOTES_v1.3.75.md similarity index 100% rename from RELEASE_NOTES_v1.3.75.md rename to MDfile/RELEASE_NOTES_v1.3.75.md diff --git a/RELEASE_NOTES_v1.3.76.md b/MDfile/RELEASE_NOTES_v1.3.76.md similarity index 100% rename from RELEASE_NOTES_v1.3.76.md rename to MDfile/RELEASE_NOTES_v1.3.76.md diff --git a/RELEASE_NOTES_v1.3.84.md b/MDfile/RELEASE_NOTES_v1.3.84.md similarity index 100% rename from RELEASE_NOTES_v1.3.84.md rename to MDfile/RELEASE_NOTES_v1.3.84.md diff --git a/RELEASE_NOTES_v2.0.0.md b/MDfile/RELEASE_NOTES_v2.0.0.md similarity index 100% rename from RELEASE_NOTES_v2.0.0.md rename to MDfile/RELEASE_NOTES_v2.0.0.md diff --git a/RELEASE_NOTES_v2.0.1.md b/MDfile/RELEASE_NOTES_v2.0.1.md similarity index 100% rename from RELEASE_NOTES_v2.0.1.md rename to MDfile/RELEASE_NOTES_v2.0.1.md diff --git a/RELEASE_NOTES_v2.0.2.md b/MDfile/RELEASE_NOTES_v2.0.2.md similarity index 100% rename from RELEASE_NOTES_v2.0.2.md rename to MDfile/RELEASE_NOTES_v2.0.2.md diff --git a/RELEASE_NOTES_v2.0.3.md b/MDfile/RELEASE_NOTES_v2.0.3.md similarity index 100% rename from RELEASE_NOTES_v2.0.3.md rename to MDfile/RELEASE_NOTES_v2.0.3.md diff --git a/RELEASE_NOTES_v2.0.4.md b/MDfile/RELEASE_NOTES_v2.0.4.md similarity index 100% rename from RELEASE_NOTES_v2.0.4.md rename to MDfile/RELEASE_NOTES_v2.0.4.md diff --git a/RELEASE_NOTES_v2.0.5.md b/MDfile/RELEASE_NOTES_v2.0.5.md similarity index 100% rename from RELEASE_NOTES_v2.0.5.md rename to MDfile/RELEASE_NOTES_v2.0.5.md diff --git a/RELEASE_NOTES_v2.0.6.md b/MDfile/RELEASE_NOTES_v2.0.6.md similarity index 100% rename from RELEASE_NOTES_v2.0.6.md rename to MDfile/RELEASE_NOTES_v2.0.6.md diff --git a/RELEASE_NOTES_v2.1.0.md b/MDfile/RELEASE_NOTES_v2.1.0.md similarity index 100% rename from RELEASE_NOTES_v2.1.0.md rename to MDfile/RELEASE_NOTES_v2.1.0.md diff --git a/RELEASE_NOTES_v2.1.1.md b/MDfile/RELEASE_NOTES_v2.1.1.md similarity index 100% rename from RELEASE_NOTES_v2.1.1.md rename to MDfile/RELEASE_NOTES_v2.1.1.md diff --git a/RELEASE_NOTES_v2.2.2.md b/MDfile/RELEASE_NOTES_v2.2.2.md similarity index 100% rename from RELEASE_NOTES_v2.2.2.md rename to MDfile/RELEASE_NOTES_v2.2.2.md diff --git a/RELEASE_NOTES_v2.2.3.md b/MDfile/RELEASE_NOTES_v2.2.3.md similarity index 100% rename from RELEASE_NOTES_v2.2.3.md rename to MDfile/RELEASE_NOTES_v2.2.3.md diff --git a/RELEASE_NOTES_v2.2.36.md b/MDfile/RELEASE_NOTES_v2.2.36.md similarity index 100% rename from RELEASE_NOTES_v2.2.36.md rename to MDfile/RELEASE_NOTES_v2.2.36.md diff --git a/RELEASE_NOTES_v2.2.39.md b/MDfile/RELEASE_NOTES_v2.2.39.md similarity index 100% rename from RELEASE_NOTES_v2.2.39.md rename to MDfile/RELEASE_NOTES_v2.2.39.md diff --git a/RELEASE_NOTES_v2.2.40.md b/MDfile/RELEASE_NOTES_v2.2.40.md similarity index 100% rename from RELEASE_NOTES_v2.2.40.md rename to MDfile/RELEASE_NOTES_v2.2.40.md diff --git a/RELEASE_NOTES_v2.2.41.md b/MDfile/RELEASE_NOTES_v2.2.41.md similarity index 100% rename from RELEASE_NOTES_v2.2.41.md rename to MDfile/RELEASE_NOTES_v2.2.41.md diff --git a/RELEASE_NOTES_v2.2.42.md b/MDfile/RELEASE_NOTES_v2.2.42.md similarity index 100% rename from RELEASE_NOTES_v2.2.42.md rename to MDfile/RELEASE_NOTES_v2.2.42.md diff --git a/RELEASE_NOTES_v2.2.43.md b/MDfile/RELEASE_NOTES_v2.2.43.md similarity index 100% rename from RELEASE_NOTES_v2.2.43.md rename to MDfile/RELEASE_NOTES_v2.2.43.md diff --git a/RELEASE_NOTES_v2.2.44.md b/MDfile/RELEASE_NOTES_v2.2.44.md similarity index 100% rename from RELEASE_NOTES_v2.2.44.md rename to MDfile/RELEASE_NOTES_v2.2.44.md diff --git a/RELEASE_NOTES_v2.2.45.md b/MDfile/RELEASE_NOTES_v2.2.45.md similarity index 100% rename from RELEASE_NOTES_v2.2.45.md rename to MDfile/RELEASE_NOTES_v2.2.45.md diff --git a/RELEASE_NOTES_v2.2.46.md b/MDfile/RELEASE_NOTES_v2.2.46.md similarity index 100% rename from RELEASE_NOTES_v2.2.46.md rename to MDfile/RELEASE_NOTES_v2.2.46.md diff --git a/RELEASE_NOTES_v2.2.47.md b/MDfile/RELEASE_NOTES_v2.2.47.md similarity index 100% rename from RELEASE_NOTES_v2.2.47.md rename to MDfile/RELEASE_NOTES_v2.2.47.md diff --git a/RELEASE_NOTES_v2.2.48.md b/MDfile/RELEASE_NOTES_v2.2.48.md similarity index 100% rename from RELEASE_NOTES_v2.2.48.md rename to MDfile/RELEASE_NOTES_v2.2.48.md diff --git a/RELEASE_NOTES_v2.2.49.md b/MDfile/RELEASE_NOTES_v2.2.49.md similarity index 100% rename from RELEASE_NOTES_v2.2.49.md rename to MDfile/RELEASE_NOTES_v2.2.49.md diff --git a/RELEASE_NOTES_v2.2.50.md b/MDfile/RELEASE_NOTES_v2.2.50.md similarity index 100% rename from RELEASE_NOTES_v2.2.50.md rename to MDfile/RELEASE_NOTES_v2.2.50.md diff --git a/RELEASE_NOTES_v2.2.51.md b/MDfile/RELEASE_NOTES_v2.2.51.md similarity index 100% rename from RELEASE_NOTES_v2.2.51.md rename to MDfile/RELEASE_NOTES_v2.2.51.md diff --git a/RELEASE_NOTES_v2.2.52.md b/MDfile/RELEASE_NOTES_v2.2.52.md similarity index 100% rename from RELEASE_NOTES_v2.2.52.md rename to MDfile/RELEASE_NOTES_v2.2.52.md diff --git a/RELEASE_NOTES_v2.2.53.md b/MDfile/RELEASE_NOTES_v2.2.53.md similarity index 100% rename from RELEASE_NOTES_v2.2.53.md rename to MDfile/RELEASE_NOTES_v2.2.53.md diff --git a/RELEASE_NOTES_v2.2.54.md b/MDfile/RELEASE_NOTES_v2.2.54.md similarity index 100% rename from RELEASE_NOTES_v2.2.54.md rename to MDfile/RELEASE_NOTES_v2.2.54.md diff --git a/RELEASE_NOTES_v2.2.56.md b/MDfile/RELEASE_NOTES_v2.2.56.md similarity index 100% rename from RELEASE_NOTES_v2.2.56.md rename to MDfile/RELEASE_NOTES_v2.2.56.md diff --git a/RELEASE_NOTES_v2.2.57.md b/MDfile/RELEASE_NOTES_v2.2.57.md similarity index 100% rename from RELEASE_NOTES_v2.2.57.md rename to MDfile/RELEASE_NOTES_v2.2.57.md diff --git a/RELEASE_NOTES_v2.2.58.md b/MDfile/RELEASE_NOTES_v2.2.58.md similarity index 100% rename from RELEASE_NOTES_v2.2.58.md rename to MDfile/RELEASE_NOTES_v2.2.58.md diff --git a/RELEASE_NOTES_v2.2.59.md b/MDfile/RELEASE_NOTES_v2.2.59.md similarity index 100% rename from RELEASE_NOTES_v2.2.59.md rename to MDfile/RELEASE_NOTES_v2.2.59.md diff --git a/RELEASE_NOTES_v2.2.60.md b/MDfile/RELEASE_NOTES_v2.2.60.md similarity index 100% rename from RELEASE_NOTES_v2.2.60.md rename to MDfile/RELEASE_NOTES_v2.2.60.md diff --git a/RELEASE_NOTES_v2.2.61.md b/MDfile/RELEASE_NOTES_v2.2.61.md similarity index 100% rename from RELEASE_NOTES_v2.2.61.md rename to MDfile/RELEASE_NOTES_v2.2.61.md diff --git a/RELEASE_NOTES_v2.2.62.md b/MDfile/RELEASE_NOTES_v2.2.62.md similarity index 100% rename from RELEASE_NOTES_v2.2.62.md rename to MDfile/RELEASE_NOTES_v2.2.62.md diff --git a/RELEASE_NOTES_v2.2.63.md b/MDfile/RELEASE_NOTES_v2.2.63.md similarity index 100% rename from RELEASE_NOTES_v2.2.63.md rename to MDfile/RELEASE_NOTES_v2.2.63.md diff --git a/RELEASE_NOTES_v2.2.64.md b/MDfile/RELEASE_NOTES_v2.2.64.md similarity index 100% rename from RELEASE_NOTES_v2.2.64.md rename to MDfile/RELEASE_NOTES_v2.2.64.md diff --git a/RELEASE_NOTES_v2.2.81.md b/MDfile/RELEASE_NOTES_v2.2.81.md similarity index 100% rename from RELEASE_NOTES_v2.2.81.md rename to MDfile/RELEASE_NOTES_v2.2.81.md diff --git a/RELEASE_NOTES_v2.2.82.md b/MDfile/RELEASE_NOTES_v2.2.82.md similarity index 100% rename from RELEASE_NOTES_v2.2.82.md rename to MDfile/RELEASE_NOTES_v2.2.82.md diff --git a/RELEASE_NOTES_v2.2.83.md b/MDfile/RELEASE_NOTES_v2.2.83.md similarity index 100% rename from RELEASE_NOTES_v2.2.83.md rename to MDfile/RELEASE_NOTES_v2.2.83.md diff --git a/RELEASE_NOTES_v2.2.84.md b/MDfile/RELEASE_NOTES_v2.2.84.md similarity index 100% rename from RELEASE_NOTES_v2.2.84.md rename to MDfile/RELEASE_NOTES_v2.2.84.md diff --git a/RELEASE_NOTES_v2.2.85.md b/MDfile/RELEASE_NOTES_v2.2.85.md similarity index 100% rename from RELEASE_NOTES_v2.2.85.md rename to MDfile/RELEASE_NOTES_v2.2.85.md diff --git a/RELEASE_NOTES_v2.2.86.md b/MDfile/RELEASE_NOTES_v2.2.86.md similarity index 100% rename from RELEASE_NOTES_v2.2.86.md rename to MDfile/RELEASE_NOTES_v2.2.86.md diff --git a/RELEASE_NOTES_v2.2.87.md b/MDfile/RELEASE_NOTES_v2.2.87.md similarity index 100% rename from RELEASE_NOTES_v2.2.87.md rename to MDfile/RELEASE_NOTES_v2.2.87.md diff --git a/RELEASE_NOTES_v2.3.1.md b/MDfile/RELEASE_NOTES_v2.3.1.md similarity index 100% rename from RELEASE_NOTES_v2.3.1.md rename to MDfile/RELEASE_NOTES_v2.3.1.md diff --git a/RELEASE_NOTES_v2.3.2.md b/MDfile/RELEASE_NOTES_v2.3.2.md similarity index 100% rename from RELEASE_NOTES_v2.3.2.md rename to MDfile/RELEASE_NOTES_v2.3.2.md diff --git a/RELEASE_NOTES_v2.3.3.md b/MDfile/RELEASE_NOTES_v2.3.3.md similarity index 100% rename from RELEASE_NOTES_v2.3.3.md rename to MDfile/RELEASE_NOTES_v2.3.3.md diff --git a/RELEASE_NOTES_v2.3.4.md b/MDfile/RELEASE_NOTES_v2.3.4.md similarity index 100% rename from RELEASE_NOTES_v2.3.4.md rename to MDfile/RELEASE_NOTES_v2.3.4.md diff --git a/RELEASE_NOTES_v2.3.5.md b/MDfile/RELEASE_NOTES_v2.3.5.md similarity index 100% rename from RELEASE_NOTES_v2.3.5.md rename to MDfile/RELEASE_NOTES_v2.3.5.md diff --git a/RELEASE_NOTES_v2.3.6.md b/MDfile/RELEASE_NOTES_v2.3.6.md similarity index 100% rename from RELEASE_NOTES_v2.3.6.md rename to MDfile/RELEASE_NOTES_v2.3.6.md diff --git a/RELEASE_NOTES_v2.3.7.md b/MDfile/RELEASE_NOTES_v2.3.7.md similarity index 100% rename from RELEASE_NOTES_v2.3.7.md rename to MDfile/RELEASE_NOTES_v2.3.7.md diff --git a/RELEASE_NOTES_v2.3.8.md b/MDfile/RELEASE_NOTES_v2.3.8.md similarity index 100% rename from RELEASE_NOTES_v2.3.8.md rename to MDfile/RELEASE_NOTES_v2.3.8.md diff --git a/RELEASE_NOTES_v2.3.md b/MDfile/RELEASE_NOTES_v2.3.md similarity index 100% rename from RELEASE_NOTES_v2.3.md rename to MDfile/RELEASE_NOTES_v2.3.md diff --git a/REMINDER_SYSTEM_IMPLEMENTATION.md b/MDfile/REMINDER_SYSTEM_IMPLEMENTATION.md similarity index 100% rename from REMINDER_SYSTEM_IMPLEMENTATION.md rename to MDfile/REMINDER_SYSTEM_IMPLEMENTATION.md diff --git a/REMINDER_SYSTEM_QUICKSTART.md b/MDfile/REMINDER_SYSTEM_QUICKSTART.md similarity index 100% rename from REMINDER_SYSTEM_QUICKSTART.md rename to MDfile/REMINDER_SYSTEM_QUICKSTART.md diff --git a/SAG_MODULE_COMPLETION_REPORT.md b/MDfile/SAG_MODULE_COMPLETION_REPORT.md similarity index 100% rename from SAG_MODULE_COMPLETION_REPORT.md rename to MDfile/SAG_MODULE_COMPLETION_REPORT.md diff --git a/SAG_MODULE_IMPLEMENTATION_PLAN.md b/MDfile/SAG_MODULE_IMPLEMENTATION_PLAN.md similarity index 100% rename from SAG_MODULE_IMPLEMENTATION_PLAN.md rename to MDfile/SAG_MODULE_IMPLEMENTATION_PLAN.md diff --git a/SAG_MODULE_PLAN.md b/MDfile/SAG_MODULE_PLAN.md similarity index 100% rename from SAG_MODULE_PLAN.md rename to MDfile/SAG_MODULE_PLAN.md diff --git a/SALES_AND_AGGREGATION_PLAN.md b/MDfile/SALES_AND_AGGREGATION_PLAN.md similarity index 100% rename from SALES_AND_AGGREGATION_PLAN.md rename to MDfile/SALES_AND_AGGREGATION_PLAN.md diff --git a/SERVICE_CONTRACT_WIZARD_README.md b/MDfile/SERVICE_CONTRACT_WIZARD_README.md similarity index 100% rename from SERVICE_CONTRACT_WIZARD_README.md rename to MDfile/SERVICE_CONTRACT_WIZARD_README.md diff --git a/TEMPLATES_FINAL_VERIFICATION.md b/MDfile/TEMPLATES_FINAL_VERIFICATION.md similarity index 100% rename from TEMPLATES_FINAL_VERIFICATION.md rename to MDfile/TEMPLATES_FINAL_VERIFICATION.md diff --git a/TEMPLATES_QUICK_REFERENCE.md b/MDfile/TEMPLATES_QUICK_REFERENCE.md similarity index 100% rename from TEMPLATES_QUICK_REFERENCE.md rename to MDfile/TEMPLATES_QUICK_REFERENCE.md diff --git a/TEMPLATE_IMPLEMENTATION_SUMMARY.md b/MDfile/TEMPLATE_IMPLEMENTATION_SUMMARY.md similarity index 100% rename from TEMPLATE_IMPLEMENTATION_SUMMARY.md rename to MDfile/TEMPLATE_IMPLEMENTATION_SUMMARY.md diff --git a/app/core/config.py b/app/core/config.py index 9752e4c..cd5c27b 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -315,6 +315,15 @@ class Settings(BaseSettings): FEDEX_BASE_URL: str = "" FEDEX_TIMEOUT_SECONDS: int = 20 + # ALSO Cloud Marketplace Integration + ALSO_ENABLED: bool = False + ALSO_READ_ONLY: bool = True + ALSO_DRY_RUN: bool = True + ALSO_API_BASE_URL: str = "" + ALSO_API_KEY: str = "" + ALSO_API_SECRET: str = "" + ALSO_TIMEOUT_SECONDS: int = 20 + # Bottom bar module BOTTOM_BAR_ENABLED: bool = False diff --git a/app/customers/backend/router.py b/app/customers/backend/router.py index 6365428..67f2018 100644 --- a/app/customers/backend/router.py +++ b/app/customers/backend/router.py @@ -5,7 +5,7 @@ Adapted from OmniSync for BMC Hub """ from fastapi import APIRouter, HTTPException, Query -from typing import List, Optional, Dict +from typing import List, Optional, Dict, Any from pydantic import BaseModel import logging import asyncio @@ -1780,3 +1780,139 @@ async def get_subscription_billing_matrix( logger.error(f"❌ Error generating billing matrix: {e}") raise HTTPException(status_code=500, detail=str(e)) + +@router.get("/customers/{customer_id}/acmp") +async def get_customer_acmp_overview( + customer_id: int, + months: int = Query(default=6, ge=1, le=24, description="Months for trend data"), +): + """Return ACMP (ALSO Cloud Marketplace) detail and statistics for one customer.""" + try: + customer = execute_query_single("SELECT id, name FROM customers WHERE id = %s", (customer_id,)) + if not customer: + raise HTTPException(status_code=404, detail=f"Customer {customer_id} not found") + + table_check = execute_query_single("SELECT to_regclass('public.also_import_lines') AS table_name") + if not table_check or not table_check.get("table_name"): + return { + "customer_id": customer_id, + "customer_name": customer.get("name"), + "available": False, + "message": "ACMP data is not available yet (missing also_import_lines table)", + "summary": {}, + "status_breakdown": [], + "products": [], + "monthly": [], + } + + summary = execute_query_single( + """ + WITH scoped AS ( + SELECT * + FROM also_import_lines + WHERE matched_customer_id = %s + ) + SELECT + COUNT(*) AS line_count, + COUNT(DISTINCT COALESCE(material_number, product_name, 'ukendt')) AS distinct_products, + COALESCE(SUM(COALESCE(total_price, sales_price, 0)), 0) AS revenue_total, + COALESCE(SUM(COALESCE(cost_amount, 0)), 0) AS cost_total, + COALESCE(SUM(COALESCE(total_price, sales_price, 0) - COALESCE(cost_amount, 0)), 0) AS margin_total, + COUNT(*) FILTER (WHERE queue_status = 'ready_for_approval') AS pending_approvals, + COUNT(*) FILTER (WHERE queue_status = 'error') AS error_count, + COALESCE( + SUM(COALESCE(total_price, sales_price, 0)) FILTER ( + WHERE date_trunc('month', COALESCE(billing_start::timestamp, created_at)) = date_trunc('month', CURRENT_DATE) + ), + 0 + ) AS revenue_month, + COALESCE( + SUM(COALESCE(cost_amount, 0)) FILTER ( + WHERE date_trunc('month', COALESCE(billing_start::timestamp, created_at)) = date_trunc('month', CURRENT_DATE) + ), + 0 + ) AS cost_month + FROM scoped + """, + (customer_id,), + ) or {} + + status_breakdown = execute_query( + """ + SELECT queue_status, COUNT(*)::INTEGER AS count + FROM also_import_lines + WHERE matched_customer_id = %s + GROUP BY queue_status + ORDER BY count DESC, queue_status ASC + """, + (customer_id,), + ) or [] + + products = execute_query( + """ + SELECT + COALESCE(material_number, 'N/A') AS material_number, + COALESCE(vendor, 'N/A') AS vendor, + COALESCE(product_name, 'Ukendt produkt') AS product_name, + COUNT(*)::INTEGER AS line_count, + COALESCE(SUM(COALESCE(billable_parameters, 1)), 0) AS quantity_total, + COALESCE(SUM(COALESCE(total_price, sales_price, 0)), 0) AS revenue_total, + COALESCE(SUM(COALESCE(cost_amount, 0)), 0) AS cost_total, + COALESCE(SUM(COALESCE(total_price, sales_price, 0) - COALESCE(cost_amount, 0)), 0) AS margin_total, + MAX(billing_start) AS last_billing_start, + MAX(created_at) AS last_seen_at + FROM also_import_lines + WHERE matched_customer_id = %s + GROUP BY COALESCE(material_number, 'N/A'), COALESCE(vendor, 'N/A'), COALESCE(product_name, 'Ukendt produkt') + ORDER BY revenue_total DESC, quantity_total DESC + LIMIT 200 + """, + (customer_id,), + ) or [] + + monthly = execute_query( + """ + SELECT + TO_CHAR(date_trunc('month', COALESCE(billing_start::timestamp, created_at)), 'YYYY-MM') AS month, + COALESCE(SUM(COALESCE(billable_parameters, 1)), 0) AS quantity_total, + COALESCE(SUM(COALESCE(total_price, sales_price, 0)), 0) AS revenue_total, + COALESCE(SUM(COALESCE(cost_amount, 0)), 0) AS cost_total, + COALESCE(SUM(COALESCE(total_price, sales_price, 0) - COALESCE(cost_amount, 0)), 0) AS margin_total, + COUNT(*)::INTEGER AS lines + FROM also_import_lines + WHERE matched_customer_id = %s + AND date_trunc('month', COALESCE(billing_start::timestamp, created_at)) >= date_trunc('month', CURRENT_DATE) - ((%s - 1) * INTERVAL '1 month') + GROUP BY date_trunc('month', COALESCE(billing_start::timestamp, created_at)) + ORDER BY month ASC + """, + (customer_id, months), + ) or [] + + response: Dict[str, Any] = { + "customer_id": customer_id, + "customer_name": customer.get("name"), + "available": True, + "summary": { + "line_count": int(summary.get("line_count") or 0), + "distinct_products": int(summary.get("distinct_products") or 0), + "revenue_total": summary.get("revenue_total") or 0, + "cost_total": summary.get("cost_total") or 0, + "margin_total": summary.get("margin_total") or 0, + "revenue_month": summary.get("revenue_month") or 0, + "cost_month": summary.get("cost_month") or 0, + "margin_month": (summary.get("revenue_month") or 0) - (summary.get("cost_month") or 0), + "pending_approvals": int(summary.get("pending_approvals") or 0), + "error_count": int(summary.get("error_count") or 0), + }, + "status_breakdown": status_breakdown, + "products": products, + "monthly": monthly, + } + return response + + except HTTPException: + raise + except Exception as e: + logger.error("❌ Error fetching ACMP overview for customer %s: %s", customer_id, e) + raise HTTPException(status_code=500, detail=str(e)) + diff --git a/app/customers/frontend/customer_detail.html b/app/customers/frontend/customer_detail.html index f420252..af6dcf8 100644 --- a/app/customers/frontend/customer_detail.html +++ b/app/customers/frontend/customer_detail.html @@ -62,6 +62,24 @@ border-radius: 12px; padding: 1.5rem; } + + .acmp-stat-card { + background: var(--bg-card); + border: 1px solid rgba(15, 76, 117, 0.14); + border-radius: 12px; + padding: 1rem; + } + + .acmp-stat-label { + color: var(--text-secondary); + font-size: 0.85rem; + } + + .acmp-stat-value { + font-size: 1.2rem; + font-weight: 700; + color: var(--text-primary); + } .info-row { display: flex; @@ -228,9 +246,33 @@ .contacts-panel { background: var(--bg-card); - border: 1px solid rgba(0, 0, 0, 0.08); + border: 1px solid rgba(15, 76, 117, 0.14); border-radius: 14px; overflow: hidden; + box-shadow: 0 10px 28px rgba(15, 76, 117, 0.08); + } + + .contacts-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 0.65rem; + margin-bottom: 0.9rem; + padding: 0.78rem 0.9rem; + background: linear-gradient(135deg, rgba(15, 76, 117, 0.08) 0%, rgba(15, 76, 117, 0.02) 100%); + border: 1px solid rgba(15, 76, 117, 0.12); + border-radius: 12px; + } + + .contacts-search { + max-width: 460px; + min-width: 280px; + } + + .contacts-search .form-control, + .contacts-search .input-group-text { + border-color: rgba(15, 76, 117, 0.2); } .contacts-table { @@ -259,6 +301,18 @@ color: var(--text-primary); } + .contacts-table .contact-name-link { + color: var(--text-primary); + text-decoration: none; + border-bottom: 1px solid transparent; + transition: color 0.15s ease, border-color 0.15s ease; + } + + .contacts-table .contact-name-link:hover { + color: var(--accent); + border-color: rgba(15, 76, 117, 0.35); + } + .contacts-table .contact-email a { overflow-wrap: anywhere; } @@ -267,6 +321,7 @@ white-space: nowrap; } + .contacts-table .contact-phone-wrap, .contacts-table .contact-mobile-wrap { display: inline-flex; align-items: center; @@ -274,6 +329,12 @@ flex-wrap: wrap; } + .contacts-table .btn-voip { + border-color: rgba(25, 135, 84, 0.6); + color: #198754; + } + + .contacts-table .contact-phone-wrap .btn, .contacts-table .contact-mobile-wrap .btn { padding: 0.18rem 0.52rem; line-height: 1.2; @@ -285,6 +346,16 @@ } @media (max-width: 992px) { + .contacts-toolbar { + padding: 0.7rem; + } + + .contacts-search { + max-width: none; + min-width: 0; + width: 100%; + } + #contactsContainer { min-width: 920px; } @@ -406,6 +477,11 @@ Abonnements Matrix +