feat(#49): add geolocation (latitude/longitude) to municipalities

- Municipality model with optional latitude/longitude decimal fields
- Municipality and brief serializers expose lat/lng (also in product_provenance of public/authenticated summaries for the Leaflet map)
- seed_geography endpoint reads lat/lng and updates existing municipalities on reseed (idempotent)
- TDD tests for model, serializers, summaries and seed
This commit is contained in:
2026-08-16 04:10:45 -05:00
parent 699983931e
commit 3a0c76ce69
7 changed files with 140 additions and 3 deletions

View File

@@ -68,6 +68,26 @@ class TestMunicipalityModel(TestCase):
self.assertEqual(municipality.department.country.name, "Colombia")
self.assertEqual(str(municipality), "La Mesa")
def test_municipality_geolocation_fields(self):
municipality = Municipality.objects.create(
name="La Mesa",
department=self.department,
country=self.country,
latitude=4.63092,
longitude=-74.39152,
)
self.assertEqual(municipality.latitude, 4.63092)
self.assertEqual(municipality.longitude, -74.39152)
def test_municipality_geolocation_optional(self):
municipality = Municipality.objects.create(
name="La Mesa",
department=self.department,
country=self.country,
)
self.assertIsNone(municipality.latitude)
self.assertIsNone(municipality.longitude)
def test_municipality_requires_department_and_country(self):
with self.assertRaises(IntegrityError):
with transaction.atomic():

View File

@@ -241,6 +241,27 @@ class TestProvenanceCRUD(APITestCase, LoginMixin):
self.assertEqual(data["country_detail"]["name"], "Colombia")
self.assertEqual(data["country_detail"]["code"], "CO")
def test_municipality_serializer_includes_geolocation(self):
self.municipality.latitude = 4.63092
self.municipality.longitude = -74.39152
self.municipality.save()
response = self.client.get(
f"/don_confiao/api/municipalities/{self.municipality.id}/"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = response.json()
self.assertEqual(float(data["latitude"]), 4.63092)
self.assertEqual(float(data["longitude"]), -74.39152)
def test_municipality_serializer_geolocation_optional(self):
response = self.client.get(
f"/don_confiao/api/municipalities/{self.municipality.id}/"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = response.json()
self.assertIsNone(data["latitude"])
self.assertIsNone(data["longitude"])
def test_link_product_to_suppliers_via_product_api(self):
product = Product.objects.create(name="Panela", price=5000)
supplier2 = Supplier.objects.create(name="Proveedor 2")
@@ -315,6 +336,37 @@ class TestSeedGeography(APITestCase, LoginMixin):
self.assertEqual(Department.objects.count(), 1)
self.assertEqual(Municipality.objects.count(), 2)
def test_seed_creates_municipality_with_geolocation(self):
self.login()
payload = self._payload()
payload["municipalities"][0] = {
"name": "La Mesa",
"department": "Cundinamarca",
"latitude": 4.63092,
"longitude": -74.39152,
}
response = self.client.post(self.url, payload, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
municipality = Municipality.objects.get(name="La Mesa")
self.assertEqual(float(municipality.latitude), 4.63092)
self.assertEqual(float(municipality.longitude), -74.39152)
def test_seed_updates_geolocation_on_reseed(self):
self.login()
self.client.post(self.url, self._payload(), format="json")
payload = self._payload()
payload["municipalities"][0] = {
"name": "La Mesa",
"department": "Cundinamarca",
"latitude": 4.63092,
"longitude": -74.39152,
}
response = self.client.post(self.url, payload, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
municipality = Municipality.objects.get(name="La Mesa")
self.assertEqual(float(municipality.latitude), 4.63092)
self.assertEqual(float(municipality.longitude), -74.39152)
def test_seed_requires_country_name(self):
self.login()
response = self.client.post(

View File

@@ -75,6 +75,23 @@ class TestProvenanceInSummaries(APITestCase, LoginMixin):
self.assertEqual(supplier["department"]["name"], "Cundinamarca")
self.assertEqual(supplier["country"]["name"], "Colombia")
def test_public_summary_municipality_includes_geolocation(self):
self.municipality.latitude = 4.63092
self.municipality.longitude = -74.39152
self.municipality.save()
sale = self._create_sale()
response = self.client.get(
f"/don_confiao/resumen_publico/{sale.code}"
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
supplier = response.json()["product_provenance"][0]["suppliers"][0]
self.assertEqual(
float(supplier["municipality"]["latitude"]), 4.63092
)
self.assertEqual(
float(supplier["municipality"]["longitude"]), -74.39152
)
def test_public_catalog_summary_includes_product_provenance(self):
catalog_sale = self._create_catalog_sale()
response = self.client.get(