2026-07-09 23:44:30 +02:00
|
|
|
import sys
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
|
|
|
|
|
|
from app.billing.backend import supplier_invoices as supplier_module
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_sync_globalconnect_extraction_creates_connections_and_ip_ranges(monkeypatch):
|
|
|
|
|
created_connections = []
|
|
|
|
|
created_ranges = []
|
|
|
|
|
ensured_ranges = []
|
|
|
|
|
|
|
|
|
|
extraction = {
|
|
|
|
|
"extraction_id": 176,
|
|
|
|
|
"vendor_name": "GlobalConnect A/S",
|
|
|
|
|
"document_id": "3018657",
|
|
|
|
|
"document_date": "2026-01-01",
|
|
|
|
|
"llm_response_json": {"template": "dk.globalconnect"},
|
|
|
|
|
}
|
|
|
|
|
lines = [
|
|
|
|
|
{
|
|
|
|
|
"description": "100 Mbps fiberforbindelse",
|
|
|
|
|
"provider_reference": "NKA008214",
|
|
|
|
|
"circuit_id": "NKA008214",
|
|
|
|
|
"line_total": 2850.0,
|
|
|
|
|
"unit_price": 950.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"end_customer_name": "Malerfirmaet Gert Jensen ApS",
|
|
|
|
|
"service_address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"description": "IPv4 IP-adresser",
|
|
|
|
|
"provider_reference": "NKA008214",
|
|
|
|
|
"circuit_id": "NKA008214",
|
|
|
|
|
"ip_address": "152.115.84.232/29",
|
|
|
|
|
"line_total": 192.0,
|
|
|
|
|
"unit_price": 64.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"end_customer_name": "Malerfirmaet Gert Jensen ApS",
|
|
|
|
|
"service_address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
def fake_load_lines(_):
|
|
|
|
|
return lines
|
|
|
|
|
|
|
|
|
|
def fake_load_customers():
|
|
|
|
|
return [{"id": 322, "name": "Malerfirmaet Gert Jensen ApS", "address": "Metalbuen 26", "postal_code": "2750", "city": "Ballerup"}]
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "FROM internet_connections_connections" in query and "WHERE id = %s" in query:
|
|
|
|
|
return {"address": "Metalbuen 26, 2750 Ballerup", "allocation_model": "dedicated"}
|
|
|
|
|
if "FROM internet_connections_pricing" in query:
|
|
|
|
|
return None
|
|
|
|
|
if "FROM internet_connections_ip_ranges" in query:
|
|
|
|
|
return None
|
|
|
|
|
if "COUNT(*) AS total" in query and "FROM internet_connections_ip_addresses" in query:
|
|
|
|
|
return {"total": 0}
|
|
|
|
|
if "FROM internet_connections_ip_addresses" in query and "WHERE ip_address = %s" in query:
|
|
|
|
|
return None
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def fake_execute_query(query, params=None):
|
|
|
|
|
if "FROM internet_connections_connections" in query:
|
|
|
|
|
return []
|
|
|
|
|
if "FROM internet_connections_ip_ranges" in query:
|
|
|
|
|
return []
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
def fake_execute_insert(query, params=None):
|
|
|
|
|
if "INSERT INTO internet_connections_connections" in query:
|
|
|
|
|
created_connections.append(params)
|
|
|
|
|
return 501
|
|
|
|
|
if "INSERT INTO internet_connections_ip_ranges" in query:
|
|
|
|
|
created_ranges.append(params)
|
|
|
|
|
return 601
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
def fake_execute_update(query, params=None):
|
|
|
|
|
if "INSERT INTO internet_connections_ip_addresses" in query:
|
|
|
|
|
ensured_ranges.append(params[0])
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_extraction_lines", fake_load_lines)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_active_customers_for_matching", fake_load_customers)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", fake_execute_query)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_insert", fake_execute_insert)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", fake_execute_update)
|
|
|
|
|
|
|
|
|
|
result = supplier_module._sync_globalconnect_extraction_to_internet(extraction)
|
|
|
|
|
|
|
|
|
|
assert result["skipped"] is False
|
|
|
|
|
assert result["connections_synced"] == 1
|
|
|
|
|
assert result["connections_synced"] == 1
|
|
|
|
|
assert created_connections
|
|
|
|
|
assert created_ranges
|
|
|
|
|
assert ensured_ranges
|
|
|
|
|
assert created_connections[0][0] == "Malerfirmaet Gert Jensen ApS"
|
2026-08-30 14:34:43 +02:00
|
|
|
assert created_connections[0][2] is None
|
2026-07-09 23:44:30 +02:00
|
|
|
assert created_connections[0][9] == 100
|
|
|
|
|
assert created_connections[0][10] == 100
|
|
|
|
|
assert created_connections[0][11] == 100
|
|
|
|
|
assert created_connections[0][13] == "dedicated"
|
|
|
|
|
assert created_connections[0][14] == "other"
|
|
|
|
|
assert created_ranges[0][2] == "152.115.84.232/29"
|
2026-08-30 14:34:43 +02:00
|
|
|
assert created_ranges[0][6] is None
|
2026-07-09 23:44:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_sync_globalconnect_infers_dsl_kbps_speed(monkeypatch):
|
|
|
|
|
created_connections = []
|
|
|
|
|
|
|
|
|
|
extraction = {
|
|
|
|
|
"extraction_id": 175,
|
|
|
|
|
"vendor_name": "GlobalConnect A/S",
|
|
|
|
|
"document_id": "3016392",
|
|
|
|
|
"document_date": "2026-01-01",
|
|
|
|
|
"llm_response_json": {"template": "dk.globalconnect"},
|
|
|
|
|
}
|
|
|
|
|
lines = [
|
|
|
|
|
{
|
|
|
|
|
"description": "20480/2048 Kbps ADSL forbindelse",
|
|
|
|
|
"provider_reference": "DSL-EB680288",
|
|
|
|
|
"circuit_id": "DSL-EB680288",
|
|
|
|
|
"line_total": 420.0,
|
|
|
|
|
"unit_price": 140.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"end_customer_name": "Malerfirmaet Gert Jensen ApS",
|
|
|
|
|
"service_address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_extraction_lines", lambda _: lines)
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
supplier_module,
|
|
|
|
|
"_load_active_customers_for_matching",
|
|
|
|
|
lambda: [{"id": 322, "name": "Malerfirmaet Gert Jensen ApS", "address": "Metalbuen 26", "postal_code": "2750", "city": "Ballerup"}],
|
|
|
|
|
)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", lambda query, params=None: [])
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "FROM internet_connections_ip_addresses" in query:
|
|
|
|
|
return {"total": 0}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
|
|
|
|
|
def fake_execute_insert(query, params=None):
|
|
|
|
|
if "INSERT INTO internet_connections_connections" in query:
|
|
|
|
|
created_connections.append(params)
|
|
|
|
|
return 700
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_insert", fake_execute_insert)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", lambda query, params=None: 1)
|
|
|
|
|
|
|
|
|
|
result = supplier_module._sync_globalconnect_extraction_to_internet(extraction)
|
|
|
|
|
|
|
|
|
|
assert result["connections_synced"] == 1
|
|
|
|
|
assert created_connections
|
|
|
|
|
assert created_connections[0][9] == 20
|
|
|
|
|
assert created_connections[0][10] == 2
|
|
|
|
|
assert created_connections[0][11] == 20
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_sync_globalconnect_marks_uncertain_connection_pending(monkeypatch):
|
|
|
|
|
extraction = {
|
|
|
|
|
"extraction_id": 176,
|
|
|
|
|
"vendor_name": "GlobalConnect A/S",
|
|
|
|
|
"document_id": "3018657",
|
|
|
|
|
"document_date": "2026-01-01",
|
|
|
|
|
"llm_response_json": {"template": "dk.globalconnect"},
|
|
|
|
|
}
|
|
|
|
|
lines = [
|
|
|
|
|
{
|
|
|
|
|
"description": "1 Gbps fiberforbindelse",
|
|
|
|
|
"provider_reference": "NKA020902",
|
|
|
|
|
"circuit_id": "NKA020902",
|
|
|
|
|
"line_total": 5417.4,
|
|
|
|
|
"unit_price": 1805.8,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"end_customer_name": None,
|
|
|
|
|
"service_address": None,
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_extraction_lines", lambda _: lines)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_active_customers_for_matching", lambda: [])
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", lambda query, params=None: [])
|
2026-08-30 14:34:43 +02:00
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", lambda query, params=None: None)
|
2026-07-09 23:44:30 +02:00
|
|
|
|
|
|
|
|
result = supplier_module._sync_globalconnect_extraction_to_internet(extraction)
|
|
|
|
|
|
|
|
|
|
assert result["connections_synced"] == 0
|
|
|
|
|
assert result["skipped_connection_lines"] == 1
|
|
|
|
|
assert result["verification"]["requires_manual_review"] is True
|
|
|
|
|
assert result["skipped_items"][0]["reason"] == "Mangler serviceadresse"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_upsert_globalconnect_connection_requires_service_address(monkeypatch):
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", lambda query, params=None: [])
|
|
|
|
|
|
|
|
|
|
connection_id = supplier_module._upsert_globalconnect_connection(
|
|
|
|
|
reference="NKA020902",
|
|
|
|
|
lines=[{
|
|
|
|
|
"description": "1 Gbps fiberforbindelse",
|
|
|
|
|
"provider_reference": "NKA020902",
|
|
|
|
|
"circuit_id": "NKA020902",
|
|
|
|
|
"line_total": 5417.4,
|
|
|
|
|
"unit_price": 1805.8,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"end_customer_name": None,
|
|
|
|
|
"service_address": None,
|
|
|
|
|
}],
|
|
|
|
|
invoice_date="2026-01-01",
|
|
|
|
|
invoice_number="3018657",
|
|
|
|
|
customers=[],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert connection_id is None
|
|
|
|
|
|
|
|
|
|
|
2026-08-30 14:34:43 +02:00
|
|
|
def test_sync_globalconnect_creates_pending_reference_shell_without_customer(monkeypatch):
|
2026-07-09 23:44:30 +02:00
|
|
|
created_connections = []
|
|
|
|
|
|
|
|
|
|
extraction = {
|
|
|
|
|
"extraction_id": 176,
|
|
|
|
|
"vendor_name": "GlobalConnect A/S",
|
|
|
|
|
"document_id": "3018657",
|
|
|
|
|
"document_date": "2026-01-01",
|
|
|
|
|
"llm_response_json": {"template": "dk.globalconnect"},
|
|
|
|
|
}
|
|
|
|
|
lines = [
|
|
|
|
|
{
|
|
|
|
|
"description": "IPv4 IP-adresser",
|
|
|
|
|
"provider_reference": "NKA008225",
|
|
|
|
|
"circuit_id": "NKA008225",
|
|
|
|
|
"ip_address": "152.115.57.96/27",
|
|
|
|
|
"line_total": 768.0,
|
|
|
|
|
"unit_price": 256.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_extraction_lines", lambda _: lines)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_active_customers_for_matching", lambda: [])
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_resolve_internal_bmc_customer", lambda: {"id": 1662, "name": "BMC Networks"})
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", lambda query, params=None: [])
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "FROM internet_connections_ip_addresses" in query:
|
|
|
|
|
return {"total": 0}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
|
|
|
|
|
def fake_execute_insert(query, params=None):
|
|
|
|
|
if "INSERT INTO internet_connections_connections" in query:
|
|
|
|
|
created_connections.append(params)
|
|
|
|
|
return 800
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_insert", fake_execute_insert)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", lambda query, params=None: 1)
|
|
|
|
|
|
|
|
|
|
result = supplier_module._sync_globalconnect_extraction_to_internet(extraction)
|
|
|
|
|
|
2026-08-30 14:34:43 +02:00
|
|
|
assert result["connections_synced"] == 1
|
|
|
|
|
assert result["ip_ranges_synced"] == 1
|
|
|
|
|
assert result["skipped_orphan_ip_ranges"] == 0
|
|
|
|
|
assert created_connections
|
|
|
|
|
assert created_connections[0][2] is None
|
2026-07-09 23:44:30 +02:00
|
|
|
|
|
|
|
|
|
2026-08-30 14:34:43 +02:00
|
|
|
def test_upsert_globalconnect_connection_does_not_infer_shared_from_address_or_mpls(monkeypatch):
|
2026-07-09 23:44:30 +02:00
|
|
|
created_connections = []
|
|
|
|
|
line = {
|
|
|
|
|
"description": "1 Gbps fiberforbindelse MPLS VPN",
|
|
|
|
|
"provider_reference": "NKA020900",
|
|
|
|
|
"circuit_id": "NKA020900",
|
|
|
|
|
"line_total": 4950.0,
|
|
|
|
|
"unit_price": 1650.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"service_address": "Rydagervej 27, 2620 Albertslund",
|
|
|
|
|
"end_customer_name": "",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", lambda query, params=None: [])
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_resolve_internal_bmc_customer", lambda: {"id": 1662, "name": "BMC Networks"})
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_ensure_connection_pricing_entry", lambda **kwargs: None)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_append_connection_history", lambda *args, **kwargs: None)
|
|
|
|
|
|
|
|
|
|
def fake_execute_insert(query, params=None):
|
|
|
|
|
if "INSERT INTO internet_connections_connections" in query:
|
|
|
|
|
created_connections.append(params)
|
|
|
|
|
return 901
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_insert", fake_execute_insert)
|
|
|
|
|
|
|
|
|
|
connection_id = supplier_module._upsert_globalconnect_connection(
|
|
|
|
|
reference="NKA020900",
|
|
|
|
|
lines=[line],
|
|
|
|
|
invoice_date="2026-01-01",
|
|
|
|
|
invoice_number="3018657",
|
|
|
|
|
customers=[],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert connection_id == 901
|
|
|
|
|
assert created_connections
|
2026-08-30 14:34:43 +02:00
|
|
|
assert created_connections[0][2] is None
|
|
|
|
|
assert created_connections[0][-3] == "dedicated"
|
|
|
|
|
assert created_connections[0][-2] == "other"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_shared_classification_requires_explicit_invoice_wording():
|
|
|
|
|
assert supplier_module._should_assign_internal_bmc_owner(
|
|
|
|
|
[{"description": "Delt transit backbone"}], None, "Testvej 1"
|
|
|
|
|
) is True
|
|
|
|
|
assert supplier_module._should_assign_internal_bmc_owner(
|
|
|
|
|
[{"description": "MPLS VPN 1 Gbps"}], None, "Testvej 1"
|
|
|
|
|
) is False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_existing_ip_range_matches_canonical_network_not_raw_text(monkeypatch):
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
supplier_module,
|
|
|
|
|
"execute_query",
|
|
|
|
|
lambda query, params=None: [{
|
|
|
|
|
"connection_id": 44,
|
|
|
|
|
"cidr": "192.0.2.0/29",
|
|
|
|
|
"service_address": "Testvej 1, 8000 Aarhus",
|
|
|
|
|
"provider_reference": "NKA123456",
|
|
|
|
|
"connection_address": "Testvej 1, 8000 Aarhus",
|
|
|
|
|
"connection_reference": "NKA123456",
|
|
|
|
|
}],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = supplier_module._resolve_existing_ip_range_connection({
|
|
|
|
|
"ip_address": "192.0.2.1 / 29",
|
|
|
|
|
"provider_reference": "NKA-123456",
|
|
|
|
|
"service_address": "Testvej 1, 8000 Aarhus",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
assert result["connection_id"] == 44
|
|
|
|
|
assert result["conflict_reason"] is None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_unique_circuit_reference_matches_dash_and_dsl_eb_alias(monkeypatch):
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
supplier_module,
|
|
|
|
|
"execute_query",
|
|
|
|
|
lambda query, params=None: [
|
|
|
|
|
{"id": 11, "circuit_number": "NKA020900"},
|
|
|
|
|
{"id": 12, "circuit_number": "DSL-EB528263"},
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert supplier_module._find_unique_globalconnect_connection_by_reference("NKA-020900") == 11
|
|
|
|
|
assert supplier_module._find_unique_globalconnect_connection_by_reference("EB528263") == 12
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_reference_match_keys_treat_eb_and_dsl_eb_as_same_circuit():
|
|
|
|
|
assert supplier_module._provider_reference_match_keys("EB528263") == {"EB528263", "DSLEB528263"}
|
|
|
|
|
assert supplier_module._provider_reference_match_keys("DSL-EB528263") == {"EB528263", "DSLEB528263"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_bare_eb_ip_reference_is_not_allowed_to_create_a_pending_connection():
|
|
|
|
|
source = Path('app/billing/backend/supplier_invoices.py').read_text()
|
|
|
|
|
|
|
|
|
|
assert 'not simulate and not reference.startswith("EB")' in source
|
|
|
|
|
assert 'simulate and not reference.startswith("EB")' in source
|
2026-07-09 23:44:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_sync_globalconnect_attaches_ip_range_to_existing_connection(monkeypatch):
|
|
|
|
|
created_ranges = []
|
|
|
|
|
|
|
|
|
|
extraction = {
|
|
|
|
|
"extraction_id": 176,
|
|
|
|
|
"vendor_name": "GlobalConnect A/S",
|
|
|
|
|
"document_id": "3018657",
|
|
|
|
|
"document_date": "2026-01-01",
|
|
|
|
|
"llm_response_json": {"template": "dk.globalconnect"},
|
|
|
|
|
}
|
|
|
|
|
lines = [
|
|
|
|
|
{
|
|
|
|
|
"description": "IPv4 IP-adresser",
|
|
|
|
|
"provider_reference": "NKA008225",
|
|
|
|
|
"circuit_id": "NKA008225",
|
|
|
|
|
"ip_address": "152.115.57.96/27",
|
|
|
|
|
"line_total": 768.0,
|
|
|
|
|
"unit_price": 256.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"service_address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_extraction_lines", lambda _: lines)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_active_customers_for_matching", lambda: [])
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
supplier_module,
|
|
|
|
|
"execute_query",
|
|
|
|
|
lambda query, params=None: [{"id": 16, "address": "Metalbuen 26, 2750 Ballerup"}] if "FROM internet_connections_connections" in query else [],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "FROM internet_connections_ip_ranges" in query:
|
|
|
|
|
return None
|
|
|
|
|
if "FROM internet_connections_ip_addresses" in query:
|
|
|
|
|
return {"total": 0}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_insert", lambda query, params=None: 601 if "INSERT INTO internet_connections_ip_ranges" in query else 1)
|
|
|
|
|
|
|
|
|
|
def fake_execute_update(query, params=None):
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", fake_execute_update)
|
|
|
|
|
|
|
|
|
|
result = supplier_module._sync_globalconnect_extraction_to_internet(extraction)
|
|
|
|
|
|
|
|
|
|
assert result["connections_synced"] == 0
|
|
|
|
|
assert result["ip_ranges_synced"] == 1
|
|
|
|
|
assert result["skipped_orphan_ip_ranges"] == 0
|
|
|
|
|
assert result["line_audit"][0]["result"] == "linked_existing_connection"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_sync_globalconnect_normalizes_reference_without_dash(monkeypatch):
|
|
|
|
|
created_ranges = []
|
|
|
|
|
|
|
|
|
|
extraction = {
|
|
|
|
|
"extraction_id": 176,
|
|
|
|
|
"vendor_name": "GlobalConnect A/S",
|
|
|
|
|
"document_id": "3018657",
|
|
|
|
|
"document_date": "2026-01-01",
|
|
|
|
|
"llm_response_json": {"template": "dk.globalconnect"},
|
|
|
|
|
}
|
|
|
|
|
lines = [
|
|
|
|
|
{
|
|
|
|
|
"description": "IPv4 IP-adresser",
|
|
|
|
|
"provider_reference": "NKA008225",
|
|
|
|
|
"circuit_id": "NKA008225",
|
|
|
|
|
"ip_address": "152.115.57.96/27",
|
|
|
|
|
"line_total": 768.0,
|
|
|
|
|
"unit_price": 256.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"service_address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_extraction_lines", lambda _: lines)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_active_customers_for_matching", lambda: [])
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
supplier_module,
|
|
|
|
|
"execute_query",
|
|
|
|
|
lambda query, params=None: [{"id": 32, "address": "Metalbuen 26, 2750 Ballerup"}] if "FROM internet_connections_connections" in query else [],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "FROM internet_connections_ip_ranges" in query:
|
|
|
|
|
return None
|
|
|
|
|
if "FROM internet_connections_ip_addresses" in query:
|
|
|
|
|
return {"total": 0}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
def fake_execute_insert(query, params=None):
|
|
|
|
|
if "INSERT INTO internet_connections_ip_ranges" in query:
|
|
|
|
|
created_ranges.append(params)
|
|
|
|
|
return 3
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_insert", fake_execute_insert)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", lambda query, params=None: 1)
|
|
|
|
|
|
|
|
|
|
result = supplier_module._sync_globalconnect_extraction_to_internet(extraction)
|
|
|
|
|
|
|
|
|
|
assert result["ip_ranges_synced"] == 1
|
|
|
|
|
assert created_ranges
|
|
|
|
|
assert created_ranges[0][0] == 32
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_sync_globalconnect_merges_duplicate_connections(monkeypatch):
|
|
|
|
|
updates = []
|
|
|
|
|
|
|
|
|
|
extraction = {
|
|
|
|
|
"extraction_id": 176,
|
|
|
|
|
"vendor_name": "GlobalConnect A/S",
|
|
|
|
|
"document_id": "3018657",
|
|
|
|
|
"document_date": "2026-01-01",
|
|
|
|
|
"llm_response_json": {"template": "dk.globalconnect"},
|
|
|
|
|
}
|
|
|
|
|
lines = [
|
|
|
|
|
{
|
|
|
|
|
"description": "100 Mbps fiberforbindelse",
|
|
|
|
|
"provider_reference": "NKA008225",
|
|
|
|
|
"circuit_id": "NKA008225",
|
|
|
|
|
"line_total": 768.0,
|
|
|
|
|
"unit_price": 256.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"service_address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_extraction_lines", lambda _: lines)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_active_customers_for_matching", lambda: [])
|
|
|
|
|
|
|
|
|
|
def fake_execute_query(query, params=None):
|
|
|
|
|
if "FROM internet_connections_connections" in query:
|
|
|
|
|
return [{"id": 16, "customer_id": None, "address": None}, {"id": 32, "customer_id": None, "address": None}]
|
|
|
|
|
if "FROM internet_connections_ip_ranges" in query:
|
|
|
|
|
return []
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "FROM internet_connections_ip_ranges" in query:
|
|
|
|
|
return None
|
|
|
|
|
if "FROM internet_connections_ip_addresses" in query:
|
|
|
|
|
return {"total": 0}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", fake_execute_query)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_insert", lambda query, params=None: 3)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_ensure_internet_change_case", lambda **kwargs: 99)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_ensure_connection_pricing_entry", lambda **kwargs: None)
|
|
|
|
|
|
|
|
|
|
def fake_execute_update(query, params=None):
|
|
|
|
|
updates.append((query, params))
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", fake_execute_update)
|
|
|
|
|
|
|
|
|
|
result = supplier_module._sync_globalconnect_extraction_to_internet(extraction)
|
|
|
|
|
|
|
|
|
|
assert result["connections_synced"] == 1
|
|
|
|
|
assert any(
|
|
|
|
|
"UPDATE internet_connections_ip_ranges" in query and params[0] == 16 and params[1] == 32
|
|
|
|
|
for query, params in updates
|
|
|
|
|
)
|
|
|
|
|
assert any(
|
|
|
|
|
"UPDATE internet_connections_connections" in query and params[1] == 32
|
|
|
|
|
for query, params in updates
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_sync_globalconnect_skips_reference_when_address_conflicts(monkeypatch):
|
|
|
|
|
extraction = {
|
|
|
|
|
"extraction_id": 176,
|
|
|
|
|
"vendor_name": "GlobalConnect A/S",
|
|
|
|
|
"document_id": "3018657",
|
|
|
|
|
"document_date": "2026-01-01",
|
|
|
|
|
"llm_response_json": {"template": "dk.globalconnect"},
|
|
|
|
|
}
|
|
|
|
|
lines = [
|
|
|
|
|
{
|
|
|
|
|
"description": "100 Mbps fiberforbindelse",
|
|
|
|
|
"provider_reference": "NKA008225",
|
|
|
|
|
"circuit_id": "NKA008225",
|
|
|
|
|
"line_total": 768.0,
|
|
|
|
|
"unit_price": 256.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"service_address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_extraction_lines", lambda _: lines)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_active_customers_for_matching", lambda: [])
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
supplier_module,
|
|
|
|
|
"execute_query",
|
|
|
|
|
lambda query, params=None: [{"id": 16, "address": "Andenvej 99, 2100 København Ø"}] if "FROM internet_connections_connections" in query else [],
|
|
|
|
|
)
|
2026-08-30 14:34:43 +02:00
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", lambda query, params=None: None)
|
2026-07-09 23:44:30 +02:00
|
|
|
|
|
|
|
|
result = supplier_module._sync_globalconnect_extraction_to_internet(extraction)
|
|
|
|
|
|
|
|
|
|
assert result["connections_synced"] == 0
|
|
|
|
|
assert result["verification"]["requires_manual_review"] is True
|
|
|
|
|
assert "anden adresse" in result["skipped_items"][0]["reason"].lower()
|
|
|
|
|
|
|
|
|
|
|
2026-08-30 14:34:43 +02:00
|
|
|
def test_sync_globalconnect_uses_circuit_address_when_ip_line_address_is_shifted(monkeypatch):
|
2026-07-09 23:44:30 +02:00
|
|
|
extraction = {
|
|
|
|
|
"extraction_id": 180,
|
|
|
|
|
"vendor_name": "GlobalConnect A/S",
|
|
|
|
|
"document_id": "3018657",
|
|
|
|
|
"document_date": "2026-01-01",
|
|
|
|
|
"llm_response_json": {"template": "dk.globalconnect"},
|
|
|
|
|
}
|
|
|
|
|
lines = [
|
|
|
|
|
{
|
|
|
|
|
"description": "1 Gbps fiberforbindelse MPLS VPN",
|
|
|
|
|
"provider_reference": "NKA020900",
|
|
|
|
|
"circuit_id": "NKA020900",
|
|
|
|
|
"line_total": 4950.0,
|
|
|
|
|
"unit_price": 1650.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"service_address": "Rydagervej 27, 2620 Albertslund",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"description": "IPv4 IP-adresser",
|
|
|
|
|
"provider_reference": "NKA-020900",
|
|
|
|
|
"circuit_id": "NKA-020900",
|
|
|
|
|
"ip_address": "87.116.1.200/29",
|
|
|
|
|
"line_total": 384.0,
|
|
|
|
|
"unit_price": 128.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"service_address": "Tobaksvejen 25, 2860 Søborg",
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_extraction_lines", lambda _: lines)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_active_customers_for_matching", lambda: [])
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_ensure_connection_pricing_entry", lambda **kwargs: None)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_append_connection_history", lambda *args, **kwargs: None)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_ensure_internet_change_case", lambda **kwargs: None)
|
|
|
|
|
|
|
|
|
|
def fake_execute_query(query, params=None):
|
|
|
|
|
if "FROM internet_connections_connections" in query:
|
|
|
|
|
if "regexp_replace(UPPER(COALESCE(circuit_number, ''))" in query:
|
|
|
|
|
return []
|
|
|
|
|
return []
|
|
|
|
|
if "FROM internet_connections_ip_ranges" in query:
|
|
|
|
|
return []
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "FROM customers" in query:
|
|
|
|
|
return None
|
|
|
|
|
if "FROM internet_connections_connections" in query and "WHERE id = %s" in query:
|
|
|
|
|
return {
|
|
|
|
|
"address": "Rydagervej 27, 2620 Albertslund",
|
|
|
|
|
"allocation_model": "dedicated",
|
|
|
|
|
}
|
|
|
|
|
if "FROM internet_connections_ip_ranges" in query:
|
|
|
|
|
return None
|
|
|
|
|
if "FROM internet_connections_ip_addresses" in query:
|
|
|
|
|
return {"total": 0}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
created_connections = []
|
|
|
|
|
|
|
|
|
|
def fake_execute_insert(query, params=None):
|
|
|
|
|
if "INSERT INTO internet_connections_connections" in query:
|
|
|
|
|
created_connections.append(params)
|
|
|
|
|
return 96
|
|
|
|
|
if "INSERT INTO internet_connections_ip_ranges" in query:
|
2026-08-30 14:34:43 +02:00
|
|
|
return 97
|
2026-07-09 23:44:30 +02:00
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", fake_execute_query)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_insert", fake_execute_insert)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", lambda query, params=None: 1)
|
|
|
|
|
|
|
|
|
|
result = supplier_module._sync_globalconnect_extraction_to_internet(extraction)
|
|
|
|
|
|
|
|
|
|
assert result["connections_synced"] == 1
|
2026-08-30 14:34:43 +02:00
|
|
|
assert result["ip_ranges_synced"] == 1
|
|
|
|
|
assert result["skipped_orphan_ip_ranges"] == 0
|
2026-07-09 23:44:30 +02:00
|
|
|
assert created_connections
|
2026-08-30 14:34:43 +02:00
|
|
|
ip_entries = [entry for entry in result["line_audit"] if entry["classification"] == "ip_range"]
|
|
|
|
|
assert ip_entries[0]["status"] == "synced"
|
|
|
|
|
assert ip_entries[0]["matched_by"] == "unique_circuit_reference"
|
|
|
|
|
assert ip_entries[0]["service_address_corrected"] is True
|
|
|
|
|
assert "Rydagervej 27" in ip_entries[0]["address_warning"]
|
2026-07-09 23:44:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_upsert_globalconnect_connection_logs_changes_and_creates_case(monkeypatch):
|
|
|
|
|
history_calls = []
|
|
|
|
|
case_calls = []
|
|
|
|
|
updates = []
|
|
|
|
|
|
|
|
|
|
customer = {
|
|
|
|
|
"id": 322,
|
|
|
|
|
"name": "Malerfirmaet Gert Jensen ApS",
|
|
|
|
|
"address": "Metalbuen 26",
|
|
|
|
|
"postal_code": "2750",
|
|
|
|
|
"city": "Ballerup",
|
|
|
|
|
}
|
|
|
|
|
line = {
|
|
|
|
|
"description": "200 Mbps fiberforbindelse",
|
|
|
|
|
"provider_reference": "NKA008214",
|
|
|
|
|
"circuit_id": "NKA008214",
|
|
|
|
|
"line_total": 360.0,
|
|
|
|
|
"unit_price": 120.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"end_customer_name": "Malerfirmaet Gert Jensen ApS",
|
|
|
|
|
"service_address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def fake_execute_query(query, params=None):
|
|
|
|
|
if "FROM internet_connections_connections" in query:
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
"id": 16,
|
|
|
|
|
"customer_id": 322,
|
|
|
|
|
"address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
"monthly_cost": Decimal("100.00"),
|
|
|
|
|
"technology": "Fiber",
|
|
|
|
|
"connection_type": "fiber",
|
|
|
|
|
"circuit_number": "NKA008214",
|
|
|
|
|
"speed_mbps": 100,
|
|
|
|
|
"download_mbps": 100,
|
|
|
|
|
"upload_mbps": 100,
|
|
|
|
|
"status": "active",
|
|
|
|
|
"allocation_model": "dedicated",
|
|
|
|
|
"value_type": "other",
|
|
|
|
|
"value_label": None,
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", fake_execute_query)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", lambda query, params=None: updates.append((query, params)) or 1)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_append_connection_history", lambda *args: history_calls.append(args))
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_ensure_internet_change_case", lambda **kwargs: case_calls.append(kwargs) or 91)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_ensure_connection_pricing_entry", lambda **kwargs: None)
|
|
|
|
|
|
|
|
|
|
connection_id = supplier_module._upsert_globalconnect_connection(
|
|
|
|
|
reference="NKA008214",
|
|
|
|
|
lines=[line],
|
|
|
|
|
invoice_date="2026-01-01",
|
|
|
|
|
invoice_number="3018657",
|
|
|
|
|
customers=[customer],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert connection_id == 16
|
|
|
|
|
assert updates
|
|
|
|
|
assert history_calls
|
|
|
|
|
assert history_calls[0][1] == "supplier_invoice_sync_changed"
|
|
|
|
|
assert history_calls[0][3]["changes"]["monthly_cost"] == {"from": "100.00", "to": "120.00"}
|
|
|
|
|
assert history_calls[0][3]["changes"]["speed_mbps"] == {"from": "100", "to": "200"}
|
|
|
|
|
assert case_calls
|
|
|
|
|
assert case_calls[0]["connection_id"] == 16
|
|
|
|
|
assert case_calls[0]["changes"]["download_mbps"] == {"from": "100", "to": "200"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_upsert_globalconnect_ip_range_logs_changes_and_creates_case(monkeypatch):
|
|
|
|
|
history_calls = []
|
|
|
|
|
case_calls = []
|
|
|
|
|
customer = {
|
|
|
|
|
"id": 322,
|
|
|
|
|
"name": "Malerfirmaet Gert Jensen ApS",
|
|
|
|
|
"address": "Metalbuen 26",
|
|
|
|
|
"postal_code": "2750",
|
|
|
|
|
"city": "Ballerup",
|
|
|
|
|
}
|
|
|
|
|
line = {
|
|
|
|
|
"description": "IPv4 IP-adresser",
|
|
|
|
|
"provider_reference": "NKA008214",
|
|
|
|
|
"circuit_id": "NKA008214",
|
|
|
|
|
"contract_number": "GC-NEW",
|
|
|
|
|
"ip_address": "152.115.84.232/29",
|
|
|
|
|
"line_total": 240.0,
|
|
|
|
|
"unit_price": 80.0,
|
|
|
|
|
"quantity": 3,
|
|
|
|
|
"end_customer_name": "Malerfirmaet Gert Jensen ApS",
|
|
|
|
|
"service_address": "Metalbuen 26, 2750 Ballerup",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "FROM internet_connections_ip_ranges" in query:
|
|
|
|
|
return {
|
|
|
|
|
"id": 88,
|
|
|
|
|
"provider_reference": "NKA008214",
|
|
|
|
|
"contract_number": "GC-OLD",
|
|
|
|
|
"customer_id": None,
|
|
|
|
|
"service_address": "Gammel adresse 1, 2000 Frederiksberg",
|
|
|
|
|
"monthly_cost": Decimal("64.00"),
|
|
|
|
|
}
|
|
|
|
|
if "FROM internet_connections_ip_addresses" in query:
|
|
|
|
|
return {"total": 6}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query", lambda query, params=None: [])
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", lambda query, params=None: 1)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_append_connection_history", lambda *args: history_calls.append(args))
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_ensure_internet_change_case", lambda **kwargs: case_calls.append(kwargs) or 92)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_ensure_ip_addresses_for_range", lambda range_id, cidr: 6)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "_load_active_customers_for_matching", lambda: [customer])
|
|
|
|
|
|
|
|
|
|
range_id = supplier_module._upsert_globalconnect_ip_range(16, line, "3018657")
|
|
|
|
|
|
|
|
|
|
assert range_id == 88
|
|
|
|
|
assert history_calls
|
|
|
|
|
assert history_calls[0][1] == "supplier_invoice_ip_range_changed"
|
|
|
|
|
assert history_calls[0][3]["changes"]["contract_number"] == {"from": "GC-OLD", "to": "GC-NEW"}
|
|
|
|
|
assert history_calls[0][3]["changes"]["monthly_cost"] == {"from": "64.00", "to": "80.00"}
|
|
|
|
|
assert case_calls
|
|
|
|
|
assert case_calls[0]["reference"] == "NKA008214"
|
|
|
|
|
assert case_calls[0]["changes"]["service_address"]["to"] == "Metalbuen 26, 2750 Ballerup"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_supplier_sync_skips_ip_addresses_that_already_exist(monkeypatch):
|
|
|
|
|
inserted = []
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "COUNT(*) AS total" in query and "internet_connections_ip_addresses" in query:
|
|
|
|
|
return {"total": 0}
|
|
|
|
|
if "FROM internet_connections_ip_addresses" in query and "WHERE ip_address = %s" in query:
|
|
|
|
|
if params[0] == "152.115.84.233":
|
|
|
|
|
return {"id": 91}
|
|
|
|
|
return None
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def fake_execute_update(query, params=None):
|
|
|
|
|
if "INSERT INTO internet_connections_ip_addresses" in query:
|
|
|
|
|
inserted.append(params[1])
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", fake_execute_update)
|
|
|
|
|
|
|
|
|
|
created = supplier_module._ensure_ip_addresses_for_range(601, "152.115.84.232/30")
|
|
|
|
|
|
|
|
|
|
assert created == 1
|
|
|
|
|
assert inserted == ["152.115.84.234"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_append_amount_validation_case_note_creates_system_comment(monkeypatch):
|
|
|
|
|
comments = []
|
|
|
|
|
|
|
|
|
|
def fake_execute_query_single(query, params=None):
|
|
|
|
|
if "FROM sag_kommentarer" in query:
|
|
|
|
|
return None
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def fake_execute_update(query, params=None):
|
|
|
|
|
if "INSERT INTO sag_kommentarer" in query:
|
|
|
|
|
comments.append(params)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_query_single", fake_execute_query_single)
|
|
|
|
|
monkeypatch.setattr(supplier_module, "execute_update", fake_execute_update)
|
|
|
|
|
|
|
|
|
|
supplier_module._append_amount_validation_case_note(
|
|
|
|
|
sag_id=91,
|
|
|
|
|
invoice_id=17,
|
|
|
|
|
invoice_number="3018657",
|
|
|
|
|
validation_details={
|
|
|
|
|
"line_sum": 600.0,
|
|
|
|
|
"subtotal": 800.0,
|
|
|
|
|
"difference": 200.0,
|
|
|
|
|
"vat_amount": 200.0,
|
|
|
|
|
"vat_expected": 200.0,
|
|
|
|
|
"vat_difference": 0.0,
|
|
|
|
|
},
|
|
|
|
|
validation_warning="Varelinjer sum (600.00) passer ikke med subtotal (800.00)",
|
|
|
|
|
vat_warning=None,
|
|
|
|
|
file_id=176,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert comments
|
|
|
|
|
assert comments[0][0] == 91
|
|
|
|
|
assert "Subtotal-advarsel" in comments[0][2]
|
|
|
|
|
assert "Afvigelse: 200.0" in comments[0][2]
|