From 9ca562745a853ebf84c39fbfe8874ef768263454 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 7 Sep 2026 19:05:58 +0200 Subject: [PATCH] feat(subscriptions): enhance subscription update logic to allow direct edits for drafts and add new endpoint for manual invoice processing feat(ticket): update email integration to use new priority constants for ticket classification feat(procurement): add procurement overview page with dynamic data loading and display test(subscriptions): add tests for billing calendar to ensure correct invoice dates feat(reminder): implement automated task lists with user-defined rules for reminders feat(migrations): create tables for managing delefiber product prices and mobile recorder provisioning history test(mobile_recorder): add tests for provisioning mobile recorders to ensure correct asset creation and updates --- .env.example | 1 + app/billing/backend/supplier_invoices.py | 27 +- app/core/config.py | 4 + app/customers/frontend/customer_detail.html | 11 +- app/emails/backend/router.py | 36 +- app/jobs/check_reminders.py | 2 + app/jobs/process_subscriptions.py | 16 +- app/modules/bottom_bar/backend/service.py | 8 + app/modules/hardware/backend/router.py | 153 +++++++ .../backend/change_case_service.py | 37 +- .../internet_connections/backend/router.py | 120 ++++- .../templates/detail.html | 91 +++- app/modules/sag/backend/reminders.py | 90 ++++ app/modules/sag/backend/router.py | 96 +++- app/modules/sag/frontend/views.py | 9 + app/modules/sag/templates/detail_v3.html | 424 +++++++++++++++++- .../sag/templates/procurement_overview.html | 15 + app/modules/sag/templates/reminder_rules.html | 19 + app/services/email_processor_service.py | 17 + app/services/email_service.py | 160 ++++++- app/services/email_workflow_service.py | 52 ++- app/services/subscription_billing_calendar.py | 17 +- app/shared/frontend/base.html | 112 ++++- app/subscriptions/backend/router.py | 96 +++- app/ticket/backend/email_integration.py | 8 +- migrations/235_delefiber_product_prices.sql | 16 + .../236_mobile_recorder_provisioning.sql | 32 ++ ...37_manual_mobile_recorder_provisioning.sql | 106 +++++ static/js/bottom-bar.js | 4 + tests/test_mobile_recorder_provisioning.py | 73 +++ tests/test_subscription_billing_calendar.py | 6 + 31 files changed, 1776 insertions(+), 82 deletions(-) create mode 100644 app/modules/sag/templates/procurement_overview.html create mode 100644 app/modules/sag/templates/reminder_rules.html create mode 100644 migrations/235_delefiber_product_prices.sql create mode 100644 migrations/236_mobile_recorder_provisioning.sql create mode 100644 migrations/237_manual_mobile_recorder_provisioning.sql create mode 100644 tests/test_mobile_recorder_provisioning.py diff --git a/.env.example b/.env.example index a9926e3..c37cf8e 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,7 @@ # ===================================================== DATABASE_URL=postgresql://bmc_hub:bmc_hub@postgres:5432/bmc_hub HUB_BASE_URL=https://hub.bmcnetworks.dk +MOBILE_RECORDER_PROVISIONING_TOKEN=replace-with-a-long-random-service-token # Database credentials (bruges af docker-compose) POSTGRES_USER=bmc_hub diff --git a/app/billing/backend/supplier_invoices.py b/app/billing/backend/supplier_invoices.py index 1c5758f..3c55095 100644 --- a/app/billing/backend/supplier_invoices.py +++ b/app/billing/backend/supplier_invoices.py @@ -1125,7 +1125,7 @@ def _get_globalconnect_connections_by_reference(reference: str) -> List[Dict]: """ SELECT id, customer_id, address, monthly_cost, technology, connection_type, circuit_number, speed_mbps, download_mbps, upload_mbps, status, - allocation_model, value_type, value_label + allocation_model, value_type, value_label, is_manual_shared FROM internet_connections_connections WHERE deleted_at IS NULL AND provider ILIKE 'GlobalConnect%%' @@ -1264,6 +1264,19 @@ def _upsert_globalconnect_connection(reference: str, lines: List[Dict], invoice_ ) if existing: + # Delefiber/BMCnet classification is internal operational data. A + # supplier invoice may update speed and cost, but must never undo a + # deliberate manual shared/delefiber designation. + preserve_manual_classification = bool(existing.get("is_manual_shared")) + resolved_allocation_model = ( + existing.get("allocation_model") if preserve_manual_classification + else ("shared" if is_shared_candidate else "dedicated") + ) + resolved_value_type = ( + existing.get("value_type") if preserve_manual_classification + else shared_value_type + ) + resolved_value_label = existing.get("value_label") if preserve_manual_classification else None updated_snapshot = { "customer_id": existing.get("customer_id"), "address": service_address, @@ -1275,9 +1288,9 @@ def _upsert_globalconnect_connection(reference: str, lines: List[Dict], invoice_ "download_mbps": download_mbps, "upload_mbps": upload_mbps, "status": target_status, - "allocation_model": "shared" if is_shared_candidate else "dedicated", - "value_type": shared_value_type, - "value_label": None, + "allocation_model": resolved_allocation_model, + "value_type": resolved_value_type, + "value_label": resolved_value_label, } update_payload = ( connection_name, @@ -1292,9 +1305,9 @@ def _upsert_globalconnect_connection(reference: str, lines: List[Dict], invoice_ download_mbps, upload_mbps, note_text, - "shared" if is_shared_candidate else "dedicated", - shared_value_type, - None, + resolved_allocation_model, + resolved_value_type, + resolved_value_label, existing["id"], ) execute_update( diff --git a/app/core/config.py b/app/core/config.py index f54a7b2..bcf1171 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -37,6 +37,10 @@ class Settings(BaseSettings): ENABLE_RELOAD: bool = False # Added to match docker-compose.yml HUB_BASE_URL: str = "https://hub.bmcnetworks.dk" + # Non-interactive service token for Apple Configurator/cfgutil provisioning. + # Leave empty to disable the endpoint rather than accepting unauthenticated calls. + MOBILE_RECORDER_PROVISIONING_TOKEN: str = "" + # Elnet supplier lookup ELNET_API_BASE_URL: str = "https://api.elnet.greenpowerdenmark.dk/api" ELNET_TIMEOUT_SECONDS: int = 12 diff --git a/app/customers/frontend/customer_detail.html b/app/customers/frontend/customer_detail.html index 62432d9..617008f 100644 --- a/app/customers/frontend/customer_detail.html +++ b/app/customers/frontend/customer_detail.html @@ -891,7 +891,7 @@