Initial commit edera-api edera-api origin/edera-api
authorRoberto Stomeo <Roberto Stomeo@dyrectalab.local>
Tue, 1 Jul 2025 14:42:34 +0000 (16:42 +0200)
committerRoberto Stomeo <Roberto Stomeo@dyrectalab.local>
Tue, 1 Jul 2025 14:42:34 +0000 (16:42 +0200)
207 files changed:
edera-api/.gitignore [new file with mode: 0644]
edera-api/README.md [new file with mode: 0644]
edera-api/api/pom.xml [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/Application.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/configuration/ApplicationConfiguration.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/configuration/AuditLogConfiguration.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/configuration/GeoAutoConfiguration.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/configuration/MLConfiguration.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/configuration/NotificationsConfiguration.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/configuration/SecurityConfiguration.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/Category.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/Device.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/DeviceDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/Gallery.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/I18nMessage.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/I18nMessageRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/Notification.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/NotificationDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/SubCategory.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/User.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/UsersDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/AuditLog.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/AuditLogMessage.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/AuditLogMessageBuilder.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/AuditLogMessageBuilderFactory.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudAuditLogMessageBuilder.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudCreateAuditLogMessageBuilder.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudDeleteAuditLogMessageBuilder.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudEditAuditLogMessageBuilder.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudFindAuditLogMessageBuilder.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudViewAuditLogMessageBuilder.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/customer/Area.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/customer/AreaRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/customer/Customer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/customer/CustomerRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/customer/Office.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/customer/Referent.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/DocxBuilder.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/DocxDataSource.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/DocxDataSourceRow.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/DocxDescriptor.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/DocxException.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/DocxFactory.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/DocxField.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/DocxTemplate.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/DocxTypeDef.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/docx/typedef/MaintenanceDocxTypeDef.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/dto/ActivityDetailsDTO.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/dto/SupplyDetailsDTO.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/equipment/Equipment.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentAttachment.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentType.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentTypeAttachment.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentTypeRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/geo/City.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/geo/Nation.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/geo/Province.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/geo/Region.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/maintenance/Activity.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityStatus.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityType.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityTypeRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/maintenance/Intervention.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/maintenance/Maintenance.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/maintenance/MaintenanceRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/package-info.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/pricing/PriceList.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/pricing/PriceListItem.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/protocol/Protocol.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/protocol/ProtocolAttachment.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/protocol/ProtocolRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/protocol/ProtocolService.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/reporting/DataPoint.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/reporting/DataPointSet.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/reporting/Filter.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/reporting/FilterSet.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/reporting/GroupBy.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/reporting/TemporalFilter.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDevice.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrack.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrackRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrackStatsRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrackStatsRepositoryImpl.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceType.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supplier/Referent.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supplier/Supplier.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supplier/SupplierRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supply/Supply.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supply/SupplyRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supply/SupplyStatsRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supply/SupplyStatsRepositoryImpl.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supply/Usage.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supply/UsageOverlapUtilsRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supply/UsageOverlapUtilsRepositoryImpl.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supply/UsageRepository.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/domain/supply/UsageStatus.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/mapping/Mapper.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/ml/MLClient.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/ml/MLRequest.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/ml/MLResponse.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/ml/PredictionItem.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/services/EderaService.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/services/GeoInitializationService.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/services/InitializationService.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/services/reporting/Report.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/services/reporting/ReportData.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/services/reporting/ReportFactory.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/services/reporting/impl/SupplyReport.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/services/reporting/impl/SupplyTracksReport.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/tasks/ProtocolServiceNotifyTask.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/utils/SecurityUtils.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/annotations/CustomerFilterable.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/DocxController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/I18nController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/IAMController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/MaintenanceController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/ProfileController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/ProtectedRestController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/RFIDDeviceTrackController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/ReportController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/SupplyController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/controllers/ValuesController.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/ActivityDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/AuditLogDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/DefaultDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/EquipmentDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/EquipmentTypeDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/MaintenanceDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/PriceListItemDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/ProtocolDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/RFIDDeviceTrackDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/crud/SupplyDataLayer.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/filters/AuditLogTraceFilter.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/filters/JwtAuthenticationFilter.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/operations/EquipmentTypeGetOperation.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/operations/I18nMessageDeleteOperation.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/operations/I18nMessageSaveOperation.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/operations/NotificationFindOperation.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/operations/PriceListItemSaveOperation.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/operations/PriceListSaveOperation.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/operations/decorators/CustomerFilterableFindDecorator.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/requests/GetReportDataRequest.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/requests/PatchProfileRequest.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/responses/ActivityInfoResponse.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/responses/CrudLoginResponse.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/responses/GetReportDataResponse.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/responses/SupplyInfoResponse.java [new file with mode: 0644]
edera-api/api/src/main/java/applica/app/web/security/JwtAuthenticationEntryPoint.java [new file with mode: 0644]
edera-api/api/src/main/resources/application-production.yaml [new file with mode: 0644]
edera-api/api/src/main/resources/application-test.yaml [new file with mode: 0644]
edera-api/api/src/main/resources/application.yaml [new file with mode: 0644]
edera-api/api/src/main/resources/credentials/service-account.json [new file with mode: 0644]
edera-api/api/src/main/resources/csv/cities.csv [new file with mode: 0644]
edera-api/api/src/main/resources/csv/nations.csv [new file with mode: 0644]
edera-api/api/src/main/resources/csv/provinces.csv [new file with mode: 0644]
edera-api/api/src/main/resources/csv/regions.csv [new file with mode: 0644]
edera-api/api/src/main/resources/docx/helper.docx [new file with mode: 0644]
edera-api/api/src/main/resources/i18n.json [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/TestApplication.java [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/TestDataFactory.java [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/configuration/TestConfiguration.java [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/domain/docx/DocxBuilderTest.java [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/endtoend/IAMControllerTest.java [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/endtoend/RFIDDeviceTrackControllerTest.java [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/fixture/AreaFixture.java [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/fixture/Fixture.java [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/fixture/RFIDDeviceFixture.java [new file with mode: 0644]
edera-api/api/src/test/java/applica/app/test/fixture/SupplyFixture.java [new file with mode: 0644]
edera-api/api/src/test/resources/i18n.json [new file with mode: 0644]
edera-api/api/src/test/resources/sample-google-workspace.docx [new file with mode: 0644]
edera-api/api/src/test/resources/sample-ms-office-open-xml.docx [new file with mode: 0644]
edera-api/api/src/test/resources/sample-ms-office.docx [new file with mode: 0644]
edera-api/api/src/test/resources/sample-open-office.docx [new file with mode: 0644]
edera-api/bitbucket-pipelines.yml [new file with mode: 0644]
edera-api/mvnw [new file with mode: 0644]
edera-api/mvnw.cmd [new file with mode: 0644]
edera-api/notifications/pom.xml [new file with mode: 0644]
edera-api/notifications/src/main/java/applica/notifications/application/NotificationService.java [new file with mode: 0644]
edera-api/notifications/src/main/java/applica/notifications/application/UserActivatedEventHandler.java [new file with mode: 0644]
edera-api/notifications/src/main/java/applica/notifications/application/UserPasswordChangedEventHandler.java [new file with mode: 0644]
edera-api/notifications/src/main/java/applica/notifications/application/UserPasswordRecoveredEventHandler.java [new file with mode: 0644]
edera-api/notifications/src/main/java/applica/notifications/application/UserRegisteredEventHandler.java [new file with mode: 0644]
edera-api/notifications/src/main/java/applica/notifications/integration/DefaultMailMessageService.java [new file with mode: 0644]
edera-api/notifications/src/main/java/applica/notifications/integration/FreemarkerHelper.java [new file with mode: 0644]
edera-api/notifications/src/main/java/applica/notifications/integration/MailMessage.java [new file with mode: 0644]
edera-api/notifications/src/main/java/applica/notifications/integration/MailMessageService.java [new file with mode: 0644]
edera-api/notifications/src/main/resources/templates/activation.ftlh [new file with mode: 0644]
edera-api/notifications/src/main/resources/templates/passwordChanged.ftlh [new file with mode: 0644]
edera-api/notifications/src/main/resources/templates/recover.ftlh [new file with mode: 0644]
edera-api/notifications/src/main/resources/templates/registration.ftlh [new file with mode: 0644]
edera-api/pom.xml [new file with mode: 0644]
edera-api/scripts/create-infrastructure.sh [new file with mode: 0644]
edera-api/scripts/docker/Dockerfile [new file with mode: 0644]
edera-api/scripts/init-gcloud.sh [new file with mode: 0644]
edera-api/scripts/init-k8s.sh [new file with mode: 0644]
edera-api/scripts/kube/api.yml [new file with mode: 0644]
edera-api/scripts/kube/ingress.yml [new file with mode: 0644]
edera-api/scripts/set-env.sh [new file with mode: 0644]
edera-api/scripts/tf/main.tf [new file with mode: 0644]
edera-api/scripts/tf/mongo-atlas.tf [new file with mode: 0644]
edera-api/scripts/tf/provider.tf [new file with mode: 0644]
edera-api/scripts/tf/service-account.json.base64 [new file with mode: 0644]
edera-api/scripts/tf/terraform.tfstate [new file with mode: 0644]
edera-api/scripts/tf/variables.tf [new file with mode: 0644]

diff --git a/edera-api/.gitignore b/edera-api/.gitignore
new file mode 100644 (file)
index 0000000..a95772d
--- /dev/null
@@ -0,0 +1,40 @@
+api/HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Idea and Custom ###
+.flattened-pom.xml
+i18n-test.json
+.DS_Store
+
+/api/src/test/resources/out-*.docx
\ No newline at end of file
diff --git a/edera-api/README.md b/edera-api/README.md
new file mode 100644 (file)
index 0000000..c68414b
--- /dev/null
@@ -0,0 +1,6 @@
+# Leggimi
+
+Il seguente archetipo Ã¨ disponibile in due varianti disponibili su branch separati
+- `master`: espone un archetipo base con configurazione **MongoDB**
+- `feature/jpa`: espone un archetipo con configurazione **JPA**
+
diff --git a/edera-api/api/pom.xml b/edera-api/api/pom.xml
new file mode 100644 (file)
index 0000000..4473207
--- /dev/null
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>applica.app</groupId>
+               <artifactId>root</artifactId>
+               <version>${revision}${sha1}${changelist}</version>
+       </parent>
+
+       <artifactId>api</artifactId>
+       <version>${revision}${sha1}${changelist}</version>
+       <name>app</name>
+
+       <properties>
+               <snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
+       </properties>
+
+       <dependencies>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-actuator</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-data-jpa</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>com.h2database</groupId>
+                       <artifactId>h2</artifactId>
+                       <version>1.4.200</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-web</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>applica.modules</groupId>
+                       <artifactId>iam</artifactId>
+                       <version>${applica-iam.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>applica.modules</groupId>
+                       <artifactId>crud-mongodb</artifactId>
+                       <version>${applica-crud.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>applica.modules</groupId>
+                       <artifactId>fs-gcp</artifactId>
+                       <version>${applica-fs.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>applica.app</groupId>
+                       <artifactId>notifications</artifactId>
+                       <version>${revision}${sha1}${changelist}</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.projectlombok</groupId>
+                       <artifactId>lombok</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-test</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework.restdocs</groupId>
+                       <artifactId>spring-restdocs-mockmvc</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.springframework.security</groupId>
+                       <artifactId>spring-security-test</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.poi</groupId>
+                       <artifactId>poi</artifactId>
+                       <version>5.2.2</version>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.poi</groupId>
+                       <artifactId>poi-ooxml</artifactId>
+                       <version>5.2.2</version>
+               </dependency>
+       </dependencies>
+
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.springframework.boot</groupId>
+                               <artifactId>spring-boot-maven-plugin</artifactId>
+                               <configuration>
+                                       <mainClass>applica.app.Application</mainClass>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <goals>
+                                                       <goal>build-info</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+
+</project>
diff --git a/edera-api/api/src/main/java/applica/app/Application.java b/edera-api/api/src/main/java/applica/app/Application.java
new file mode 100644 (file)
index 0000000..a99054c
--- /dev/null
@@ -0,0 +1,39 @@
+package applica.app;
+
+import applica.crud.configuration.EnableCrud;
+import applica.fs.gcp.configuration.EnableGcpFs;
+import applica.iam.configuration.EnableIAM;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.mongodb.config.EnableMongoAuditing;
+import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+@SpringBootApplication
+@ComponentScan({
+        "applica.app.web",
+        "applica.app.services",
+        "applica.app.configuration",
+        "applica.app.domain",
+        "applica.app.tasks"
+})
+@EnableWebMvc
+@EnableWebSecurity
+@EnableCrud(packages = {
+        "applica.app.domain"
+})
+@EnableGcpFs
+@EnableIAM
+@EnableMongoAuditing
+@EnableMongoRepositories(basePackages = "applica.app.domain")
+@EnableScheduling
+public class Application {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/configuration/ApplicationConfiguration.java b/edera-api/api/src/main/java/applica/app/configuration/ApplicationConfiguration.java
new file mode 100644 (file)
index 0000000..f383c02
--- /dev/null
@@ -0,0 +1,52 @@
+package applica.app.configuration;
+
+import applica.app.web.crud.DefaultDataLayer;
+import applica.app.web.operations.decorators.CustomerFilterableFindDecorator;
+import applica.crud.operations.decorators.user.LoggedUserIdSaveDecorator;
+import applica.crud.query.CrudQueryConverter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.annotation.Scope;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class ApplicationConfiguration {
+    @Bean
+    public WebMvcConfigurer corsConfigurer(@Value("${cors.allowed-origins}") String[] allowedOrigins) {
+        return new WebMvcConfigurer() {
+            @Override
+            public void addCorsMappings(CorsRegistry registry) {
+                registry
+                        .addMapping("/**")
+                        .allowedMethods("POST", "PUT", "GET", "DELETE", "OPTIONS", "PATCH")
+                        .allowedOrigins(allowedOrigins)
+                ;
+            }
+        };
+    }
+
+    @Bean
+    @Scope("prototype")
+    public LoggedUserIdSaveDecorator loggedUserIdSaveDecorator() {
+        return new LoggedUserIdSaveDecorator();
+    }
+
+    @Bean
+    @Scope("prototype")
+    public CustomerFilterableFindDecorator customerFilterableFindDecorator() {
+        return new CustomerFilterableFindDecorator();
+    }
+
+    @Bean
+    @Scope("prototype")
+    @Primary
+    public DefaultDataLayer<?> dataLayer(MongoTemplate mongoTemplate, CrudQueryConverter<Query> queryConverter) {
+        return new DefaultDataLayer<>(mongoTemplate, queryConverter);
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/configuration/AuditLogConfiguration.java b/edera-api/api/src/main/java/applica/app/configuration/AuditLogConfiguration.java
new file mode 100644 (file)
index 0000000..7fbca21
--- /dev/null
@@ -0,0 +1,17 @@
+package applica.app.configuration;
+
+import applica.app.domain.audit.AuditLogMessageBuilder;
+import applica.app.domain.audit.AuditLogMessageBuilderFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+@Configuration
+@SuppressWarnings("unused")
+public class AuditLogConfiguration {
+    @Bean
+    public AuditLogMessageBuilderFactory logExplainerFactory(List<AuditLogMessageBuilder> explainers) {
+        return new AuditLogMessageBuilderFactory(explainers);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/configuration/GeoAutoConfiguration.java b/edera-api/api/src/main/java/applica/app/configuration/GeoAutoConfiguration.java
new file mode 100644 (file)
index 0000000..c797e08
--- /dev/null
@@ -0,0 +1,32 @@
+package applica.app.configuration;
+
+import applica.app.services.GeoInitializationService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.mongodb.core.MongoTemplate;
+
+@ComponentScan({
+        "applica.app.services"
+})
+public class GeoAutoConfiguration {
+
+    @Bean
+    @ConditionalOnMissingBean
+    public GeoInitializationService geoInitializationService(
+            @Value("${geo.csv.nation}") String nationCsvPath,
+            @Value("${geo.csv.region}") String regionCsvPath,
+            @Value("${geo.csv.province}") String provinceCsvPath,
+            @Value("${geo.csv.city}") String cityCsvPath,
+            MongoTemplate mongoTemplate
+    ) {
+        return new GeoInitializationService(
+                nationCsvPath,
+                regionCsvPath,
+                provinceCsvPath,
+                cityCsvPath,
+                mongoTemplate
+        );
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/configuration/MLConfiguration.java b/edera-api/api/src/main/java/applica/app/configuration/MLConfiguration.java
new file mode 100644 (file)
index 0000000..df30aea
--- /dev/null
@@ -0,0 +1,15 @@
+package applica.app.configuration;
+
+import applica.app.ml.MLClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@SuppressWarnings("unused")
+public class MLConfiguration {
+    @Bean
+    public MLClient mlClient(@Value("${ml.endpoint}") String endpoint) {
+        return new MLClient(endpoint);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/configuration/NotificationsConfiguration.java b/edera-api/api/src/main/java/applica/app/configuration/NotificationsConfiguration.java
new file mode 100644 (file)
index 0000000..168c46e
--- /dev/null
@@ -0,0 +1,76 @@
+package applica.app.configuration;
+
+import applica.iam.sdk.IAMService;
+import applica.integration.events.ApplicationDomainEventsHandler;
+import applica.integration.events.DomainEventsHandler;
+import applica.notifications.application.UserActivatedEventHandler;
+import applica.notifications.application.UserPasswordChangedEventHandler;
+import applica.notifications.application.UserPasswordRecoveredEventHandler;
+import applica.notifications.application.UserRegisteredEventHandler;
+import applica.notifications.integration.DefaultMailMessageService;
+import applica.notifications.integration.FreemarkerHelper;
+import applica.notifications.integration.MailMessageService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@SuppressWarnings("unused")
+@Configuration
+public class NotificationsConfiguration {
+
+    @Bean
+    public FreemarkerHelper freemarkerHelper(@Value("${mail.templates.path}") String templatesPath) {
+        return new FreemarkerHelper(templatesPath);
+    }
+
+    @Bean
+    public MailMessageService mailMessageService() {
+        return new DefaultMailMessageService();
+    }
+    @Bean
+    public UserRegisteredEventHandler profileCreatedEventHandler(
+            MailMessageService mailMessageService,
+            IAMService iamService
+    ) {
+        return new UserRegisteredEventHandler(mailMessageService, iamService);
+    }
+
+    @Bean
+    public UserPasswordChangedEventHandler userPasswordChangedEventHandler(
+            MailMessageService mailMessageService,
+            IAMService iamService
+    ) {
+        return new UserPasswordChangedEventHandler(mailMessageService, iamService);
+    }
+
+    @Bean
+    public UserPasswordRecoveredEventHandler userPasswordRecoveredEventHandler(
+            MailMessageService mailMessageService,
+            IAMService iamService
+    ) {
+        return new UserPasswordRecoveredEventHandler(mailMessageService, iamService);
+    }
+
+    @Bean
+    public UserActivatedEventHandler userActivatedEventHandler(
+            MailMessageService mailMessageService,
+            IAMService iamService
+    ) {
+        return new UserActivatedEventHandler(mailMessageService, iamService);
+    }
+
+    @Bean
+    public DomainEventsHandler domainEventsHandler(
+            UserRegisteredEventHandler userRegisteredEventHandler,
+            UserPasswordChangedEventHandler userPasswordChangedEventHandler,
+            UserPasswordRecoveredEventHandler userPasswordRecoveredEventHandler,
+            UserActivatedEventHandler userActivatedEventHandler
+    ) {
+        ApplicationDomainEventsHandler domainEventsListener = new ApplicationDomainEventsHandler();
+        domainEventsListener.registerHandler(userRegisteredEventHandler);
+        domainEventsListener.registerHandler(userPasswordChangedEventHandler);
+        domainEventsListener.registerHandler(userPasswordRecoveredEventHandler);
+        domainEventsListener.registerHandler(userActivatedEventHandler);
+        return domainEventsListener;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/configuration/SecurityConfiguration.java b/edera-api/api/src/main/java/applica/app/configuration/SecurityConfiguration.java
new file mode 100644 (file)
index 0000000..94c86af
--- /dev/null
@@ -0,0 +1,61 @@
+package applica.app.configuration;
+
+import applica.app.web.filters.JwtAuthenticationFilter;
+import applica.iam.sdk.IAMService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+@SuppressWarnings("unused")
+@EnableWebSecurity
+@EnableMethodSecurity
+@Configuration
+public class SecurityConfiguration {
+    @Bean
+    public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
+        http
+                .cors()
+                .and()
+
+                .csrf().disable()
+                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                .and()
+
+                .authorizeHttpRequests()
+                .requestMatchers(
+                        "/actuator/**",
+                        "/attachments/**",
+                        "/iam/**",
+                        "/i18n/**",
+                        "/track",
+                        "/docx/helper/**",
+                        "/docx/generate/**"
+                ).permitAll()
+                .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
+                .requestMatchers("/**").authenticated()
+                .and()
+                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
+
+                .headers()
+                .contentSecurityPolicy("script-src 'self'")
+                .and()
+                .frameOptions()
+                .sameOrigin()
+                .httpStrictTransportSecurity()
+                .includeSubDomains(true);
+
+        return http.build();
+    }
+
+    @Bean
+    public JwtAuthenticationFilter jwtAuthenticationFilter(IAMService iamService) {
+        return new JwtAuthenticationFilter(iamService);
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/Category.java b/edera-api/api/src/main/java/applica/app/domain/Category.java
new file mode 100644 (file)
index 0000000..bdf3cb7
--- /dev/null
@@ -0,0 +1,20 @@
+package applica.app.domain;
+
+import applica.crud.annotations.CrudEntity;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@EqualsAndHashCode
+@CrudEntity("category")
+public class Category {
+    @EqualsAndHashCode.Include
+    String id;
+    @NotEmpty
+    String description;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/Device.java b/edera-api/api/src/main/java/applica/app/domain/Device.java
new file mode 100644 (file)
index 0000000..8bf08ea
--- /dev/null
@@ -0,0 +1,25 @@
+package applica.app.domain;
+
+import applica.crud.annotations.CrudEntity;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * @author Roberto Conte Rosito
+ *
+ * Identifica un singolo dispositivo riconosciuto.
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@CrudEntity("device")
+public class Device {
+    String id;
+    String code;
+    String secret;
+    Date registrationDate;
+}
+
diff --git a/edera-api/api/src/main/java/applica/app/domain/DeviceDataLayer.java b/edera-api/api/src/main/java/applica/app/domain/DeviceDataLayer.java
new file mode 100644 (file)
index 0000000..fb11860
--- /dev/null
@@ -0,0 +1,82 @@
+package applica.app.domain;
+
+import applica.crud.datalayer.DataLayer;
+import applica.crud.model.Result;
+import applica.crud.query.CrudQuery;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.requests.*;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.util.Optional;
+
+import static applica.app.mapping.Mapper.deviceViewToDevice;
+
+@Component
+public class DeviceDataLayer implements DataLayer<Device> {
+    private IAMService iamService;
+
+    public DeviceDataLayer(IAMService iamService) {
+        this.iamService = iamService;
+    }
+
+    @Override
+    public Optional<Device> get(Object id) {
+        var getDeviceRequest = new GetDeviceByIdRequest(id.toString());
+        var device = iamService.getDeviceById(getDeviceRequest);
+        return Optional.ofNullable(deviceViewToDevice(device.getDevice()));
+    }
+
+    @Override
+    public Result<Device> find(CrudQuery query) {
+        var keyword = query.getKeyword();
+        var searchRequest = new SearchDevicesRequest();
+        if (StringUtils.hasLength(keyword)) {
+            searchRequest.setKeyword(keyword);
+        }
+        var pageable = query.getPageable();
+        if (pageable != null) {
+            searchRequest.setPageable(pageable);
+        }
+
+        var devices = iamService.searchDevices(searchRequest);
+        return new Result<>(
+                devices.getDevices().stream().map(deviceView -> deviceViewToDevice(deviceView)).toList(),
+                devices.getDevices().getTotalElements(),
+                devices.getDevices().getPageable().getPageNumber(),
+                devices.getDevices().getPageable().getPageSize()
+        );
+    }
+
+    @Override
+    public void save(Device entity) {
+        if (StringUtils.hasLength(entity.getId())) {
+            var getDeviceByIdRequest = new GetDeviceByCodeRequest(entity.getId().toString());
+            var getDeviceByIdResponse = iamService.getDeviceByCode(getDeviceByIdRequest);
+            if (getDeviceByIdResponse.getDevice() != null) {
+                var updateDeviceRequest = new UpdateDeviceRequest(
+                        entity.getId(),
+                        entity.getCode(),
+                        entity.getSecret()
+                );
+                iamService.updateDevice(updateDeviceRequest);
+            }
+        } else {
+            var registerDeviceRequest = new RegisterDeviceRequest(entity.getCode());
+            var registerDeviceResponse = iamService.registerDevice(registerDeviceRequest);
+            entity.setId(registerDeviceResponse.getDevice().getId());
+            entity.setSecret(registerDeviceResponse.getDevice().getSecret());
+        }
+    }
+
+    @Override
+    public void delete(Object id) {
+        var deleteRequest = new DeleteDeviceRequest(id.toString());
+        iamService.deleteDevice(deleteRequest);
+    }
+
+    @Override
+    public Class<Device> getEntityType() {
+        return Device.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/Gallery.java b/edera-api/api/src/main/java/applica/app/domain/Gallery.java
new file mode 100644 (file)
index 0000000..98453e7
--- /dev/null
@@ -0,0 +1,24 @@
+package applica.app.domain;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.attachments.Attachment;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+
+import java.util.List;
+
+@CrudEntity("gallery")
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Gallery {
+
+    String id;
+    String description;
+    List<Attachment> images;
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/I18nMessage.java b/edera-api/api/src/main/java/applica/app/domain/I18nMessage.java
new file mode 100644 (file)
index 0000000..6e79605
--- /dev/null
@@ -0,0 +1,119 @@
+package applica.app.domain;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * @author Roberto Conte Rosito
+ */
+@Data
+@NoArgsConstructor
+@CrudEntity(value = "i18n-message", listPermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" })
+@ToString
+@Document
+public class I18nMessage {
+    private static Log log = LogFactory.getLog(I18nMessage.class);
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    @Keyword
+    String id;
+    @Keyword
+    String code;
+    @Keyword
+    String lang;
+    @Keyword
+    String text;
+
+    public I18nMessage(String lang, String code, String text) {
+        this.lang = lang;
+        this.code = code;
+        this.text = text;
+        this.id = String.format("%s.%s", lang, code);
+    }
+
+    @JsonIgnore
+    public boolean isValid() {
+        return lang != null && code != null && text != null;
+    }
+
+    @JsonIgnore
+    public boolean isEmpty() {
+        var isNull = lang == null && code == null && text == null;
+        return isNull || text.equals(code);
+    }
+
+    public static I18nMessage create(String lang, String code, String text) {
+        return new I18nMessage(lang, code, text);
+    }
+
+    private static void writeJson(List<I18nMessage> messages, String csvPath) {
+        var objectMapper = new ObjectMapper();
+        objectMapper.getFactory().configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, false);
+        objectMapper.getFactory().configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true);
+        objectMapper.getFactory().configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
+        objectMapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, false);
+        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+        var jsonFile = new File(csvPath);
+
+        try {
+            messages.forEach(m -> m.setId(String.format("%s.%s", m.getLang(), m.getCode())));
+            messages.sort((m1, m2) -> {
+                var langCompare = m1.getLang().compareTo(m2.getLang());
+                if (langCompare == 0) {
+                    return m1.getCode().compareTo(m2.getCode());
+                }
+                return langCompare;
+            });
+            objectMapper.writerWithDefaultPrettyPrinter().writeValue(jsonFile, messages);
+        } catch (Exception e) {
+            log.error(e);
+        }
+    }
+
+    private static List<I18nMessage> readJson(InputStream jsonStream) {
+        var objectMapper = new ObjectMapper();
+        try {
+            var messages = objectMapper.readValue(jsonStream, I18nMessage[].class);
+
+            return List.of(messages);
+        } catch (Exception e) {
+            log.error(e);
+            return null;
+        }
+    }
+
+    public static void persistAsResource(List<I18nMessage> messages) {
+        persistAsResource(messages, "i18n.json");
+    }
+
+    public static void persistAsResource(List<I18nMessage> messages, String filename) {
+        var jarPath = I18nMessage.class.getProtectionDomain().getCodeSource().getLocation().getPath();
+        var cleanPath = jarPath.replace("/target/classes/", "");
+        var resourcePath = String.format("%s/src/main/resources/%s", cleanPath, filename);
+        writeJson(messages, resourcePath);
+    }
+
+    public static List<I18nMessage> loadFromResource() {
+        var stream = I18nMessage.class.getResourceAsStream("/i18n.json");
+        return readJson(stream);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/I18nMessageRepository.java b/edera-api/api/src/main/java/applica/app/domain/I18nMessageRepository.java
new file mode 100644 (file)
index 0000000..d2cd6ac
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface I18nMessageRepository extends CrudRepository<I18nMessage, String> {
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/Notification.java b/edera-api/api/src/main/java/applica/app/domain/Notification.java
new file mode 100644 (file)
index 0000000..42821a1
--- /dev/null
@@ -0,0 +1,76 @@
+package applica.app.domain;
+
+import applica.crud.annotations.CrudEntity;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.springframework.data.domain.AbstractAggregateRoot;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+import org.springframework.util.StringUtils;
+
+import java.util.Date;
+import java.util.UUID;
+
+/**
+ * @author Roberto Conte Rosito
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@CrudEntity(value = "notification", completePermissionsRoles = {"ROLE_ADMIN", "ROLE_USER", "ROLE_MAINTAINER", "ROLE_OPERATOR", "ROLE_CUSTOMER"})
+@Entity
+public class Notification extends AbstractAggregateRoot<Notification> {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    String userId;
+
+    String title;
+
+    String content;
+
+    String resource;
+
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
+    Date created;
+
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
+    Date readed;
+
+    public boolean isNew() {
+        return !StringUtils.hasLength(id);
+    }
+
+    public Notification merge(Notification notification) {
+        if (notification == null) {
+            return this;
+        }
+
+        this.setTitle(notification.getTitle());
+        this.setContent(notification.getContent());
+        this.setResource(notification.getResource());
+        this.setUserId(notification.getUserId());
+        this.setCreated(notification.getCreated());
+
+        return this;
+    }
+
+    public static Notification create(String userId, String title, String content, String resource) {
+        return new Notification(
+                UUID.randomUUID().toString(),
+                userId,
+                title,
+                content,
+                resource,
+                new Date(),
+                null);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/NotificationDataLayer.java b/edera-api/api/src/main/java/applica/app/domain/NotificationDataLayer.java
new file mode 100644 (file)
index 0000000..7fe51c3
--- /dev/null
@@ -0,0 +1,59 @@
+package applica.app.domain;
+
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.builders.CrudQueryBuilder;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component
+public class NotificationDataLayer implements DataLayer<Notification> {
+    final DataLayer<Notification> dataLayer;
+
+    public NotificationDataLayer(CrudFactory crudFactory) {
+        dataLayer = crudFactory.createDataLayer(Notification.class);
+    }
+    @Override
+    public Optional<Notification> get(Object id) {
+        return dataLayer.get(id);
+    }
+
+    @Override
+    public Result<Notification> find(CrudQuery query) {
+        var queryBuilder = CrudQueryBuilder.build(query);
+        var readed = queryBuilder.popFilter("readed").orElse(null);
+        if (readed != null) {
+            queryBuilder.setNullCheck(false);
+            if (readed.getValue().equals(true)) {
+                queryBuilder.ne("readAt", null);
+            } else {
+                queryBuilder.eq("readAt", null);
+            }
+        }
+        return dataLayer.find(queryBuilder.get());
+    }
+
+    @Override
+    public void save(Notification entity) {
+        if (!entity.isNew()) {
+            var old = dataLayer.get(entity.getId()).orElse(null);
+            if (old != null) {
+                entity = entity.merge(old);
+            }
+        }
+        dataLayer.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+        dataLayer.delete(id);
+    }
+
+    @Override
+    public Class<Notification> getEntityType() {
+        return Notification.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/SubCategory.java b/edera-api/api/src/main/java/applica/app/domain/SubCategory.java
new file mode 100644 (file)
index 0000000..43b9ac1
--- /dev/null
@@ -0,0 +1,37 @@
+package applica.app.domain;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.query.Keyword;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+@CrudEntity("subCategory")
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@Entity
+public class SubCategory {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+    @NotNull
+    @JsonMaterialize(entityType = Category.class, destination = "category")
+    String categoryId;
+
+    @NotEmpty
+    @Keyword
+    String description;
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/User.java b/edera-api/api/src/main/java/applica/app/domain/User.java
new file mode 100644 (file)
index 0000000..cac8fc1
--- /dev/null
@@ -0,0 +1,43 @@
+package applica.app.domain;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.Image;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.Date;
+import java.util.List;
+
+@CrudEntity(value = "user", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN" })
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+public class User {
+
+    String id;
+
+    @NotEmpty
+    String email;
+
+    @NotEmpty
+    String name;
+
+    @Image(nodeProperty = "_image", directory = "users")
+    String image;
+
+    boolean active;
+
+    Date registrationDate;
+
+    @NotEmpty
+    List<String> roles;
+    String password;
+
+    String pinCode;
+
+    String customerId;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/UsersDataLayer.java b/edera-api/api/src/main/java/applica/app/domain/UsersDataLayer.java
new file mode 100644 (file)
index 0000000..5171e44
--- /dev/null
@@ -0,0 +1,180 @@
+package applica.app.domain;
+
+import applica.crud.datalayer.DataLayer;
+import applica.crud.model.Result;
+import applica.crud.operations.OperationException;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.Filter;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.ResponseCodes;
+import applica.iam.sdk.requests.*;
+import org.springframework.data.domain.Range;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+
+import static applica.app.mapping.Mapper.userViewToUser;
+import static applica.crud.model.ErrorCodes.OK;
+
+@Component
+public class UsersDataLayer implements DataLayer {
+
+    final IAMService iamService;
+
+    public UsersDataLayer(IAMService iamService) {
+        this.iamService = iamService;
+    }
+
+    @Override
+    public Optional<Object> get(Object id) {
+        var response = iamService.getUserById(new GetUserByIdRequest(id.toString()));
+        return Optional.ofNullable(userViewToUser(response.getUser()));
+    }
+
+    @Override
+    public Result<Object> find(CrudQuery query) throws OperationException {
+        var registrationDateFilter = (Long) query.getFilterValue("registrationDate");
+        Date registrationDateFrom = null;
+        Date registrationDateTo = null;
+        if (registrationDateFilter != null) {
+            var calendar = GregorianCalendar.getInstance();
+            if (Objects.equals(Filter.GTE, query.getFilterType("registrationDate"))) {
+                registrationDateFrom = new Date(registrationDateFilter);
+                calendar.setTime(registrationDateFrom);
+                calendar.add(Calendar.YEAR, 1000);
+                registrationDateTo = calendar.getTime();
+            } else {
+                registrationDateTo = new Date(registrationDateFilter);
+                calendar.setTime(registrationDateTo);
+                calendar.add(Calendar.YEAR, -1000);
+                registrationDateFrom = calendar.getTime();
+            }
+        }
+
+        Boolean active = (Boolean) query.getFilterValue("active");
+        String keyword = query.getKeyword();
+        String[] possibleKeywords = new String[]{"q", "keyword"};
+        for (String possibleKeyword : possibleKeywords) {
+            String value = (String) query.getFilterValue(possibleKeyword);
+            if (StringUtils.hasLength(value)) {
+                keyword = value;
+                break;
+            }
+        }
+
+        Map profile = null;
+        var customerId = query.popFilter("customerId");
+        if (customerId.isPresent()) {
+            profile = Map.of("customerId", customerId.get().getValue());
+        }
+
+        var response = iamService.searchUsers(new SearchUsersRequest(
+                keyword,
+                (String) query.getFilterValue("mail"),
+                registrationDateFrom != null ? Range.closed(registrationDateFrom, registrationDateTo) : null,
+                active,
+                (String) query.getFilterValue("role"),
+                profile,
+                query.getPageable()
+        ));
+
+        if (!OK.equals(response.getResponseCode())) {
+            throw new OperationException(response.getResponseCode());
+        }
+
+        return new Result<>(
+                response.getUsers().stream().map(u -> (Object) userViewToUser(u)).toList(),
+                response.getUsers().getTotalElements(),
+                response.getUsers().getPageable().getPageNumber(),
+                response.getUsers().getPageable().getPageSize()
+        );
+    }
+
+    @Override
+    @Transactional
+    public void save(Object entity) throws OperationException {
+        var user = (User) entity;
+
+        var profile = new HashMap<String, String>();
+        profile.put("name", user.getName());
+        profile.put("image", StringUtils.hasLength(user.getImage()) ? user.getImage() : "");
+        profile.put("customerId", user.getCustomerId());
+
+        if (user.getId() == null) {
+            register(user, profile);
+        } else {
+            update(user, profile);
+        }
+    }
+
+    private void update(User user, HashMap<String, String> profile) throws OperationException {
+        iamService.updateProfile(new UpdateProfileRequest(user.getId(), profile));
+        var response = iamService.getUserById(new GetUserByIdRequest(user.getId()));
+        if (!OK.equals(response.getResponseCode())) {
+            throw new OperationException(response.getResponseCode());
+        }
+        if (!user.getRoles().equals(response.getUser().getRoles())) {
+            var updateRolesResponse = iamService.updateRoles(new UpdateRolesRequest(user.getId(), user.getRoles()));
+            if (!OK.equals(updateRolesResponse.getResponseCode())) {
+                throw new OperationException(updateRolesResponse.getResponseCode());
+            }
+        }
+        if (user.getPassword() != null && !user.getPassword().isEmpty()) {
+            var resetPasswordResponse = iamService.resetPassword(new ResetPasswordRequest(user.getId(), user.getPassword()));
+            if (!OK.equals(resetPasswordResponse.getResponseCode())) {
+                throw new OperationException(resetPasswordResponse.getResponseCode());
+            }
+        }
+        if (user.getPinCode() != null) {
+            var resetPinCodeResponse = iamService.resetPin(new ResetPinRequest(user.getId(), user.getPinCode()));
+            if (!OK.equals(resetPinCodeResponse.getResponseCode())) {
+                throw new OperationException(resetPinCodeResponse.getResponseCode());
+            }
+        }
+
+        if (user.isActive() == response.getUser().isActive()) {
+            return;
+        }
+
+        if (user.isActive()) {
+            var enableUserResponse = iamService.enableUser(new EnableUserRequest(user.getId()));
+            if (!OK.equals(enableUserResponse.getResponseCode())) {
+                throw new OperationException(enableUserResponse.getResponseCode());
+            }
+        } else {
+            var disableUserResponse = iamService.disableUser(new DisableUserRequest(user.getId()));
+            if (!OK.equals(disableUserResponse.getResponseCode())) {
+                throw new OperationException(disableUserResponse.getResponseCode());
+            }
+        }
+    }
+
+    private void register(User user, HashMap<String, String> profile) throws OperationException {
+        var response = iamService.register(new RegistrationRequest(
+                user.getEmail(),
+                null,
+                user.getRoles(),
+                user.isActive(),
+                profile)
+        );
+        if (!ResponseCodes.OK.equals(response.getResponseCode())) {
+            throw new OperationException(response.getResponseCode());
+        }
+        user.setId(response.getUserId());
+        if (StringUtils.hasLength(user.getPinCode())) {
+            iamService.resetPin(new ResetPinRequest(user.getId(), user.getPinCode()));
+        }
+    }
+
+    @Override
+    public void delete(Object id) {
+        iamService.deleteUser(new DeleteUserRequest(id.toString()));
+    }
+
+    @Override
+    public Class<?> getEntityType() {
+        return User.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/AuditLog.java b/edera-api/api/src/main/java/applica/app/domain/audit/AuditLog.java
new file mode 100644 (file)
index 0000000..7a8ff54
--- /dev/null
@@ -0,0 +1,94 @@
+package applica.app.domain.audit;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.query.Keyword;
+import applica.iam.sdk.readmodel.UserView;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.google.gson.Gson;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.Data;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+@Data
+@CrudEntity(value = "audit-log", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN" })
+public class AuditLog {
+    final static String[] skipList = new String[] {
+            "/api/actuator/health",
+            "/api/entities/audit-log",
+            "/api/entities/notification/find",
+            "/api/entities/user/find",
+            "/api/entities/i18n-message",
+            "/api/entities/city",
+            "/api/entities/nation",
+            "/api/entities/province",
+            "/api/entities/region",
+            "/api/iam/login",
+            "/api/iam/impersonate",
+            "/api/iam/token-login",
+            "/api/iam/fresh-token",
+            "/api/profile/",
+            "/api/i18n/messages",
+    };
+    final static Gson gson = new com.google.gson.Gson();
+
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    String userId;
+    @Keyword
+    String method;
+    @Keyword
+    String url;
+    String requestBody;
+    String responseBody;
+    @Keyword
+    String userAgent;
+    @Keyword
+    String ip;
+    AuditLogMessage message;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime created;
+
+    public static AuditLog create(
+            HttpServletRequest request,
+            String requestBody,
+            String responseBody,
+            Optional<UserView> loggedUser,
+            AuditLogMessageBuilderFactory auditLogMessageBuilderFactory) {
+        var auditLog = new AuditLog();
+        auditLog.setIp(request.getRemoteAddr());
+        auditLog.setUrl(request.getRequestURI());
+        auditLog.setMethod(request.getMethod());
+        auditLog.setUserAgent(request.getHeader("user-agent"));
+        auditLog.setCreated(LocalDateTime.now());
+        auditLog.setRequestBody(requestBody);
+        auditLog.setResponseBody(responseBody);
+        auditLog.setMessage(auditLogMessageBuilderFactory.build(auditLog));
+        if (loggedUser.isPresent()) {
+            auditLog.setUserId(loggedUser.get().getId());
+        }
+        return auditLog;
+    }
+
+
+    public static boolean skip(HttpServletRequest request) {
+        var url = request.getRequestURI();
+        for (String urlToSkip : skipList) {
+            if (url.startsWith(urlToSkip)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/AuditLogMessage.java b/edera-api/api/src/main/java/applica/app/domain/audit/AuditLogMessage.java
new file mode 100644 (file)
index 0000000..6f95219
--- /dev/null
@@ -0,0 +1,15 @@
+package applica.app.domain.audit;
+
+import lombok.Data;
+
+import java.util.HashMap;
+
+@Data
+public class AuditLogMessage {
+    String message;
+    HashMap<String, Object> args = new HashMap<>();
+
+    public void setArg(String key, Object value) {
+        args.put(key, value);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/AuditLogMessageBuilder.java b/edera-api/api/src/main/java/applica/app/domain/audit/AuditLogMessageBuilder.java
new file mode 100644 (file)
index 0000000..d8f2865
--- /dev/null
@@ -0,0 +1,14 @@
+package applica.app.domain.audit;
+
+/**
+ * Definisce una interfaccia da implementare qualora si voglia provare a interpretare
+ * un log di audit secondo specifiche regole di parsing.
+ */
+public interface AuditLogMessageBuilder {
+    /**
+     * Restituisce la spiegazione del log di audit o NULL se non Ã¨ possibile spiegarlo.
+     * @param auditLog il log di audit da spiegare
+     * @return la spiegazione del log di audit o NULL se non Ã¨ possibile spiegarlo
+     */
+    AuditLogMessage build(AuditLog auditLog);
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/AuditLogMessageBuilderFactory.java b/edera-api/api/src/main/java/applica/app/domain/audit/AuditLogMessageBuilderFactory.java
new file mode 100644 (file)
index 0000000..5416f86
--- /dev/null
@@ -0,0 +1,21 @@
+package applica.app.domain.audit;
+
+import java.util.List;
+
+public class AuditLogMessageBuilderFactory {
+    private List<AuditLogMessageBuilder> auditLogMessageBuilders;
+
+    public AuditLogMessageBuilderFactory(List<AuditLogMessageBuilder> auditLogMessageBuilders) {
+        this.auditLogMessageBuilders = auditLogMessageBuilders;
+    }
+
+    public AuditLogMessage build(AuditLog auditLog) {
+        for (var explainer : auditLogMessageBuilders) {
+            var explanation = explainer.build(auditLog);
+            if (explanation != null) {
+                return explanation;
+            }
+        }
+        return null;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudAuditLogMessageBuilder.java b/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudAuditLogMessageBuilder.java
new file mode 100644 (file)
index 0000000..b11bb0e
--- /dev/null
@@ -0,0 +1,41 @@
+package applica.app.domain.audit.builders;
+
+import applica.app.domain.audit.AuditLog;
+import applica.app.domain.audit.AuditLogMessage;
+import applica.app.domain.audit.AuditLogMessageBuilder;
+
+/**
+ * Definisce una classe base da poter utilizzare per la predisposizione di un builder che lavora con entità CRUD.
+ * Internamente espone già una serie di metodi utili a lavorare in questo contesto.
+ */
+public abstract class CrudAuditLogMessageBuilder implements AuditLogMessageBuilder {
+    public boolean isCrudEntity(AuditLog auditLog) {
+        return auditLog.getUrl().matches("/api/entities/[^/]+.*");
+    }
+
+    public String getI18nEntityName(AuditLog auditLog) {
+        var url = auditLog.getUrl();
+        var entityName = url.split("/")[3];
+        var i18nEntityName = String.format("resources.entities/%s.name", entityName);
+
+        return i18nEntityName;
+    }
+
+    /**
+     * L'unico metodo che devi implementare per gestire un messaggio relativo alla CRUD.
+     * Questo metodo sarà chiamato solo se il log di audit Ã¨ relativo a una entità CRUD.
+     * @param auditLog il log di audit da spiegare
+     * @return la spiegazione del log di audit o NULL se non Ã¨ possibile spiegarlo
+     */
+    public abstract AuditLogMessage buildInternal(AuditLog auditLog);
+
+    @Override
+    public AuditLogMessage build(AuditLog auditLog) {
+        if (isCrudEntity(auditLog)) {
+            return buildInternal(auditLog);
+        }
+        else {
+            return null;
+        }
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudCreateAuditLogMessageBuilder.java b/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudCreateAuditLogMessageBuilder.java
new file mode 100644 (file)
index 0000000..2c58557
--- /dev/null
@@ -0,0 +1,41 @@
+package applica.app.domain.audit.builders;
+
+import applica.app.domain.audit.AuditLog;
+import applica.app.domain.audit.AuditLogMessage;
+import com.google.gson.Gson;
+import com.google.gson.internal.LinkedTreeMap;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.util.HashMap;
+
+@Component
+@SuppressWarnings("unused")
+public class CrudCreateAuditLogMessageBuilder extends CrudAuditLogMessageBuilder {
+    private Gson gson = new Gson();
+    @Override
+    public AuditLogMessage buildInternal(AuditLog auditLog) {
+        var requestBody = auditLog.getRequestBody();
+        if (!StringUtils.hasLength(requestBody)) {
+            return null;
+        }
+
+        var jsonData = gson.fromJson(requestBody, HashMap.class);
+        var entity = (LinkedTreeMap<String, Object>)jsonData.get("entity");
+        if (entity == null) {
+            return null;
+        }
+        var id = (String)entity.get("id");
+        if (StringUtils.hasLength(id)) {
+            return null;
+        }
+
+        var method = auditLog.getMethod();
+        var i18nEntityName = getI18nEntityName(auditLog);
+        var message = new AuditLogMessage();
+        message.setMessage("ra.audit-log.entities/create");
+        message.setArg("entity", i18nEntityName);
+
+        return message;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudDeleteAuditLogMessageBuilder.java b/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudDeleteAuditLogMessageBuilder.java
new file mode 100644 (file)
index 0000000..50225f1
--- /dev/null
@@ -0,0 +1,35 @@
+package applica.app.domain.audit.builders;
+
+import applica.app.domain.audit.AuditLog;
+import applica.app.domain.audit.AuditLogMessage;
+import com.google.gson.Gson;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+
+@Component
+@SuppressWarnings("unused")
+public class CrudDeleteAuditLogMessageBuilder extends CrudAuditLogMessageBuilder {
+    private Gson gson = new Gson();
+
+    @Override
+    public AuditLogMessage buildInternal(AuditLog auditLog) {
+        var url = auditLog.getUrl();
+        var isDelete = url.endsWith("/delete");
+        if (!isDelete) {
+            return null;
+        }
+
+        var method = auditLog.getMethod();
+        var requestBody = auditLog.getRequestBody();
+        var i18nEntityName = getI18nEntityName(auditLog);
+        var message = new AuditLogMessage();
+        var jsonData = gson.fromJson(requestBody, HashMap.class);
+        var ids = (String)jsonData.get("ids");
+        message.setMessage(String.format("ra.audit-log.entities/%s", method.toLowerCase()));
+        message.setArg("entity", i18nEntityName);
+        message.setArg("ids", ids);
+
+        return message;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudEditAuditLogMessageBuilder.java b/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudEditAuditLogMessageBuilder.java
new file mode 100644 (file)
index 0000000..63b2c4f
--- /dev/null
@@ -0,0 +1,43 @@
+package applica.app.domain.audit.builders;
+
+import applica.app.domain.audit.AuditLog;
+import applica.app.domain.audit.AuditLogMessage;
+import com.google.gson.Gson;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+@SuppressWarnings("unused")
+public class CrudEditAuditLogMessageBuilder extends CrudAuditLogMessageBuilder {
+    private Gson gson = new Gson();
+    @Override
+    public AuditLogMessage buildInternal(AuditLog auditLog) {
+        var requestBody = auditLog.getRequestBody();
+        if (!StringUtils.hasLength(requestBody)) {
+            return null;
+        }
+
+        var jsonData = gson.fromJson(requestBody, HashMap.class);
+        var entity = (Map)jsonData.get("entity");
+
+        if (entity == null) {
+            return null;
+        }
+        var id = (String)entity.get("id");
+        if (!StringUtils.hasLength(id)) {
+            return null;
+        }
+
+        var method = auditLog.getMethod();
+        var i18nEntityName = getI18nEntityName(auditLog);
+        var message = new AuditLogMessage();
+        message.setMessage("ra.audit-log.entities/edit");
+        message.setArg("entity", i18nEntityName);
+        message.setArg("id", id);
+
+        return message;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudFindAuditLogMessageBuilder.java b/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudFindAuditLogMessageBuilder.java
new file mode 100644 (file)
index 0000000..99328a7
--- /dev/null
@@ -0,0 +1,23 @@
+package applica.app.domain.audit.builders;
+
+import applica.app.domain.audit.AuditLog;
+import applica.app.domain.audit.AuditLogMessage;
+import org.springframework.stereotype.Component;
+
+@Component
+@SuppressWarnings("unused")
+public class CrudFindAuditLogMessageBuilder extends CrudAuditLogMessageBuilder {
+    @Override
+    public AuditLogMessage buildInternal(AuditLog auditLog) {
+        var url = auditLog.getUrl();
+        if (!url.endsWith("/find")) {
+            return null;
+        }
+
+        var i18nEntityName = getI18nEntityName(auditLog);
+        var message = new AuditLogMessage();
+        message.setMessage("ra.audit-log.entities/find");
+        message.setArg("entity", i18nEntityName);
+        return message;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudViewAuditLogMessageBuilder.java b/edera-api/api/src/main/java/applica/app/domain/audit/builders/CrudViewAuditLogMessageBuilder.java
new file mode 100644 (file)
index 0000000..db2f0ff
--- /dev/null
@@ -0,0 +1,29 @@
+package applica.app.domain.audit.builders;
+
+import applica.app.domain.audit.AuditLog;
+import applica.app.domain.audit.AuditLogMessage;
+import org.springframework.stereotype.Component;
+
+@Component
+@SuppressWarnings("unused")
+public class CrudViewAuditLogMessageBuilder extends CrudAuditLogMessageBuilder {
+    @Override
+    public AuditLogMessage buildInternal(AuditLog auditLog) {
+        var url = auditLog.getUrl();
+        var method = auditLog.getMethod();
+
+        var isView = method.equals("GET") && url.split("/").length == 5;
+        if (!isView) {
+            return null;
+        }
+
+        var id = url.split("/")[4];
+        var i18nEntityName = getI18nEntityName(auditLog);
+        var message = new AuditLogMessage();
+        message.setMessage("ra.audit-log.entities/" + method.toLowerCase());
+        message.setArg("entity", i18nEntityName);
+        message.setArg("id", id);
+
+        return message;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/customer/Area.java b/edera-api/api/src/main/java/applica/app/domain/customer/Area.java
new file mode 100644 (file)
index 0000000..44df50d
--- /dev/null
@@ -0,0 +1,62 @@
+package applica.app.domain.customer;
+
+import applica.app.domain.rfid.RFIDDevice;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@CrudEntity(value = "customer-area", completePermissionsRoles = { "ROLE_ADMIN", "ROLE_OPERATOR" }, listPermissionsRoles = {"ROLE_MAINTAINER", "ROLE_CUSTOMER", "ROLE_USER"})
+@Document
+public class Area {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Customer.class)
+    String customerId;
+
+    @Indexed
+    @ForeignKey(primaryType = Office.class)
+    @JsonMaterialize(entityType = Office.class, destination = "office")
+    String officeId;
+
+    @Indexed
+    @ForeignKey(primaryType = RFIDDevice.class)
+    String rfidDeviceId;
+
+    @NotBlank
+    @Keyword
+    String name;
+
+    @NotBlank
+    @Keyword
+    String description;
+
+    @CreatedDate
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime created;
+
+    @LastModifiedDate
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/customer/AreaRepository.java b/edera-api/api/src/main/java/applica/app/domain/customer/AreaRepository.java
new file mode 100644 (file)
index 0000000..6b19830
--- /dev/null
@@ -0,0 +1,9 @@
+package applica.app.domain.customer;
+
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.Optional;
+
+public interface AreaRepository extends CrudRepository<Area, String> {
+    Optional<Area> findFirstByRfidDeviceId(String rfidDeviceId);
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/customer/Customer.java b/edera-api/api/src/main/java/applica/app/domain/customer/Customer.java
new file mode 100644 (file)
index 0000000..962185d
--- /dev/null
@@ -0,0 +1,46 @@
+package applica.app.domain.customer;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@Data
+@CrudEntity(value = "customer", completePermissionsRoles = { "ROLE_ADMIN", "ROLE_OPERATOR" }, listPermissionsRoles = "ROLE_MAINTAINER")
+@NoArgsConstructor
+@AllArgsConstructor
+@Document
+public class Customer {
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @NotBlank
+    @Keyword
+    String name;
+
+    @NotBlank
+    @Keyword
+    String vatCode;
+
+    boolean active;
+
+    @CreatedDate
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime created;
+
+    @LastModifiedDate
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/customer/CustomerRepository.java b/edera-api/api/src/main/java/applica/app/domain/customer/CustomerRepository.java
new file mode 100644 (file)
index 0000000..e7e5fdd
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.customer;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface CustomerRepository extends CrudRepository<Customer, String> {
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/customer/Office.java b/edera-api/api/src/main/java/applica/app/domain/customer/Office.java
new file mode 100644 (file)
index 0000000..a1cba2d
--- /dev/null
@@ -0,0 +1,82 @@
+package applica.app.domain.customer;
+
+
+import applica.app.domain.geo.City;
+import applica.app.domain.geo.Nation;
+import applica.app.domain.geo.Province;
+import applica.app.domain.geo.Region;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@Data
+@CrudEntity(value = "customer-office", completePermissionsRoles = { "ROLE_ADMIN", "ROLE_OPERATOR" })
+@NoArgsConstructor
+@AllArgsConstructor
+public class Office {
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Customer.class)
+    String customerId;
+
+    @Indexed
+    @ForeignKey(primaryType = City.class)
+    @JsonMaterialize(entityType = City.class, destination = "city")
+    String cityId;
+
+    @Indexed
+    @ForeignKey(primaryType = Province.class)
+    @JsonMaterialize(entityType = Province.class, destination = "province")
+    String provinceId;
+
+    @Indexed
+    @ForeignKey(primaryType = Region.class)
+    @JsonMaterialize(entityType = Region.class, destination = "region")
+    String regionId;
+
+    @Indexed
+    @ForeignKey(primaryType = Nation.class)
+    @JsonMaterialize(entityType = Nation.class, destination = "nation")
+    String nationId;
+
+    /*
+    Il nome Ã¨ stato inserito nel caso serva in futuro
+     */
+    @Keyword
+    String name;
+
+    @NotBlank
+    @Keyword
+    String address;
+
+    boolean headquarter;
+
+    @NotBlank
+    @Keyword
+    String email;
+
+    @CreatedDate
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime created;
+
+    @LastModifiedDate
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/customer/Referent.java b/edera-api/api/src/main/java/applica/app/domain/customer/Referent.java
new file mode 100644 (file)
index 0000000..c553e23
--- /dev/null
@@ -0,0 +1,60 @@
+package applica.app.domain.customer;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@CrudEntity(value = "customer-referent", completePermissionsRoles = { "ROLE_ADMIN", "ROLE_OPERATOR" })
+public class Referent {
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Customer.class)
+    String customerId;
+
+    @Indexed
+    @ForeignKey(primaryType = Office.class)
+    @JsonMaterialize(entityType = Office.class, destination = "office")
+    String officeId;
+
+    @NotBlank
+    @Keyword
+    String name;
+    @NotBlank
+    @Keyword
+    String surname;
+
+    @Keyword
+    String email;
+    @Keyword
+    String mobile;
+    @Keyword
+    String unit;
+
+    @CreatedDate
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime created;
+
+    @LastModifiedDate
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/DocxBuilder.java b/edera-api/api/src/main/java/applica/app/domain/docx/DocxBuilder.java
new file mode 100644 (file)
index 0000000..ed11825
--- /dev/null
@@ -0,0 +1,180 @@
+package applica.app.domain.docx;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.xwpf.usermodel.*;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
+
+import java.io.*;
+import java.util.stream.Stream;
+
+public class DocxBuilder {
+    private final Log logger = LogFactory.getLog(getClass());
+
+    /**
+     * @see <a href="https://stackoverflow.com/a/49765239/5936443">stackoverflow answer</a>
+     * This method is responsible for generating a document as a ByteArrayInputStream, using an exisiting word template at templatePath.
+     * It replaces any keyTags in the document by the corresponding value in the JSONObject dataSource,
+     * it assumes the keyTags come preceeded by the separator "{#" and proceeded by "#}", in the following form: {#keyTag#}
+     *
+     * @param templatePath path to the template file
+     * @param dataSource JSONObject containing the keyTags and their corresponding values
+     * @return ByteArrayInputStream containing the generated document
+     */
+    public ByteArrayInputStream generate(String templatePath, DocxDataSource dataSource) throws DocxException {
+        try {
+            var doc = new XWPFDocument(OPCPackage.open(templatePath));
+
+            for(XWPFParagraph p : doc.getParagraphs()){
+                processParagraph(p, dataSource.get(0));
+            }
+
+            for(var table : doc.getTables()){
+                processTable(table, dataSource);
+            }
+
+            var out = new ByteArrayOutputStream();
+            doc.write(out);
+
+            return new ByteArrayInputStream(out.toByteArray());
+        } catch (IOException | InvalidFormatException e) {
+            logger.error(e.getMessage(), e);
+            throw new DocxException(e.getMessage());
+        }
+    }
+
+    public ByteArrayInputStream generate(InputStream template, DocxDataSource dataSource) throws DocxException {
+        try {
+            var doc = new XWPFDocument(OPCPackage.open(template));
+            for(XWPFParagraph p : doc.getParagraphs()){
+                processParagraph(p, dataSource.get(0));
+            }
+
+            for(var table : doc.getTables()){
+                processTable(table, dataSource);
+            }
+
+            var out = new ByteArrayOutputStream();
+            doc.write(out);
+
+            return new ByteArrayInputStream(out.toByteArray());
+        }
+        catch (IOException | InvalidFormatException e) {
+            logger.error(e.getMessage(), e);
+            throw new DocxException(e.getMessage());
+        }
+    }
+
+    /**
+     * This method is responsible for replacing any occurrences in the paragraph of the keyTags present
+     * in the JSONObject dataSourceRow by the corresponding value.
+     *
+     * @param paragraph paragraph to replace the keyTags
+     * @param dataSourceRow JSONObject containing the keyTags and their corresponding values
+     */
+    public void processParagraph(XWPFParagraph paragraph, DocxDataSourceRow dataSourceRow){
+        var text = paragraph.getText();
+        if (!text.contains("{#")) {
+            return;
+        }
+
+        for (var column : dataSourceRow.getColumns()){
+            var bindValue = String.format("{#%s#}", column);
+            if( text.contains(bindValue)) {
+                mergeRunsWithSplittedKeyTags(paragraph);
+                for (var run : paragraph.getRuns()) {
+                    checkAndReplaceFieldRun(run, bindValue, String.valueOf(dataSourceRow.get(column)));
+                }
+            }
+        }
+    }
+
+    /**
+     * This method is responsible for replacing any occurrences in the table of the keyTags
+     * present in the JSONObject dataSource by the corresponding value
+     *
+     * @param table table to replace the keyTags
+     * @param dataSource JSONObject containing the keyTags and their corresponding values
+     */
+    public void processTable(XWPFTable table, DocxDataSource dataSource){
+        var hasHeader = table.getNumberOfRows() > 1;
+        var template = hasHeader
+                ? table.getRow(1)
+                : table.getRow(0);
+        for (var dataSourceRow : dataSource) {
+            var row = new XWPFTableRow((CTRow) template.getCtRow().copy(), table);
+            for( var cell : row.getTableCells()){
+                if( cell.getParagraphs() != null && !cell.getParagraphs().isEmpty()){
+                    for(XWPFParagraph paragraph : cell.getParagraphs()){
+                        processParagraph(paragraph, dataSourceRow);
+                    }
+                }
+            }
+            table.addRow(row);
+        }
+
+        table.removeRow(hasHeader ? 1 : 0);
+    }
+
+    public void checkAndReplaceFieldRun(XWPFRun run, String findStr, String value){
+        String runText = run.getText(0);
+        if( runText!= null && runText.contains(findStr)){
+            runText = runText.replace(findStr, value);
+            run.setText(runText, 0);
+        }
+    }
+
+    /**
+     * A run is a part of the paragraph that has the same formatting.
+     * Word separates the text in paragraphs by different runs in an almost 'random' way,
+     * sometimes the tag we are looking for is splitted across multiple runs.
+     * This method merges the runs that have a keyTag or part of one, so that the keyTag starting with
+     * "{#" and ending with "#}" is in the same run
+     * 
+     * @param paragraph paragraph to merge the runs
+     */
+    public void mergeRunsWithSplittedKeyTags(XWPFParagraph paragraph){
+        String runText;
+        XWPFRun run, nextRun;
+        var runs = paragraph.getRuns();
+
+        for( int i=0 ; i<runs.size(); i++){
+            run = runs.get(i);
+            runText = run.getText(0);
+            if( runText != null &&
+                    (runText.contains("{#") ||  // current run has the complete separator "{#"
+                            (runText.contains("{") && (runs.get(i + 1).getText(0)!=null && runs.get(i + 1).getText(0).substring(0, 1).equals("#"))))){   //current run has the first char, next run has the second char
+
+                while( !openTagMatchesCloseTag(runText) ){
+                    nextRun = runs.get(i + 1);
+                    runText = runText + nextRun.getText(0);
+                    paragraph.removeRun(i + 1);
+                }
+                run.setText(runText, 0); // if we don't set with arg pos=0 it doesn't replace the contents, it adds to them and repeats chars
+            }
+        }
+    }
+
+    /**
+     * This method validates if we have a complete run.
+     * Either by having no keyTags present, or by having a complete keyTag.
+     * If we have parts of a keyTag, but not the complete one, returns false.
+     *
+     * @param runText text of the run to validate
+     * @return true if we have a complete run, false otherwise
+     */
+    private boolean openTagMatchesCloseTag(String runText){
+        int incompleteOpenTagCount = runText.split("\\{", -1).length - 1;   // "{"
+        int completeOpenTagCount = runText.split("\\{#", -1).length - 1;    // "{#"
+        int completeCloseTagCount = runText.split("#}", -1).length - 1;     // "#}"
+
+        if (completeOpenTagCount>0) {
+            return completeOpenTagCount == completeCloseTagCount;
+        } else {
+            return incompleteOpenTagCount <= 0;
+        }
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/DocxDataSource.java b/edera-api/api/src/main/java/applica/app/domain/docx/DocxDataSource.java
new file mode 100644 (file)
index 0000000..a8b0214
--- /dev/null
@@ -0,0 +1,28 @@
+package applica.app.domain.docx;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Rappresenta un insieme di righe di dati da utilizzare per popolare un documento RTF.
+ */
+public class DocxDataSource extends ArrayList<DocxDataSourceRow> {
+    /**
+     * Restituisce l'elenco delle colonne presenti all'interno di ogni singola riga del DataSource.
+     * @return Elenco delle colonne presenti all'interno di ogni singola riga del DataSource.
+     */
+    public List<String> getColumns() {
+        if (!isEmpty()) {
+            var firstRow = get(0);
+            return new ArrayList<>(firstRow.keySet());
+        }
+
+        return new ArrayList<>();
+    }
+
+    public DocxDataSourceRow createRow() {
+        var row = new DocxDataSourceRow();
+        add(row);
+        return row;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/DocxDataSourceRow.java b/edera-api/api/src/main/java/applica/app/domain/docx/DocxDataSourceRow.java
new file mode 100644 (file)
index 0000000..5bfbb8a
--- /dev/null
@@ -0,0 +1,38 @@
+package applica.app.domain.docx;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class DocxDataSourceRow extends HashMap<String, Object> {
+    private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+    private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
+
+    @Override
+    public Object put(String key, Object value) {
+        return super.put(key, value);
+    }
+
+    public void put(String key, LocalDateTime date) {
+        put(key, date.format(dateTimeFormatter));
+    }
+
+    public void put(String key, LocalDate date) {
+        put(key, date.format(dateFormatter));
+    }
+
+    public Object get(String key) {
+        var v = super.get(key);
+        if (v == null) {
+            return "";
+        }
+
+        return v;
+    }
+
+    public ArrayList<String> getColumns() {
+        return new ArrayList<>(keySet());
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/DocxDescriptor.java b/edera-api/api/src/main/java/applica/app/domain/docx/DocxDescriptor.java
new file mode 100644 (file)
index 0000000..6994fbe
--- /dev/null
@@ -0,0 +1,60 @@
+package applica.app.domain.docx;
+
+import java.util.ArrayList;
+
+public class DocxDescriptor extends ArrayList<DocxField> {
+    public static DocxDescriptor build() {
+        return new DocxDescriptor();
+    }
+
+    /**
+     * Un campo 'header' Ã¨ un campo che non varia per ogni eventuale riga di dati.
+     *
+     * @param name Nome del campo.
+     * @param description Descrizione del campo.
+     * @return Riferimento all'istanza corrente.
+     */
+    public DocxDescriptor header(String name, String description) {
+        var field = new DocxField();
+        field.setName(name);
+        field.setDescription(description);
+        field.setBody(false);
+
+        add(field);
+        return this;
+    }
+
+    /**
+     * Un campo 'footer' Ã¨ identico a un campo 'header' poiché non varia per ogni eventuale riga di dati.
+     * Questo metodo Ã¨ stato aggiunto come 'convenzione' per rendere più leggibile il codice.
+     *
+     * @param name Nome del campo.
+     * @param description Descrizione del campo.
+     * @return Riferimento all'istanza corrente.
+     */
+    public DocxDescriptor footer(String name, String description) {
+        return header(name, description);
+    }
+
+    /**
+     * Un campo 'body' Ã¨ differente da un campo 'header' poiché varia per ogni eventuale riga di dati.
+     *
+     * @param name Nome del campo.
+     * @param description Descrizione del campo.
+     * @return Riferimento all'istanza corrente.
+     */
+    public DocxDescriptor body(String name, String description) {
+        var field = new DocxField();
+        field.setName(name);
+        field.setDescription(description);
+        field.setBody(true);
+
+        add(field);
+        return this;
+    }
+
+    public DocxDescriptor get() {
+        return this;
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/DocxException.java b/edera-api/api/src/main/java/applica/app/domain/docx/DocxException.java
new file mode 100644 (file)
index 0000000..ab6f6bb
--- /dev/null
@@ -0,0 +1,8 @@
+package applica.app.domain.docx;
+
+public class DocxException extends Exception {
+    public DocxException(String message) {
+        super(message);
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/DocxFactory.java b/edera-api/api/src/main/java/applica/app/domain/docx/DocxFactory.java
new file mode 100644 (file)
index 0000000..02cdbeb
--- /dev/null
@@ -0,0 +1,48 @@
+package applica.app.domain.docx;
+
+import applica.fs.FilesService;
+import org.springframework.stereotype.Component;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.List;
+
+@Component
+public class DocxFactory {
+    private final List<DocxTypeDef> types;
+    private final DocxBuilder builder;
+    private final FilesService filesService;
+
+    public DocxFactory(List<DocxTypeDef> types, FilesService filesService) {
+        this.types = types;
+        this.builder = new DocxBuilder();
+        this.filesService = filesService;
+    }
+
+    public DocxTypeDef get(String type) {
+        return types.stream().filter(t -> t.getType().equals(type)).findFirst().orElse(null);
+    }
+
+    public List<String> getTypesNames() {
+        return types.stream().map(DocxTypeDef::getType).toList();
+    }
+
+    public ByteArrayInputStream generate(DocxTemplate template, String id) throws DocxException, FileNotFoundException {
+        var typeDef = get(template.getType());
+        if (typeDef == null) {
+            throw new DocxException(String.format("Invalid type %s", template.getType()));
+        }
+
+        var dataSource = typeDef.createDataSource(id);
+        if (dataSource.isEmpty()) {
+            throw new FileNotFoundException(String.format("Data source not found for type %s and id %s", template.getType(), id));
+        }
+        var templateFile = filesService.get(template.getAttachment().getPath());
+        return builder.generate(templateFile, dataSource);
+    }
+
+    public ByteArrayInputStream generate(InputStream template, DocxDataSource dataSource) throws DocxException {
+        return builder.generate(template, dataSource);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/DocxField.java b/edera-api/api/src/main/java/applica/app/domain/docx/DocxField.java
new file mode 100644 (file)
index 0000000..d99c26e
--- /dev/null
@@ -0,0 +1,10 @@
+package applica.app.domain.docx;
+
+import lombok.Data;
+
+@Data
+public class DocxField {
+    String name;
+    String description;
+    boolean body;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/DocxTemplate.java b/edera-api/api/src/main/java/applica/app/domain/docx/DocxTemplate.java
new file mode 100644 (file)
index 0000000..8ba5342
--- /dev/null
@@ -0,0 +1,38 @@
+package applica.app.domain.docx;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.attachments.Attachment;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.persistence.Id;
+import lombok.Data;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@Document
+@CrudEntity(value = "docx-template",
+        completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN"},
+        listPermissionsRoles = {"ROLE_CUSTOMER", "ROLE_MAINTAINER", "ROLE_USER"})
+@Data
+public class DocxTemplate {
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+    String type;
+    @Keyword
+    String name;
+    Attachment attachment;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/DocxTypeDef.java b/edera-api/api/src/main/java/applica/app/domain/docx/DocxTypeDef.java
new file mode 100644 (file)
index 0000000..9438209
--- /dev/null
@@ -0,0 +1,26 @@
+package applica.app.domain.docx;
+
+public abstract class DocxTypeDef {
+
+    /**
+     * Definisce il tipo mappato e utilizzabile all'interno del template.
+     * Il tipo, in formato codice, deve essere localizzato nell'interfaccia.
+     */
+    public abstract String getType();
+
+    /**
+     * Restituisce il descrittore associato al tipo.
+     * @return Descrittore associato al tipo.
+     */
+    public abstract DocxDescriptor getDescriptor();
+
+    /**
+     * Consente, a partire da un ID specificato in input, di caricare il data source associato al tipo con i relativi dati.
+     *
+     * @param id ID del datasource da caricare.
+     * @return Data source associato al tipo con i relativi dati.
+     * @throws DocxException Eccezione generata in caso di errore durante il caricamento del datasource.
+     */
+    public abstract DocxDataSource createDataSource(String id) throws DocxException;
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/docx/typedef/MaintenanceDocxTypeDef.java b/edera-api/api/src/main/java/applica/app/domain/docx/typedef/MaintenanceDocxTypeDef.java
new file mode 100644 (file)
index 0000000..bd1df4c
--- /dev/null
@@ -0,0 +1,151 @@
+package applica.app.domain.docx.typedef;
+
+import applica.app.domain.docx.DocxDataSource;
+import applica.app.domain.docx.DocxDescriptor;
+import applica.app.domain.docx.DocxTypeDef;
+import applica.app.domain.equipment.EquipmentRepository;
+import applica.app.domain.equipment.EquipmentTypeRepository;
+import applica.app.domain.maintenance.*;
+import applica.app.domain.protocol.ProtocolRepository;
+import applica.app.domain.supplier.SupplierRepository;
+import applica.app.domain.supply.SupplyRepository;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.format.datetime.DateFormatter;
+import org.springframework.stereotype.Component;
+
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.List;
+
+@Component
+@SuppressWarnings("unused")
+public class MaintenanceDocxTypeDef extends DocxTypeDef {
+    public static final String TYPE = "maintenance";
+
+    private final ActivityRepository activityRepository;
+    private final ActivityTypeRepository activityTypeRepository;
+    private final MaintenanceRepository maintenanceRepository;
+    private final SupplyRepository supplyRepository;
+    private final ProtocolRepository protocolRepository;
+    private final EquipmentRepository equipmentRepository;
+    private final EquipmentTypeRepository equipmentTypeRepository;
+
+    public MaintenanceDocxTypeDef(
+            ActivityRepository activityRepository,
+            ActivityTypeRepository activityTypeRepository,
+            MaintenanceRepository maintenanceRepository,
+            SupplyRepository supplyRepository,
+            ProtocolRepository protocolRepository,
+            EquipmentRepository equipmentRepository,
+            EquipmentTypeRepository equipmentTypeRepository,
+            SupplierRepository supplierRepository) {
+        this.activityRepository = activityRepository;
+        this.activityTypeRepository = activityTypeRepository;
+        this.maintenanceRepository = maintenanceRepository;
+        this.supplyRepository = supplyRepository;
+        this.protocolRepository = protocolRepository;
+        this.equipmentRepository = equipmentRepository;
+        this.equipmentTypeRepository = equipmentTypeRepository;
+    }
+
+    @Override
+    public String getType() {
+        return TYPE;
+    }
+
+    @Override
+    public DocxDescriptor getDescriptor() {
+        return DocxDescriptor.build()
+                .header("ID", "Identificativo univoco alfanumerico della manutenzione")
+                .header("TITLE", "Titolo della manutenzione")
+                .header("DESCRIPTION", "Descrizione della manutenzione")
+                .header("INTERVENTION_NUMBER", "Numero intervento")
+                .header("DATE", "Data intervento")
+                .header("ACTIVITY_ID", "Identificativo univoco alfanumerico dell'attività correlata")
+                .header("ACTIVITY_TITLE", "Titolo dell'attività correlata")
+                .header("ACTIVITY_DESCR", "Descrizione dell'attività correlata")
+                .header("ACTIVITY_DATE", "Data dell'attività correlata")
+                .header("ACTIVITY_TYPE_ID", "Identificativo univoco alfanumerico del tipo di attività correlata")
+                .header("ACTIVITY_TYPE_DESCR", "Nome del tipo di attività correlata")
+                .header("SUPPLY_ID", "Identificativo univoco alfanumerico dell'approvvigionamento")
+                .header("SUPPLY_QUANTITY", "Quantità dell'approvvigionamento")
+                .header("SUPPLY_BREAKPOINT_IN_HOURS", "Punto di rottura in ore dell'approvvigionamento")
+                .header("SUPPLY_ACCUMULATED_HOURS", "Ore accumulate dall'apparecchiatura")
+                .header("SUPPLY_ORDER_DATE", "Data dell'ordine dell'approvvigionamento")
+                .header("SUPPLY_DELIVERY_DATE", "Data di consegna dell'approvvigionamento")
+                .header("SUPPLY_SERIAL_NUMBER", "Numero seriale dell'approvvigionamento")
+                .header("SUPPLY_DTT_NUMBER", "Numero DTT dell'approvvigionamento")
+                .header("EQUIPMENT_TYPE", "Tipo di approvvigionamento")
+                .header("PROTOCOL_ID", "Identificativo univoco alfanumerico del protocollo")
+                .header("PROTOCOL_TITLE", "Titolo del protocollo")
+                .header("PROTOCOL_DESCR", "Descrizione del protocollo")
+                .header("PROTOCOL_DATE", "Data del protocollo")
+                .body("INTERVENTION_ITEM_EQUIPMENT_TYPE_NAME", "Nome del tipo di apparecchiatura")
+                .body("INTERVENTION_ITEM_DESCRIPTION", "Descrizione dell'intervento")
+                .body("INTERVENTION_ITEM_SUBSTITUTION", "Sostituzione")
+                .get();
+    }
+
+    @Override
+    public DocxDataSource createDataSource(String id) {
+        var ds = new DocxDataSource();
+        var maintenance = maintenanceRepository.findById(id).orElseThrow();
+        var activity = activityRepository.findById(maintenance.getActivityId()).orElseThrow();
+        var activityType = activityTypeRepository.findById(activity.getActivityTypeId()).orElseThrow();
+        var supply = supplyRepository.findById(activity.getSupplyId()).orElseThrow();
+        var protocol = protocolRepository.findById(supply.getProtocolId()).orElseThrow();
+        var equipment = equipmentRepository.findById(supply.getEquipmentId()).orElseThrow();
+        var equipmentType = equipmentTypeRepository.findById(equipment.getEquipmentTypeId()).orElseThrow();
+        var dateFormatter = new DateFormatter();
+        var localDateTimeFormatter = new DateTimeFormatterBuilder()
+                .toFormatter();
+
+        var bodyItems = createBody(maintenance);
+        for (var item : bodyItems) {
+            var row = ds.createRow();
+            row.put("ID", maintenance.getId());
+            row.put("TITLE", maintenance.getTitle());
+            row.put("DESCRIPTION", maintenance.getDescription());
+            row.put("INTERVENTION_NUMBER", maintenance.getInterventionNumber());
+            row.put("DATE", maintenance.getDate());
+
+            if (!StringUtils.isEmpty(item.getEquipmentTypeId())) {
+                var itemEquipmentType = equipmentTypeRepository.findById(item.getEquipmentTypeId()).orElseThrow();
+                row.put("INTERVENTION_ITEM_EQUIPMENT_TYPE_NAME", itemEquipmentType.getName());
+                row.put("INTERVENTION_ITEM_DESCRIPTION", item.getDescription());
+                row.put("INTERVENTION_ITEM_SUBSTITUTION", item.isSubstitution() ? "Sì" : "No");
+                row.put("INTERVENTION_ITEM_REORDER", item.isReorder() ? "Sì" : "No");
+            }
+
+            row.put("ACTIVITY_ID", activity.getId());
+            row.put("ACTIVITY_TITLE", activity.getTitle());
+            row.put("ACTIVITY_DESCR", activity.getDescription());
+            row.put("ACTIVITY_DATE", activity.getDate());
+            row.put("ACTIVITY_TYPE_ID", activityType.getId());
+            row.put("ACTIVITY_TYPE_DESCR", activityType.getDescription());
+            row.put("SUPPLY_ID", supply.getId());
+            row.put("SUPPLY_QUANTITY", supply.getQuantity());
+            row.put("SUPPLY_BREAKPOINT_IN_HOURS", supply.getBreakpointInHours());
+            row.put("SUPPLY_ACCUMULATED_HOURS", supply.getAccumulatedHours());
+            row.put("SUPPLY_ORDER_DATE", supply.getOrderDate());
+            row.put("SUPPLY_DELIVERY_DATE", supply.getDeliveryDate());
+            row.put("SUPPLY_SERIAL_NUMBER", supply.getSerialNumber());
+            row.put("SUPPLY_DTT_NUMBER", supply.getDdtNumber());
+            row.put("EQUIPMENT_TYPE", equipmentType.getName());
+            row.put("PROTOCOL_ID", protocol.getId());
+            row.put("PROTOCOL_TITLE", protocol.getTitle());
+            row.put("PROTOCOL_DESCR", protocol.getDescription());
+            row.put("PROTOCOL_DATE", protocol.getProtocolDate());
+        }
+
+        return ds;
+    }
+
+    private List<Intervention> createBody(Maintenance maintenance) {
+        var interventions = maintenance.getInterventions();
+        if (interventions == null || interventions.isEmpty()) {
+            return List.of(new Intervention());
+        }
+
+        return interventions;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/dto/ActivityDetailsDTO.java b/edera-api/api/src/main/java/applica/app/domain/dto/ActivityDetailsDTO.java
new file mode 100644 (file)
index 0000000..e48c403
--- /dev/null
@@ -0,0 +1,10 @@
+package applica.app.domain.dto;
+
+import lombok.Data;
+
+@Data
+public class ActivityDetailsDTO {
+    String equipmentName;
+    String supplierName;
+    String locationName;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/dto/SupplyDetailsDTO.java b/edera-api/api/src/main/java/applica/app/domain/dto/SupplyDetailsDTO.java
new file mode 100644 (file)
index 0000000..827e421
--- /dev/null
@@ -0,0 +1,10 @@
+package applica.app.domain.dto;
+
+import lombok.Data;
+
+@Data
+public class SupplyDetailsDTO {
+    String supplierName;
+    String equipmentName;
+    String protocolName;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/equipment/Equipment.java b/edera-api/api/src/main/java/applica/app/domain/equipment/Equipment.java
new file mode 100644 (file)
index 0000000..066a39c
--- /dev/null
@@ -0,0 +1,58 @@
+package applica.app.domain.equipment;
+
+import applica.app.domain.supplier.Supplier;
+import applica.app.web.annotations.CustomerFilterable;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "equipment", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" }, listPermissionsRoles = {"ROLE_CUSTOMER","ROLE_MAINTAINER"}, savePermissionsRoles = {"ROLE_CUSTOMER","ROLE_MAINTAINER"})
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@CustomerFilterable
+@Document
+public class Equipment {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @Keyword
+    @ForeignKey(primaryType = EquipmentType.class)
+    String equipmentTypeId;
+
+    @Indexed
+    @ForeignKey(primaryType = Supplier.class)
+    @JsonMaterialize(entityType = Supplier.class, destination = "supplier")
+    String supplierId;
+
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+
+    transient EquipmentType equipmentType;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentAttachment.java b/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentAttachment.java
new file mode 100644 (file)
index 0000000..dffd629
--- /dev/null
@@ -0,0 +1,50 @@
+package applica.app.domain.equipment;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.attachments.Attachment;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "equipment-attachment", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" },
+        listPermissionsRoles = {"ROLE_CUSTOMER","ROLE_MAINTAINER" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class EquipmentAttachment {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Equipment.class)
+    String equipmentId;
+
+    @Keyword
+    String description;
+
+    Attachment attachment;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentRepository.java b/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentRepository.java
new file mode 100644 (file)
index 0000000..81910b7
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.equipment;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface EquipmentRepository extends CrudRepository<Equipment, String> {
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentType.java b/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentType.java
new file mode 100644 (file)
index 0000000..19ec0be
--- /dev/null
@@ -0,0 +1,68 @@
+package applica.app.domain.equipment;
+
+import applica.crud.annotations.Code;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "equipment-type",
+        completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR", "ROLE_MAINTAINER", "ROLE_CUSTOMER" },
+        listPermissionsRoles = {"ROLE_USER"}
+)
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@Document
+public class EquipmentType {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    /**
+     * Il progressivo della tipologia di equipment ci servirà, in futuro, per l'addestramento del modello
+     * di machine learning per la predizione dei guasti.
+     */
+    @Code
+    Long progressive;
+
+    @Indexed
+    @ForeignKey(primaryType = EquipmentType.class)
+    String parentId;
+
+    @Keyword
+    @NotBlank
+    String name;
+
+    boolean accessory;
+
+    /**
+     * Numero di ore di funzionamento dopo le quali l'attrezzatura deve essere sottoposta a manutenzione per probabile guasto.
+     */
+    int breakpointInHours;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentTypeAttachment.java b/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentTypeAttachment.java
new file mode 100644 (file)
index 0000000..fd86342
--- /dev/null
@@ -0,0 +1,49 @@
+package applica.app.domain.equipment;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.attachments.Attachment;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "equipment-type-attachment", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class EquipmentTypeAttachment {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Keyword
+    String description;
+
+    Attachment attachment;
+
+    @Indexed
+    @ForeignKey(primaryType = EquipmentType.class)
+    String typeId;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentTypeRepository.java b/edera-api/api/src/main/java/applica/app/domain/equipment/EquipmentTypeRepository.java
new file mode 100644 (file)
index 0000000..b1f3740
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.equipment;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface EquipmentTypeRepository extends CrudRepository<EquipmentType, String> {
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/geo/City.java b/edera-api/api/src/main/java/applica/app/domain/geo/City.java
new file mode 100644 (file)
index 0000000..8dbadb8
--- /dev/null
@@ -0,0 +1,43 @@
+package applica.app.domain.geo;
+
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+@CrudEntity(value = "city", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR", "ROLE_CUSTOMER", "ROLE_MAINTAINER" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class City {
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Province.class)
+    @JsonMaterialize(destination = "province", entityType = Province.class)
+    String provinceId;
+
+    @Keyword
+    String name;
+
+    @Keyword
+    String postalCode;
+
+    @Keyword
+    String istatCode;
+
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/geo/Nation.java b/edera-api/api/src/main/java/applica/app/domain/geo/Nation.java
new file mode 100644 (file)
index 0000000..ee7c5eb
--- /dev/null
@@ -0,0 +1,31 @@
+package applica.app.domain.geo;
+
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.query.Keyword;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+@CrudEntity(value = "nation", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR", "ROLE_CUSTOMER", "ROLE_MAINTAINER" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Nation {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Keyword
+    String code;
+
+    @Keyword
+    String name;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/geo/Province.java b/edera-api/api/src/main/java/applica/app/domain/geo/Province.java
new file mode 100644 (file)
index 0000000..ea7cee5
--- /dev/null
@@ -0,0 +1 @@
+package applica.app.domain.geo;\r\rimport applica.crud.annotations.CrudEntity;\rimport applica.crud.annotations.JsonMaterialize;\rimport applica.crud.constraints.ForeignKey;\rimport applica.crud.query.Keyword;\rimport lombok.*;\rimport lombok.experimental.FieldDefaults;\rimport org.springframework.data.annotation.Id;\rimport org.springframework.data.mongodb.core.index.Indexed;\rimport org.springframework.data.mongodb.core.mapping.FieldType;\rimport org.springframework.data.mongodb.core.mapping.MongoId;\r\r@CrudEntity(value = "province", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR", "ROLE_CUSTOMER", "ROLE_MAINTAINER" })\r@AllArgsConstructor\r@Getter\r@Setter\r@NoArgsConstructor\r@ToString\r@EqualsAndHashCode\r@FieldDefaults(level = AccessLevel.PRIVATE)\rpublic class Province {\r\r    @Id\r    @MongoId(targetType = FieldType.STRING)\r    String id;\r\r    @Indexed\r    @ForeignKey(primaryType = Region.class)\r    @JsonMaterialize(destination = "region", entityType = Region.class)\r    String regionId;\r\r    @Keyword\r    String name;\r\r    @Keyword\r    String code;\r\r}\r
\ No newline at end of file
diff --git a/edera-api/api/src/main/java/applica/app/domain/geo/Region.java b/edera-api/api/src/main/java/applica/app/domain/geo/Region.java
new file mode 100644 (file)
index 0000000..a8d99a0
--- /dev/null
@@ -0,0 +1,39 @@
+package applica.app.domain.geo;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+@CrudEntity(value = "region", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR", "ROLE_CUSTOMER", "ROLE_MAINTAINER" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Region {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Nation.class)
+    @JsonMaterialize(destination = "nation", entityType = Nation.class)
+    String nationId;
+
+    @Keyword
+    String code;
+
+    @Keyword
+    String name;
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/maintenance/Activity.java b/edera-api/api/src/main/java/applica/app/domain/maintenance/Activity.java
new file mode 100644 (file)
index 0000000..daf852f
--- /dev/null
@@ -0,0 +1,84 @@
+package applica.app.domain.maintenance;
+
+import applica.app.domain.User;
+import applica.app.domain.customer.Customer;
+import applica.app.domain.protocol.Protocol;
+import applica.app.domain.supply.Supply;
+import applica.app.web.annotations.CustomerFilterable;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.annotations.LoggedUserId;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "activity",
+        completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN","ROLE_USER", "ROLE_CUSTOMER" },
+        listPermissionsRoles = {"ROLE_MAINTAINER"},
+        savePermissionsRoles = {"ROLE_MAINTAINER"})
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@CustomerFilterable
+@Document
+public class Activity {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Supply.class)
+    String supplyId;
+
+    @Indexed
+    @ForeignKey(primaryType = ActivityType.class)
+    @JsonMaterialize(destination = "activityType", entityType = ActivityType.class)
+    String activityTypeId;
+
+    @JsonMaterialize(destination = "user", entityType = User.class)
+    @LoggedUserId
+    String userId;
+
+    @Keyword
+    @NotBlank
+    String title;
+
+    @Keyword
+    String description;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    LocalDate date;
+
+    ActivityStatus status;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+
+    transient Protocol protocol;
+    transient Supply supply;
+    transient Customer customer;
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityRepository.java b/edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityRepository.java
new file mode 100644 (file)
index 0000000..dff4ab8
--- /dev/null
@@ -0,0 +1,9 @@
+package applica.app.domain.maintenance;
+
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.Optional;
+
+public interface ActivityRepository extends CrudRepository<Activity, String> {
+    Optional<Activity> findFirstBySupplyIdAndActivityTypeIdOrderByCreatedDesc(String supplyId, String activityTypeId);
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityStatus.java b/edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityStatus.java
new file mode 100644 (file)
index 0000000..45dac05
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.maintenance;
+
+public enum ActivityStatus {
+    OPEN,
+    CLOSED
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityType.java b/edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityType.java
new file mode 100644 (file)
index 0000000..453a894
--- /dev/null
@@ -0,0 +1,50 @@
+package applica.app.domain.maintenance;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.attachments.Attachment;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "activity-type",
+        completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN" },
+        listPermissionsRoles = { "ROLE_CUSTOMER", "ROLE_MAINTAINER", "ROLE_USER" }
+)
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@Document
+public class ActivityType {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @NotBlank
+    @Keyword
+    String description;
+
+    Attachment attachment;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityTypeRepository.java b/edera-api/api/src/main/java/applica/app/domain/maintenance/ActivityTypeRepository.java
new file mode 100644 (file)
index 0000000..53cd99a
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.maintenance;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface ActivityTypeRepository extends CrudRepository<ActivityType, String> {
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/maintenance/Intervention.java b/edera-api/api/src/main/java/applica/app/domain/maintenance/Intervention.java
new file mode 100644 (file)
index 0000000..a68659a
--- /dev/null
@@ -0,0 +1,18 @@
+package applica.app.domain.maintenance;
+
+import applica.app.domain.equipment.EquipmentType;
+import applica.crud.annotations.JsonMaterialize;
+import lombok.Data;
+import org.springframework.data.mongodb.core.index.Indexed;
+
+@Data
+public class Intervention {
+    @Indexed
+    @JsonMaterialize(destination = "equipmentType", entityType = EquipmentType.class)
+    String equipmentTypeId;
+
+    String description;
+
+    boolean substitution;
+    boolean reorder;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/maintenance/Maintenance.java b/edera-api/api/src/main/java/applica/app/domain/maintenance/Maintenance.java
new file mode 100644 (file)
index 0000000..23d7957
--- /dev/null
@@ -0,0 +1,79 @@
+package applica.app.domain.maintenance;
+
+import applica.app.domain.User;
+import applica.app.domain.protocol.Protocol;
+import applica.app.web.annotations.CustomerFilterable;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.annotations.LoggedUserId;
+import applica.crud.attachments.Attachment;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@CrudEntity(value = "maintenance", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_CUSTOMER", "ROLE_USER" }, listPermissionsRoles = {"ROLE_MAINTAINER"}, savePermissionsRoles = {"ROLE_MAINTAINER"}, creationPermissionsRoles = {"ROLE_MAINTAINER"})
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@CustomerFilterable
+@Document
+public class Maintenance {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Activity.class)
+    String activityId;
+
+    @LoggedUserId
+    @JsonMaterialize(entityType = User.class, destination = "user")
+    String userId;
+
+    @Keyword
+    @NotBlank
+    String title;
+
+    String description;
+
+    @NotBlank
+    String interventionNumber;
+
+    Attachment attachment;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    LocalDate date;
+
+    List<Intervention> interventions;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+
+    transient Protocol protocol;
+    transient Activity activity;
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/maintenance/MaintenanceRepository.java b/edera-api/api/src/main/java/applica/app/domain/maintenance/MaintenanceRepository.java
new file mode 100644 (file)
index 0000000..e5a59e9
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.maintenance;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface MaintenanceRepository extends CrudRepository<Maintenance, String> {
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/package-info.java b/edera-api/api/src/main/java/applica/app/domain/package-info.java
new file mode 100644 (file)
index 0000000..888c375
--- /dev/null
@@ -0,0 +1 @@
+package applica.app.domain;
\ No newline at end of file
diff --git a/edera-api/api/src/main/java/applica/app/domain/pricing/PriceList.java b/edera-api/api/src/main/java/applica/app/domain/pricing/PriceList.java
new file mode 100644 (file)
index 0000000..f7d2bee
--- /dev/null
@@ -0,0 +1,57 @@
+package applica.app.domain.pricing;
+
+import applica.app.domain.supplier.Supplier;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "price-list", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class PriceList {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @Keyword
+    @ForeignKey(primaryType = Supplier.class)
+    String supplierId;
+
+    @Keyword
+    @NotBlank
+    String name;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    LocalDate start;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    LocalDate end;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/pricing/PriceListItem.java b/edera-api/api/src/main/java/applica/app/domain/pricing/PriceListItem.java
new file mode 100644 (file)
index 0000000..59b6f00
--- /dev/null
@@ -0,0 +1,56 @@
+package applica.app.domain.pricing;
+
+import applica.app.domain.equipment.Equipment;
+import applica.app.domain.equipment.EquipmentType;
+import applica.app.domain.supplier.Supplier;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "price-list-item", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class PriceListItem {
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @Keyword
+    @ForeignKey(primaryType = PriceList.class)
+    String priceListId;
+
+    @Indexed
+    @Keyword
+    @ForeignKey(primaryType = Equipment.class)
+    String equipmentId;
+
+    float price;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+
+    transient Supplier supplier;
+    transient EquipmentType equipmentType;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/protocol/Protocol.java b/edera-api/api/src/main/java/applica/app/domain/protocol/Protocol.java
new file mode 100644 (file)
index 0000000..fdd9d8d
--- /dev/null
@@ -0,0 +1,71 @@
+package applica.app.domain.protocol;
+
+import applica.app.domain.customer.Customer;
+import applica.app.web.annotations.CustomerFilterable;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@CrudEntity(value = "protocol", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" }, listPermissionsRoles = {"ROLE_CUSTOMER", "ROLE_MAINTAINER"}, savePermissionsRoles = {"ROLE_CUSTOMER", "ROLE_MAINTAINER"})
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@CustomerFilterable
+@Document
+public class Protocol {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @Keyword
+    @ForeignKey(primaryType = Customer.class)
+    String customerId;
+
+    @Keyword
+    @NotBlank
+    String title;
+
+    @Keyword
+    String description;
+
+    @Keyword
+    @NotBlank
+    String protocolNumber;
+
+    List<ProtocolService> services;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    LocalDate protocolDate;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+
+    transient Customer customer;
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/protocol/ProtocolAttachment.java b/edera-api/api/src/main/java/applica/app/domain/protocol/ProtocolAttachment.java
new file mode 100644 (file)
index 0000000..f0f9c71
--- /dev/null
@@ -0,0 +1,50 @@
+package applica.app.domain.protocol;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.attachments.Attachment;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "protocol-attachment", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" }, listPermissionsRoles = {"ROLE_CUSTOMER","ROLE_MAINTAINER"})
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class ProtocolAttachment {
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Protocol.class)
+    String protocolId;
+
+    @Keyword
+    @NotBlank
+    String description;
+
+    Attachment attachment;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/protocol/ProtocolRepository.java b/edera-api/api/src/main/java/applica/app/domain/protocol/ProtocolRepository.java
new file mode 100644 (file)
index 0000000..824bda0
--- /dev/null
@@ -0,0 +1,7 @@
+package applica.app.domain.protocol;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface ProtocolRepository extends CrudRepository<Protocol, String>{
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/protocol/ProtocolService.java b/edera-api/api/src/main/java/applica/app/domain/protocol/ProtocolService.java
new file mode 100644 (file)
index 0000000..2997b60
--- /dev/null
@@ -0,0 +1,25 @@
+package applica.app.domain.protocol;
+
+import applica.app.domain.maintenance.ActivityType;
+import applica.crud.constraints.ForeignKey;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.data.mongodb.core.index.Indexed;
+
+import java.time.LocalDate;
+
+@Data
+public class ProtocolService {
+    @Indexed
+    @ForeignKey(primaryType = ActivityType.class)
+    String fromActivityTypeId;
+
+    @Indexed
+    @ForeignKey(primaryType = ActivityType.class)
+    String toActivityTypeId;
+
+    long frequency;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    LocalDate end;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/reporting/DataPoint.java b/edera-api/api/src/main/java/applica/app/domain/reporting/DataPoint.java
new file mode 100644 (file)
index 0000000..c7f91d7
--- /dev/null
@@ -0,0 +1,73 @@
+package applica.app.domain.reporting;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.HashMap;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DataPoint extends HashMap<String, Object> {
+    @Override
+    @JsonIgnore
+    public Object get(Object key) {
+        return super.get(key);
+    }
+
+    public double getDouble(String key) {
+        var v = get(key);
+        if (v instanceof Double) {
+            return (double) v;
+        } else if (v instanceof Integer) {
+            return (int) v;
+        } else {
+            return 0;
+        }
+    }
+
+    public int getInt(String key) {
+        var v = get(key);
+        if (v instanceof Integer) {
+            return (int) v;
+        } else if (v instanceof Double) {
+            return (int) (double) v;
+        } else {
+            return 0;
+        }
+    }
+
+    public double getTrend(DataPoint other, String property) {
+        var thisAverage = getDouble(property);
+        var otherAverage = other.getDouble(property);
+
+        if (thisAverage == 0 || otherAverage == 0) {
+            return 0;
+        }
+
+        var v = ((thisAverage - otherAverage) / otherAverage) * 100;
+        return Math.round(v * 100.0) / 100.0;
+    }
+
+    public DataPoint set(String key, Object value) {
+        put(key, value);
+        return this;
+    }
+
+    public static DataPoint create() {
+        return new DataPoint();
+    }
+
+    public DataPoint rename(String key, String newKey) {
+        var value = get(key);
+        remove(key);
+        put(newKey, value);
+        return this;
+    }
+
+    public DataPoint clone() {
+        DataPoint clone = (DataPoint) super.clone();
+        clone.putAll(this);
+        return clone;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/reporting/DataPointSet.java b/edera-api/api/src/main/java/applica/app/domain/reporting/DataPointSet.java
new file mode 100644 (file)
index 0000000..fa95818
--- /dev/null
@@ -0,0 +1,62 @@
+package applica.app.domain.reporting;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class DataPointSet {
+    List<DataPoint> points;
+
+    public DataPointSet() {
+        points = new ArrayList<>();
+    }
+
+    public DataPointSet(List<DataPoint> points) {
+        this.points = new ArrayList<>(points);
+    }
+
+    public double getTrend(DataPointSet other, String property) {
+        var thisAverage = getAverage(property);
+        var otherAverage = other.getAverage(property);
+
+        if (thisAverage == 0 || otherAverage == 0) {
+            return 0;
+        }
+
+        var v = ((thisAverage - otherAverage) / otherAverage) * 100;
+        return Math.round(v * 100.0) / 100.0;
+    }
+
+    public double getSum(String property) {
+        return points.stream().mapToDouble(p -> p.getDouble(property)).sum();
+    }
+
+    public double getAverage(String property) {
+        return points.stream().mapToDouble(p -> p.getDouble(property)).average().orElse(0);
+    }
+
+    public double getMin(String property) {
+        return points.stream().mapToDouble(p -> p.getDouble(property)).min().orElse(0);
+    }
+
+    public double getMax(String property) {
+        return points.stream().mapToDouble(p -> p.getDouble(property)).max().orElse(0);
+    }
+
+    public int getCount() {
+        return points.size();
+    }
+
+    public void add(DataPoint point) {
+        points.add(point);
+    }
+
+    @JsonIgnore
+    public DataPoint getLast() {
+        return points.get(points.size() - 1);
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/reporting/Filter.java b/edera-api/api/src/main/java/applica/app/domain/reporting/Filter.java
new file mode 100644 (file)
index 0000000..988f989
--- /dev/null
@@ -0,0 +1,32 @@
+package applica.app.domain.reporting;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Optional;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Filter implements Cloneable{
+    Optional<GroupBy> groupBy = Optional.empty();
+    Optional<String> supplyId = Optional.empty();
+    Optional<String> tagId = Optional.empty();
+    Optional<String> gatewayId = Optional.empty();
+    Optional<String> officeId = Optional.empty();
+
+    @Override
+    public Filter clone() {
+        try {
+            var filter = (Filter)super.clone();
+            filter.setSupplyId(supplyId);
+            filter.setGatewayId(gatewayId);
+            filter.setTagId(tagId);
+            filter.setOfficeId(officeId);
+            return filter;
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/reporting/FilterSet.java b/edera-api/api/src/main/java/applica/app/domain/reporting/FilterSet.java
new file mode 100644 (file)
index 0000000..0736dc1
--- /dev/null
@@ -0,0 +1,7 @@
+package applica.app.domain.reporting;
+
+import lombok.Data;
+
+@Data
+public class FilterSet extends TemporalFilter {
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/reporting/GroupBy.java b/edera-api/api/src/main/java/applica/app/domain/reporting/GroupBy.java
new file mode 100644 (file)
index 0000000..d54e60e
--- /dev/null
@@ -0,0 +1,19 @@
+package applica.app.domain.reporting;
+
+public enum GroupBy {
+    YEAR("year"),
+    MONTH("month"),
+    DAY("day"),
+    HOUR("hour"),
+    MINUTE("minute");
+
+    private final String alias;
+
+    GroupBy(String alias) {
+        this.alias = alias;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/reporting/TemporalFilter.java b/edera-api/api/src/main/java/applica/app/domain/reporting/TemporalFilter.java
new file mode 100644 (file)
index 0000000..b617209
--- /dev/null
@@ -0,0 +1,190 @@
+package applica.app.domain.reporting;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.mongodb.core.aggregation.*;
+import org.springframework.data.mongodb.core.query.Criteria;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class TemporalFilter extends Filter implements Cloneable {
+    LocalDateTime from;
+    LocalDateTime to;
+
+
+    public GroupBy getSuggestedGroupBy() {
+        if (groupBy.isPresent()) {
+            return groupBy.get();
+        }
+        if (from == null || to == null) {
+            return GroupBy.YEAR;
+        }
+
+        if (from.getYear() != to.getYear()) {
+            return GroupBy.YEAR;
+        }
+
+        if (from.getMonth() != to.getMonth()) {
+            return GroupBy.MONTH;
+        }
+
+        if (from.getDayOfMonth() != to.getDayOfMonth()) {
+            return GroupBy.DAY;
+        }
+
+        if (from.getHour() != to.getHour()) {
+            return GroupBy.HOUR;
+        }
+
+        if (from.getMinute() != to.getMinute()) {
+            return GroupBy.MINUTE;
+        }
+
+        return GroupBy.YEAR;
+    }
+
+    public ProjectionOperation project(String field) {
+        var groupBy = getSuggestedGroupBy();
+        var dateOperators = List.of(
+                DateOperators.Year.yearOf(field),
+                DateOperators.Month.monthOf(field),
+                DateOperators.DayOfMonth.dayOfMonth(field),
+                DateOperators.Hour.hourOf(field),
+                DateOperators.Minute.minuteOf(field)
+        );
+        var projectOperation = Aggregation.project();
+        for (int i = 0; i <= groupBy.ordinal(); i++) {
+            projectOperation = projectOperation.and(dateOperators.get(i)).as(GroupBy.values()[i].getAlias());
+        }
+        return projectOperation;
+    }
+
+    public GroupOperation group(String... additionalFields) {
+        var groupBy = getSuggestedGroupBy();
+        var fields = new String[groupBy.ordinal() + 1 + additionalFields.length];
+        for (int i = 0; i <= groupBy.ordinal(); i++) {
+            fields[i] = GroupBy.values()[i].getAlias();
+        }
+        for (int i = 0; i < additionalFields.length; i++) {
+            fields[groupBy.ordinal() + 1 + i] = additionalFields[i];
+        }
+
+        return Aggregation.group(fields);
+    }
+
+    public SortOperation sort() {
+        var groupBy = getSuggestedGroupBy();
+        var sortOperation = Aggregation.sort(Sort.Direction.ASC, GroupBy.values()[0].getAlias());
+        for (int i = 1; i <= groupBy.ordinal(); i++) {
+            sortOperation = sortOperation.and(Sort.Direction.ASC, GroupBy.values()[i].getAlias());
+        }
+        return sortOperation;
+    }
+
+    public ProjectionOperation projectionAs() {
+        var projectionOperation = Aggregation.project();
+        var groupBy = getSuggestedGroupBy();
+        for (var i = 0; i <= groupBy.ordinal(); i++) {
+            var alias = GroupBy.values()[i].getAlias();
+            projectionOperation = projectionOperation.and(alias).as(alias);
+        }
+        return projectionOperation;
+    }
+
+    public MatchOperation between(String field) {
+        var criteria = new Criteria();
+        criteria = criteria.and(field).gte(from).lte(to);
+        return Aggregation.match(criteria);
+    }
+
+
+    public TemporalFilter forTrend() {
+        var trendFilter = (TemporalFilter) clone();
+        var groupBy = getSuggestedGroupBy();
+        switch (groupBy) {
+            case YEAR:
+                trendFilter.setFrom(from.minusYears(1));
+                trendFilter.setTo(to.minusYears(1));
+                break;
+            case MONTH:
+                trendFilter.setFrom(from.minusMonths(1));
+                trendFilter.setTo(to.minusMonths(1));
+                break;
+            case DAY:
+                trendFilter.setFrom(from.minusDays(1));
+                trendFilter.setTo(to.minusDays(1));
+                break;
+            case HOUR:
+                trendFilter.setFrom(from.minusHours(1));
+                trendFilter.setTo(to.minusHours(1));
+                break;
+            case MINUTE:
+                trendFilter.setFrom(from.minusMinutes(1));
+                trendFilter.setTo(to.minusMinutes(1));
+                break;
+        }
+        return trendFilter;
+    }
+
+    public TemporalFilter groupBy(GroupBy groupBy) {
+        this.setGroupBy(Optional.of(groupBy));
+        return this;
+    }
+
+    public TemporalFilter clone() {
+        var filter = (TemporalFilter) super.clone();
+        filter.setFrom(from);
+        filter.setTo(to);
+        return filter;
+    }
+
+    public static TemporalFilter between(LocalDateTime from, LocalDateTime to) {
+        var filter = new TemporalFilter();
+        filter.setFrom(from);
+        filter.setTo(to);
+        return filter;
+    }
+
+    public TemporalFilter currentMonth() {
+        var start = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
+        var end = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).plusMonths(1);
+
+        var filter = (TemporalFilter) clone();
+        filter.setFrom(start);
+        filter.setTo(end);
+        filter.setSupplyId(supplyId);
+        return filter;
+    }
+
+    public TemporalFilter currentYear() {
+        var start = LocalDateTime.now().withDayOfYear(1).withHour(0).withMinute(0).withSecond(0);
+        var end = LocalDateTime.now().withDayOfYear(1).withHour(0).withMinute(0).withSecond(0).plusYears(1);
+
+        var filter = (TemporalFilter) clone();
+        filter.setFrom(start);
+        filter.setTo(end);
+        filter.setSupplyId(supplyId);
+        return filter;
+    }
+
+    public TemporalFilter lastXDays(int days) {
+        var start = LocalDateTime.now().minusDays(days);
+        var end = LocalDateTime.now();
+
+        var filter = (TemporalFilter) clone();
+        filter.setFrom(start);
+        filter.setTo(end);
+        return filter;
+    }
+
+    public int getDays() {
+        return (int) (to.toLocalDate().toEpochDay() - from.toLocalDate().toEpochDay());
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDevice.java b/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDevice.java
new file mode 100644 (file)
index 0000000..7c3c87a
--- /dev/null
@@ -0,0 +1,54 @@
+package applica.app.domain.rfid;
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "rfid-device", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN" },
+        listPermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_MAINTAINER", "ROLE_CUSTOMER", "ROLE_OPERATOR" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@Document
+public class RFIDDevice {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @NotBlank
+    @Keyword
+    String name;
+
+    @NotBlank
+    @Keyword
+    String code;
+
+    @NotNull
+    RFIDDeviceType type;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceRepository.java b/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceRepository.java
new file mode 100644 (file)
index 0000000..beb4881
--- /dev/null
@@ -0,0 +1,9 @@
+package applica.app.domain.rfid;
+
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.Optional;
+
+public interface RFIDDeviceRepository extends CrudRepository<RFIDDevice, String> {
+    Optional<RFIDDevice> findFirstByCodeAndType(String code, RFIDDeviceType type);
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrack.java b/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrack.java
new file mode 100644 (file)
index 0000000..3c5dbed
--- /dev/null
@@ -0,0 +1,61 @@
+package applica.app.domain.rfid;
+
+import applica.app.domain.customer.Area;
+import applica.app.domain.supply.Supply;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "rfid-device-track", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_MAINTAINER", "ROLE_CUSTOMER", "ROLE_OPERATOR" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@Document
+public class RFIDDeviceTrack {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = RFIDDevice.class)
+    @JsonMaterialize(destination = "tag", entityType = RFIDDevice.class)
+    String tagId;
+
+    @Indexed
+    @ForeignKey(primaryType = Supply.class)
+    String supplyId;
+
+    @Indexed
+    @ForeignKey(primaryType = RFIDDevice.class)
+    @JsonMaterialize(destination = "gateway", entityType = RFIDDevice.class)
+    String gatewayId;
+
+    @Indexed
+    @ForeignKey(primaryType = Area.class)
+    String areaId;
+
+    String additionalData;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime ts;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrackRepository.java b/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrackRepository.java
new file mode 100644 (file)
index 0000000..83e27f7
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.rfid;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface RFIDDeviceTrackRepository extends CrudRepository<RFIDDeviceTrack, String>, RFIDDeviceTrackStatsRepository {
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrackStatsRepository.java b/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrackStatsRepository.java
new file mode 100644 (file)
index 0000000..439a832
--- /dev/null
@@ -0,0 +1,10 @@
+package applica.app.domain.rfid;
+
+import applica.app.domain.reporting.DataPointSet;
+import applica.app.domain.reporting.TemporalFilter;
+
+public interface RFIDDeviceTrackStatsRepository {
+    DataPointSet getTotalTrackedRFID(TemporalFilter temporalFilter);
+    DataPointSet getMonthlyTotalTrackedRFID(TemporalFilter temporalFilter);
+    DataPointSet getTagsAndGatewaysNames(TemporalFilter temporalFilter);
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrackStatsRepositoryImpl.java b/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceTrackStatsRepositoryImpl.java
new file mode 100644 (file)
index 0000000..76b5405
--- /dev/null
@@ -0,0 +1,104 @@
+package applica.app.domain.rfid;
+
+import applica.app.domain.reporting.DataPoint;
+import applica.app.domain.reporting.DataPointSet;
+import applica.app.domain.reporting.TemporalFilter;
+import applica.app.domain.supply.Supply;
+import com.mongodb.BasicDBObject;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.data.mongodb.core.aggregation.ArithmeticOperators;
+
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@SuppressWarnings("unused")
+public class RFIDDeviceTrackStatsRepositoryImpl implements RFIDDeviceTrackStatsRepository{
+
+    private final MongoTemplate mongoTemplate;
+    public RFIDDeviceTrackStatsRepositoryImpl(MongoTemplate mongoTemplate) {
+        this.mongoTemplate = mongoTemplate;
+    }
+
+    @Override
+    public DataPointSet getTotalTrackedRFID(TemporalFilter temporalFilter) {
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            add(match(where("ts").gte(temporalFilter.getFrom()).lte(temporalFilter.getTo())));
+            add(lookup("area", "areaId", "_id", "areas"));
+            add(unwind("areas"));
+            add(lookup("office", "areas.officeId", "_id", "office"));
+            add(unwind("office"));
+            temporalFilter.getOfficeId().ifPresent(officeId -> add(match(where("office._id").is(officeId))));
+
+            add(count().as("totalTrackedRFID"));
+
+            add(project().andInclude("totalTrackedRFID")
+                    .andExclude("_id"));
+        }};
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var dataPoints = mongoTemplate.aggregate(aggregation, RFIDDeviceTrack.class, DataPoint.class);
+        return new DataPointSet(dataPoints.getMappedResults());
+    }
+
+    @Override
+    public DataPointSet getMonthlyTotalTrackedRFID(TemporalFilter temporalFilter) {
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            add(match(where("ts").gte(temporalFilter.getFrom()).lte(temporalFilter.getTo())));
+            add(lookup("area", "areaId", "_id", "areas"));
+            add(unwind("areas"));
+            add(lookup("office", "areas.officeId", "_id", "office"));
+            add(unwind("office"));
+            temporalFilter.getOfficeId().ifPresent(officeId -> add(match(where("office._id").is(officeId))));
+
+            add(group("supplyId"));
+
+            add(count().as("totalUniqueSupplyId"));
+
+            add(project().andInclude("totalUniqueSupplyId").andExclude("_id"));
+        }};
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var dataPoints = mongoTemplate.aggregate(aggregation, RFIDDeviceTrack.class, DataPoint.class);
+        return new DataPointSet(dataPoints.getMappedResults());
+
+    }
+
+    @Override
+    public DataPointSet getTagsAndGatewaysNames(TemporalFilter temporalFilter) {
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            add(lookup("area", "areaId", "_id", "areas"));
+            add(unwind("areas", true));
+            add(lookup("office", "areas.officeId", "_id", "office"));
+            add(unwind("office", true));
+            temporalFilter.getOfficeId().ifPresent(officeId -> add(match(where("office._id").is(officeId))));
+
+            add(lookup("rFIDDevice", "tagId", "_id", "tag"));
+            add(lookup("rFIDDevice", "gatewayId", "_id", "gateway"));
+
+            add(group("tagId", "gatewayId")
+                    .first("tagId").as("tagId")
+                    .first("tag.name").as("tagName")
+                    .first("gatewayId").as("gatewayId")
+                    .first("gateway.name").as("gatewayName"));
+
+            add(group()
+                    .addToSet(new BasicDBObject("tagId", "$tagId")
+                            .append("tagName", "$tagName")).as("tags")
+                    .addToSet(new BasicDBObject("gatewayId", "$gatewayId")
+                            .append("gatewayName", "$gatewayName")).as("gateways"));
+
+        }};
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var dataPoints = mongoTemplate.aggregate(aggregation, RFIDDeviceTrack.class, DataPoint.class);
+        return new DataPointSet(dataPoints.getMappedResults());
+
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceType.java b/edera-api/api/src/main/java/applica/app/domain/rfid/RFIDDeviceType.java
new file mode 100644 (file)
index 0000000..755d621
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.rfid;
+
+public enum RFIDDeviceType {
+    TAG,
+    GATEWAY
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supplier/Referent.java b/edera-api/api/src/main/java/applica/app/domain/supplier/Referent.java
new file mode 100644 (file)
index 0000000..5194ee2
--- /dev/null
@@ -0,0 +1,66 @@
+package applica.app.domain.supplier;
+
+
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "supplier-referent", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" })
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Referent {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Supplier.class)
+    @JsonMaterialize(entityType = Supplier.class, destination = "supplier")
+    String supplierId;
+
+    @NotBlank
+    @Keyword
+    String name;
+
+    @NotBlank
+    @Keyword
+    String surname;
+
+    @NotBlank
+    @Keyword
+    String email;
+
+    @Keyword
+    String phone;
+
+    @NotBlank
+    @Keyword
+    String department;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supplier/Supplier.java b/edera-api/api/src/main/java/applica/app/domain/supplier/Supplier.java
new file mode 100644 (file)
index 0000000..b32c47f
--- /dev/null
@@ -0,0 +1,61 @@
+package applica.app.domain.supplier;
+
+import applica.app.domain.geo.City;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.query.Keyword;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "supplier", completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" }, listPermissionsRoles = {"ROLE_MAINTAINER"})
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@Document
+public class Supplier {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = City.class)
+    @Keyword
+    @JsonMaterialize(entityType = City.class, destination = "city")
+    String cityId;
+
+    @NotBlank
+    @Keyword
+    String businessName;
+
+    @NotBlank
+    @Keyword
+    String vatNumber;
+
+    @Keyword
+    String address;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supplier/SupplierRepository.java b/edera-api/api/src/main/java/applica/app/domain/supplier/SupplierRepository.java
new file mode 100644 (file)
index 0000000..0e32a1d
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.supplier;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface SupplierRepository extends CrudRepository<Supplier, String> {
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supply/Supply.java b/edera-api/api/src/main/java/applica/app/domain/supply/Supply.java
new file mode 100644 (file)
index 0000000..e74e772
--- /dev/null
@@ -0,0 +1,137 @@
+package applica.app.domain.supply;
+
+import applica.app.domain.customer.Area;
+import applica.app.domain.customer.Customer;
+import applica.app.domain.equipment.Equipment;
+import applica.app.domain.equipment.EquipmentType;
+import applica.app.domain.protocol.Protocol;
+import applica.app.domain.rfid.RFIDDevice;
+import applica.app.domain.rfid.RFIDDeviceTrack;
+import applica.app.domain.supplier.Supplier;
+import applica.app.web.annotations.CustomerFilterable;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.annotations.JsonMaterialize;
+import applica.crud.constraints.ForeignKey;
+import applica.crud.operations.OperationException;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotNull;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@CrudEntity(value = "supply",
+        completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR" },
+        listPermissionsRoles = {"ROLE_MAINTAINER", "ROLE_CUSTOMER", "ROLE_USER"},
+        savePermissionsRoles = {"ROLE_MAINTAINER", "ROLE_CUSTOMER"}
+)
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@CustomerFilterable
+@Document
+public class Supply {
+
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    @ForeignKey(primaryType = Supplier.class)
+    @JsonMaterialize(destination = "supplier", entityType = Supplier.class)
+    String supplierId;
+
+    @Indexed
+    @ForeignKey(primaryType = Equipment.class)
+    String equipmentId;
+
+    @Indexed
+    @ForeignKey(primaryType = Protocol.class)
+    String protocolId;
+
+    @Indexed
+    @ForeignKey(primaryType = RFIDDevice.class)
+    @JsonMaterialize(destination = "rfidDevice", entityType = RFIDDevice.class)
+    String rfidDeviceId;
+
+    @Indexed
+    @ForeignKey(primaryType = Area.class)
+    @JsonMaterialize(destination = "area", entityType = Area.class)
+    String areaId;
+
+    @Indexed
+    String usageId;
+
+    @NotNull
+    int quantity;
+    /**
+     * Numero di ore di funzionamento dopo le quali l'attrezzatura deve essere sottoposta a manutenzione per probabile guasto.
+     * Questo campo, se valorizzato, prevale sullo stesso campo di EquipmentType.
+     */
+    int breakpointInHours;
+    /**
+     * Numero di ore di funzionamento accumulate dall'attrezzatura.
+     */
+    double accumulatedHours;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    LocalDate orderDate;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    LocalDate deliveryDate;
+
+    String serialNumber;
+
+    String ddtNumber;
+
+    UsageStatus usageStatus;
+
+    RFIDDeviceTrack latestRfidTrack;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+
+    transient Protocol protocol;
+    transient EquipmentType equipmentType;
+    transient Equipment equipment;
+    transient Customer customer;
+    transient Supplier supplier;
+    transient List<Usage> usages;
+    transient Area area;
+    transient Area latestArea;
+
+    public void setUsage(Usage usage) {
+        this.usageId = usage.getId();
+        this.usageStatus = UsageStatus.RUNNING;
+    }
+
+    public void stopUsage(Usage usage) {
+        if (this.usageId != null) {
+            this.usageId = null;
+            this.usageStatus = UsageStatus.STOPPED;
+            this.accumulatedHours += usage.getDurationInHours();
+        }
+        else {
+            throw new OperationException("ra.error.supply-not-in-use");
+        }
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supply/SupplyRepository.java b/edera-api/api/src/main/java/applica/app/domain/supply/SupplyRepository.java
new file mode 100644 (file)
index 0000000..444ed35
--- /dev/null
@@ -0,0 +1,12 @@
+package applica.app.domain.supply;
+
+
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface SupplyRepository extends CrudRepository<Supply, String>, SupplyStatsRepository {
+    List<Supply> findByProtocolId(String protocolId);
+    Optional<Supply> findFirstByRfidDeviceId (String rfidDeviceId);
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supply/SupplyStatsRepository.java b/edera-api/api/src/main/java/applica/app/domain/supply/SupplyStatsRepository.java
new file mode 100644 (file)
index 0000000..118836b
--- /dev/null
@@ -0,0 +1,25 @@
+package applica.app.domain.supply;
+
+import applica.app.domain.reporting.DataPoint;
+import applica.app.domain.reporting.DataPointSet;
+import applica.app.domain.reporting.TemporalFilter;
+import applica.app.domain.rfid.RFIDDeviceTrack;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+
+import java.util.ArrayList;
+
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+public interface SupplyStatsRepository {
+
+    DataPointSet getCustomUsage(TemporalFilter temporalFilter);
+    DataPointSet getTotalUsage(TemporalFilter temporalFilter);
+    DataPointSet getAverageUsage(TemporalFilter temporalFilter);
+    DataPointSet getMonthlyUtilizationPercentage(TemporalFilter temporalFilter);
+    DataPointSet getLatestTrackedSupplies(TemporalFilter temporalFilter);
+    DataPointSet getMonthlyTrackingPercentage(TemporalFilter temporalFilter);
+
+}
+
diff --git a/edera-api/api/src/main/java/applica/app/domain/supply/SupplyStatsRepositoryImpl.java b/edera-api/api/src/main/java/applica/app/domain/supply/SupplyStatsRepositoryImpl.java
new file mode 100644 (file)
index 0000000..eabd0b0
--- /dev/null
@@ -0,0 +1,279 @@
+package applica.app.domain.supply;
+
+import applica.app.domain.reporting.DataPoint;
+import applica.app.domain.reporting.DataPointSet;
+import applica.app.domain.reporting.GroupBy;
+import applica.app.domain.reporting.TemporalFilter;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.*;
+
+import java.time.Month;
+import java.time.Year;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@SuppressWarnings("unused")
+public class SupplyStatsRepositoryImpl implements SupplyStatsRepository {
+
+    private final MongoTemplate mongoTemplate;
+
+    public SupplyStatsRepositoryImpl(MongoTemplate mongoTemplate) {
+        this.mongoTemplate = mongoTemplate;
+    }
+
+    @Override
+    public DataPointSet getCustomUsage(TemporalFilter temporalFilter) {
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            temporalFilter.getSupplyId().ifPresent(supplyId -> add(match(where("_id").is(supplyId))));
+            add(lookup("usage", "_id", "supplyId", "usages"));
+            add(unwind("usages"));
+
+
+            var suggestedGroupBy = temporalFilter.getSuggestedGroupBy();
+            var divideBy = suggestedGroupBy == GroupBy.HOUR ? 3600000 : 86400000;
+
+            add(temporalFilter.between("usages.start"));
+            add(temporalFilter
+                    .project("usages.start")
+                    .and(ArithmeticOperators.Divide.valueOf(
+                            ArithmeticOperators.Subtract.valueOf("usages.stop").subtract("usages.start")
+                    ).divideBy(divideBy)).as("total")
+            );
+            add(temporalFilter.group().sum("total").as("total"));
+            add(temporalFilter.projectionAs().andInclude("total"));
+
+            add(temporalFilter.sort());
+        }};
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var rawResults = mongoTemplate.aggregate(aggregation, Supply.class, DataPoint.class).getMappedResults();
+
+
+        List<DataPoint> transformedResults = new ArrayList<>();
+        for (DataPoint rawResult : rawResults) {
+            double totalUsageHours;
+            if (rawResult.get("hours") != null) {
+                totalUsageHours = (double) rawResult.get("hours");
+            } else {
+                return new DataPointSet(rawResults);
+            }
+            if (totalUsageHours > 24)
+            {
+                int fullDays = (int) (totalUsageHours / 24);
+                double remainingHours = totalUsageHours % 24;
+
+                for (int i = 0; i < fullDays; i++) {
+                    DataPoint transformedResult = new DataPoint();
+                    int year = (int) rawResult.get("year");
+                    int month = (int) rawResult.get("month");
+                    int day = (int) rawResult.get("day") + i;
+
+                    if (day > Month.of(month).length(Year.isLeap(year))) {
+                        day = 1;
+                        month++;
+                        if (month > 12) {
+                            month = 1;
+                            year++;
+                        }
+                    }
+
+                    transformedResult.put("year", year);
+                    transformedResult.put("month", month);
+                    transformedResult.put("day", day);
+                    transformedResult.put("totalUsageHours", 24);
+                    transformedResults.add(transformedResult);
+                }
+                if (remainingHours > 0) {
+                    DataPoint transformedResult = new DataPoint();
+                    int year = (int) rawResult.get("year");
+                    int month = (int) rawResult.get("month");
+                    int day = (int) rawResult.get("day") + fullDays;
+
+                    if (day > Month.of(month).length(Year.isLeap(year))) {
+                        day = 1;
+                        month++;
+                        if (month > 12) {
+                            month = 1;
+                            year++;
+                        }
+                    }
+
+                    transformedResult.put("year", year);
+                    transformedResult.put("month", month);
+                    transformedResult.put("day", day);
+                    transformedResult.put("totalUsageHours", remainingHours);
+                    transformedResults.add(transformedResult);
+                }
+            }
+            else {
+                transformedResults.add(rawResult);
+            }
+        }
+
+        return new DataPointSet(transformedResults);
+    }
+
+    @Override
+    public DataPointSet getTotalUsage(TemporalFilter temporalFilter) {
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            temporalFilter.getSupplyId().ifPresent(supplyId -> add(match(where("_id").is(supplyId))));
+            add(lookup("usage", "_id", "supplyId", "usages"));
+            add(unwind("usages"));
+            add(match(where("usages.start").gte(temporalFilter.getFrom()).lte(temporalFilter.getTo())));
+
+            long daysBetween = ChronoUnit.DAYS.between(temporalFilter.getFrom(), temporalFilter.getTo());
+            boolean useHours = daysBetween <= 60;
+
+            if (useHours) {
+                add(project()
+                        .and(ArithmeticOperators.Divide.valueOf(
+                                ArithmeticOperators.Subtract.valueOf("usages.stop").subtract("usages.start")
+                        ).divideBy(3600000)).as("usageDurationInHours")
+                );
+                add(group().sum("usageDurationInHours").as("totalUsageHours"));
+                add(project()
+                        .andInclude("totalUsageHours")
+                );
+            } else {
+                add(project()
+                        .and(ArithmeticOperators.Divide.valueOf(
+                                ArithmeticOperators.Subtract.valueOf("usages.stop").subtract("usages.start")
+                        ).divideBy(86400000)).as("usageDurationInDays")
+                );
+                add(group().sum("usageDurationInDays").as("totalUsageDays"));
+                add(project()
+                        .andInclude("totalUsageDays")
+                );
+            }
+        }};
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var dataPoints = mongoTemplate.aggregate(aggregation, Supply.class, DataPoint.class);
+        return new DataPointSet(dataPoints.getMappedResults());
+    }
+
+
+    @Override
+    public DataPointSet getAverageUsage(TemporalFilter temporalFilter) {
+
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            temporalFilter.getSupplyId().ifPresent(supplyId -> add(match(where("_id").is(supplyId))));
+            add(lookup("usage", "_id", "supplyId", "usages"));
+            add(unwind("usages"));
+            add(match(where("usages.start").gte(temporalFilter.getFrom()).lte(temporalFilter.getTo())));
+            add(group().sum("usage.start").as("totalUsages").count().as("count"));
+            add(project()
+                    .andInclude("count")
+            );
+
+        }};
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var dataPoints = mongoTemplate.aggregate(aggregation, Supply.class, DataPoint.class);
+        return new DataPointSet(dataPoints.getMappedResults());
+    }
+
+    @Override
+    public DataPointSet getMonthlyUtilizationPercentage(TemporalFilter temporalFilter) {
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            temporalFilter.getSupplyId().ifPresent(supplyId -> add(match(where("_id").is(supplyId))));
+            add(lookup("usage", "_id", "supplyId", "usages"));
+            add(unwind("usages"));
+            add(match(where("usages.start").gte(temporalFilter.getFrom()).lte(temporalFilter.getTo())));
+            add(temporalFilter.projectionAs()
+                    .and(ArithmeticOperators.Multiply.valueOf(
+                            ArithmeticOperators.Divide.valueOf("totalUsageDuration")
+                                    .divideBy(ChronoUnit.HOURS.between(
+                                            temporalFilter.getFrom(), temporalFilter.getTo())
+                                    )
+                            )
+                            .multiplyBy(100)).as("utilizationPercentage")
+            );
+        }};
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var dataPoints = mongoTemplate.aggregate(aggregation, Supply.class, DataPoint.class);
+        return new DataPointSet(dataPoints.getMappedResults());
+    }
+
+    @Override
+    public DataPointSet getLatestTrackedSupplies(TemporalFilter temporalFilter) {
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            add(lookup("area", "latestRfidTrack.areaId", "_id", "areas"));
+            add(unwind("areas", true));
+            add(lookup("office", "areas.officeId", "_id", "office"));
+            add(unwind("office", true));
+            temporalFilter.getOfficeId().ifPresent(officeId -> add(match(where("office._id").is(officeId))));
+            temporalFilter.getGatewayId().ifPresent(gatewayId -> add(
+                    match(where("latestRfidTrack.gatewayId").is(gatewayId))
+            ));
+            temporalFilter.getTagId().ifPresent(tagId -> add(match(where("latestRfidTrack.tagId").is(tagId))));
+
+            if (temporalFilter.getTo() != null && temporalFilter.getFrom() != null) {
+                add(match(where("latestRfidTrack.ts").gte(temporalFilter.getFrom()).lte(temporalFilter.getTo())));
+            }
+
+            add(match(where("latestRfidTrack").exists(true)));
+
+            add(lookup("equipment", "equipmentId", "_id", "equipment"));
+            add(unwind("equipment"));
+            add(lookup("equipmentType", "equipment.equipmentTypeId", "_id", "equipmentType"));
+            add(unwind("equipmentType"));
+            add(lookup("rFIDDevice", "latestRfidTrack.gatewayId", "_id", "gateway"));
+            add(unwind("gateway"));
+            add(lookup("rFIDDevice", "latestRfidTrack.tagId", "_id", "tag"));
+            add(unwind("tag"));
+
+            add(project().andInclude("latestRfidTrack")
+                    .and("equipmentType.name").as("equipmentTypeName")
+                    .and("area.description").as("areaDescription")
+                    .and("tag.code").as("tagCode")
+                    .and("gateway.code").as("gatewayCode")
+                    .andExclude("_id"));
+        }};
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var dataPoints = mongoTemplate.aggregate(aggregation, Supply.class, DataPoint.class);
+        return new DataPointSet(dataPoints.getMappedResults());
+    }
+
+    @Override
+    public DataPointSet getMonthlyTrackingPercentage(TemporalFilter temporalFilter) {
+
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            add(lookup("area", "latestRfidTrack.areaId", "_id", "areas"));
+            add(unwind("areas", true));
+            add(lookup("office", "areas.officeId", "_id", "office"));
+            add(unwind("office", true));
+            temporalFilter.getOfficeId().ifPresent(officeId -> add(match(where("office._id").is(officeId))));
+
+            add(match(where("latestRfidTrack").exists(true)));
+            add(match(where("latestRfidTrack.ts").gte(temporalFilter.getFrom()).lte(temporalFilter.getTo())));
+
+            add(group().count().as("uniqueSupplyCount")
+                    .addToSet("$_id").as("uniqueSupplies"));
+
+            add(group().sum("uniqueSupplyCount").as("totalSupplies")
+                    .first("uniqueSupplies").as("uniqueSuppliesList"));
+
+            add(project()
+                    .and(ArithmeticOperators.Multiply.valueOf(
+                                    ArithmeticOperators.Divide.valueOf("totalSupplies")
+                                            .divideBy(ArrayOperators.Size.lengthOfArray("uniqueSuppliesList"))
+                            )
+                            .multiplyBy(100)).as("trackingPercentage")
+            );
+        }};
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var dataPoints = mongoTemplate.aggregate(aggregation, Supply.class, DataPoint.class);
+        return new DataPointSet(dataPoints.getMappedResults());
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supply/Usage.java b/edera-api/api/src/main/java/applica/app/domain/supply/Usage.java
new file mode 100644 (file)
index 0000000..a8a5383
--- /dev/null
@@ -0,0 +1,68 @@
+package applica.app.domain.supply;
+
+import applica.app.web.annotations.CustomerFilterable;
+import applica.crud.annotations.CrudEntity;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.mongodb.core.index.Indexed;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
+
+import java.time.LocalDateTime;
+
+@CrudEntity(value = "usage",
+        completePermissionsRoles = { "ROLE_SUPERUSER", "ROLE_ADMIN", "ROLE_OPERATOR", "ROLE_MAINTAINER", "ROLE_CUSTOMER" }
+)
+@AllArgsConstructor
+@Getter
+@Setter
+@NoArgsConstructor
+@ToString
+@EqualsAndHashCode
+@FieldDefaults(level = AccessLevel.PRIVATE)
+@CustomerFilterable
+@Document
+public class Usage {
+    @Id
+    @MongoId(targetType = FieldType.STRING)
+    String id;
+
+    @Indexed
+    String supplyId;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime start;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    LocalDateTime stop;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @CreatedDate
+    LocalDateTime created;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @LastModifiedDate
+    LocalDateTime updated;
+
+    public static Usage start(String supplyId) {
+        Usage usage = new Usage();
+        usage.setSupplyId(supplyId);
+        usage.setStart(LocalDateTime.now());
+        return usage;
+    }
+
+    public void stop() {
+        this.setStop(LocalDateTime.now());
+    }
+
+    @JsonIgnore
+    public int getDurationInHours() {
+        return (int) Math.ceil((double) (stop.getSecond() - start.getSecond()) / 3600);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supply/UsageOverlapUtilsRepository.java b/edera-api/api/src/main/java/applica/app/domain/supply/UsageOverlapUtilsRepository.java
new file mode 100644 (file)
index 0000000..696ffbb
--- /dev/null
@@ -0,0 +1,7 @@
+package applica.app.domain.supply;
+
+import java.time.LocalDateTime;
+
+public interface UsageOverlapUtilsRepository {
+    int countOverlappingUsages(String supplyId, LocalDateTime start, LocalDateTime end);
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supply/UsageOverlapUtilsRepositoryImpl.java b/edera-api/api/src/main/java/applica/app/domain/supply/UsageOverlapUtilsRepositoryImpl.java
new file mode 100644 (file)
index 0000000..b8e42e3
--- /dev/null
@@ -0,0 +1,30 @@
+package applica.app.domain.supply;
+
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Query;
+
+import java.time.LocalDateTime;
+
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@SuppressWarnings("unused")
+public class UsageOverlapUtilsRepositoryImpl implements UsageOverlapUtilsRepository {
+    private final MongoTemplate mongoTemplate;
+
+    public UsageOverlapUtilsRepositoryImpl(MongoTemplate mongoTemplate) {
+        this.mongoTemplate = mongoTemplate;
+    }
+
+    @Override
+    public int countOverlappingUsages(String supplyId, LocalDateTime start, LocalDateTime end) {
+        var query = new Query();
+        query.addCriteria(
+            where("supplyId").is(supplyId)
+            .andOperator(
+                where("start").lte(end),
+                where("end").gte(start)
+            )
+        );
+        return (int) mongoTemplate.count(query, Usage.class);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supply/UsageRepository.java b/edera-api/api/src/main/java/applica/app/domain/supply/UsageRepository.java
new file mode 100644 (file)
index 0000000..427f8a8
--- /dev/null
@@ -0,0 +1,9 @@
+package applica.app.domain.supply;
+
+import org.springframework.data.repository.CrudRepository;
+
+import java.util.List;
+
+public interface UsageRepository extends CrudRepository<Usage, String>, UsageOverlapUtilsRepository {
+    List<Usage> findBySupplyId(String supplyId);
+}
diff --git a/edera-api/api/src/main/java/applica/app/domain/supply/UsageStatus.java b/edera-api/api/src/main/java/applica/app/domain/supply/UsageStatus.java
new file mode 100644 (file)
index 0000000..a7e5c63
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.domain.supply;
+
+public enum UsageStatus {
+    RUNNING,
+    STOPPED
+}
diff --git a/edera-api/api/src/main/java/applica/app/mapping/Mapper.java b/edera-api/api/src/main/java/applica/app/mapping/Mapper.java
new file mode 100644 (file)
index 0000000..3a70cbd
--- /dev/null
@@ -0,0 +1,42 @@
+package applica.app.mapping;
+
+import applica.app.domain.Device;
+import applica.app.domain.User;
+import applica.iam.sdk.readmodel.DeviceView;
+import applica.iam.sdk.readmodel.UserView;
+import org.apache.commons.beanutils.PropertyUtils;
+
+import static applica.crud.utils.LangUtils.unchecked;
+
+public class Mapper {
+
+    public static User userViewToUser(UserView userView) {
+        if (userView == null) {
+            return null;
+        }
+        var user = new User();
+        unchecked(() -> PropertyUtils.copyProperties(user, userView));
+        user.setId(userView.getId());
+        user.setName(userView.getProfile().getOrDefault("name", null));
+        user.setImage(userView.getProfile().getOrDefault("image", null));
+        user.setCustomerId(userView.getProfile().getOrDefault("customerId", null));
+        user.setRegistrationDate(userView.getRegistrationDate());
+
+        return user;
+    }
+
+    public static Device deviceViewToDevice(DeviceView deviceView) {
+        if (deviceView == null) {
+            return null;
+        }
+        var device = new Device();
+        unchecked(() -> PropertyUtils.copyProperties(device, deviceView));
+
+        device.setCode(deviceView.getCode());
+        device.setSecret(deviceView.getSecret());
+        device.setRegistrationDate(deviceView.getRegistrationDate());
+
+        return device;
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/ml/MLClient.java b/edera-api/api/src/main/java/applica/app/ml/MLClient.java
new file mode 100644 (file)
index 0000000..6ddf2fe
--- /dev/null
@@ -0,0 +1,52 @@
+package applica.app.ml;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.Getter;
+import org.apache.commons.logging.Log;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+
+/**
+ * Machine Learning client used to communicate with python server.
+ */
+public class MLClient {
+    private final Log logger = org.apache.commons.logging.LogFactory.getLog(MLClient.class);
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    @Getter
+    String endpoint;
+    HttpClient client = null;
+
+    public MLClient(String endpoint) {
+        this.endpoint = endpoint;
+        this.client = HttpClient.newHttpClient();
+    }
+
+    /**
+     * Send a prediction request to the machine learning server.
+     * @param request Prediction request.
+     * @return Prediction response.
+     */
+    public MLResponse predict(MLRequest request) {
+        var requestBuilder = HttpRequest.newBuilder(URI.create(endpoint + "/predict"));
+
+        try {
+            var json = mapper.writeValueAsString(request);
+            var body = java.net.http.HttpRequest.BodyPublishers.ofString(json);
+            var requestWithBody = requestBuilder.POST(body)
+                    .header("Content-Type", "application/json")
+                    .header("Accept", "application/json")
+                    .build();
+            var response = client.send(requestWithBody, java.net.http.HttpResponse.BodyHandlers.ofString());
+            if (response.statusCode() != 200) {
+                throw new Exception(String.format("Error %d: %s", response.statusCode(), response.body()));
+            }
+            return mapper.readValue(response.body(), MLResponse.class);
+        } catch (Exception e) {
+            logger.error(e.getMessage());
+            return MLResponse.failed(e);
+        }
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/ml/MLRequest.java b/edera-api/api/src/main/java/applica/app/ml/MLRequest.java
new file mode 100644 (file)
index 0000000..eb1658b
--- /dev/null
@@ -0,0 +1,85 @@
+package applica.app.ml;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Machine Learning request.
+ */
+@Getter
+@Setter
+public class MLRequest {
+    /**
+     * Type of machine for which you are requesting the prediction.
+     */
+    int machine;
+    /**
+     * Day of week from which you are requesting the prediction.
+     */
+    int day;
+    /**
+     * Number of next days for which you are requesting the prediction.
+     */
+    @JsonProperty("max_days")
+    Integer maxDays;
+    /**
+     * Max number of hours for which you are requesting the prediction.
+     * For example you can ask to predict usage based on working hours (8) or 24 hours.
+     */
+    @JsonProperty("max_hours")
+    Integer maxHours;
+    /**
+     * Accumulated hours until now.
+     */
+    @JsonProperty("accumulated_hours")
+    int accumulatedHours;
+    /**
+     * Number of total hours after which the machine will be considered as broken.
+     */
+    @JsonProperty("breakpoint_hours")
+    int breakpointHours;
+    /**
+     * Machine Learning model threshold.
+     */
+    float threshold;
+
+    public MLRequest(int machine, int day) {
+        this.machine = machine;
+        this.day = day;
+    }
+
+    public MLRequest withAccumulatedHours(int accumulatedHours) {
+        this.accumulatedHours = accumulatedHours;
+        return this;
+    }
+
+    public MLRequest withBreakpointHours(int breakpointHours) {
+        this.breakpointHours = breakpointHours;
+        return this;
+    }
+
+    public MLRequest withMaxDays(int maxDays) {
+        this.maxDays = maxDays;
+        return this;
+    }
+
+    public MLRequest withMaxHours(int maxHours) {
+        this.maxHours = maxHours;
+        return this;
+    }
+
+    public MLRequest withThreshold(float threshold) {
+        this.threshold = threshold;
+        return this;
+    }
+
+    public MLRequest build() {
+        return this;
+    }
+
+    public static MLRequest of(int machine, int day) {
+        return new MLRequest(machine, day);
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/ml/MLResponse.java b/edera-api/api/src/main/java/applica/app/ml/MLResponse.java
new file mode 100644 (file)
index 0000000..b7818f6
--- /dev/null
@@ -0,0 +1,24 @@
+package applica.app.ml;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayList;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class MLResponse extends ArrayList<PredictionItem> {
+    String error;
+    boolean failed;
+
+    public static MLResponse failed(Exception e) {
+        var response = new MLResponse();
+        response.setFailed(true);
+        response.setError(e.getMessage());
+        return response;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/ml/PredictionItem.java b/edera-api/api/src/main/java/applica/app/ml/PredictionItem.java
new file mode 100644 (file)
index 0000000..86c53d4
--- /dev/null
@@ -0,0 +1,17 @@
+package applica.app.ml;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class PredictionItem {
+    int day;
+    int hours;
+    @JsonProperty("accumulated_hours")
+    int accumulatedHours;
+    boolean broken;
+}
diff --git a/edera-api/api/src/main/java/applica/app/services/EderaService.java b/edera-api/api/src/main/java/applica/app/services/EderaService.java
new file mode 100644 (file)
index 0000000..7b9a262
--- /dev/null
@@ -0,0 +1,245 @@
+package applica.app.services;
+
+import applica.app.domain.customer.Area;
+import applica.app.domain.customer.AreaRepository;
+import applica.app.domain.dto.ActivityDetailsDTO;
+import applica.app.domain.dto.SupplyDetailsDTO;
+import applica.app.domain.equipment.Equipment;
+import applica.app.domain.equipment.EquipmentType;
+import applica.app.domain.maintenance.Activity;
+import applica.app.domain.protocol.Protocol;
+import applica.app.domain.rfid.*;
+import applica.app.domain.supplier.Supplier;
+import applica.app.domain.supply.Supply;
+import applica.app.domain.supply.SupplyRepository;
+import applica.app.domain.supply.Usage;
+import applica.app.services.reporting.ReportFactory;
+import applica.app.services.reporting.impl.SupplyReport;
+import applica.app.services.reporting.impl.SupplyTracksReport;
+import applica.app.web.requests.GetReportDataRequest;
+import applica.app.web.responses.GetReportDataResponse;
+import applica.crud.datalayer.DataLayerAwareAdapter;
+import applica.crud.operations.OperationException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Map;
+
+@Component
+public class EderaService extends DataLayerAwareAdapter {
+
+    private final SupplyRepository supplyRepository;
+    private final RFIDDeviceRepository rfidDeviceRepository;
+    private final RFIDDeviceTrackRepository rfidDeviceTrackRepository;
+    private final AreaRepository areaRepository;
+    private final Log log = LogFactory.getLog(getClass());
+    private final ObjectMapper objectMapper;
+    private final ReportFactory reportFactory;
+
+    public EderaService(SupplyReport supplyReport,
+                        SupplyTracksReport rfidDeviceTrackingReport,
+                        SupplyRepository supplyRepository,
+                        RFIDDeviceRepository rfidDeviceRepository,
+                        RFIDDeviceTrackRepository rfidDeviceTrackRepository,
+                        AreaRepository areaRepository,
+                        ObjectMapper objectMapper,
+                        ReportFactory reportFactory) {
+        this.supplyRepository = supplyRepository;
+        this.rfidDeviceRepository = rfidDeviceRepository;
+        this.rfidDeviceTrackRepository = rfidDeviceTrackRepository;
+        this.areaRepository = areaRepository;
+        this.objectMapper = objectMapper;
+        this.reportFactory = reportFactory;
+    }
+
+    public ActivityDetailsDTO getActivityDetails(String activityId) {
+        var activity = dal(Activity.class).get(activityId);
+        if (activity.isEmpty()) {
+            throw new OperationException("ra.error.activity-not-found");
+        }
+        var supply = dal(Supply.class).get(activity.get().getSupplyId());
+        if (supply.isEmpty()) {
+            throw new OperationException("ra.error.supply-not-found");
+        }
+        var equipment = dal(Equipment.class).get(supply.get().getEquipmentId());
+        if (equipment.isEmpty()) {
+            throw new OperationException("ra.error.equipment-not-found");
+        }
+        var supplier = dal(Supplier.class).get(supply.get().getSupplierId());
+        if (supplier.isEmpty()) {
+            throw new OperationException("ra.error.supplier-not-found");
+        }
+        var locationName = "";
+        if (StringUtils.hasLength(supply.get().getRfidDeviceId())) {
+            var rfid = dal(RFIDDevice.class).get(supply.get().getRfidDeviceId());
+            if (rfid.isEmpty()) {
+                throw new OperationException("ra.error.rfid-not-found");
+            }
+            locationName = rfid.get().getName();
+        }
+        else if (StringUtils.hasLength(supply.get().getAreaId())) {
+            var area = dal(Area.class).get(supply.get().getAreaId());
+            if (area.isEmpty()) {
+                throw new OperationException("ra.error.area-not-found");
+            }
+            locationName = area.get().getName();
+        }
+        var equipmentType = dal(EquipmentType.class).get(equipment.get().getEquipmentTypeId());
+        if (equipmentType.isEmpty()) {
+            throw new OperationException("ra.error.equipment-type-not-found");
+        }
+
+        ActivityDetailsDTO activityDetailsDTO = new ActivityDetailsDTO();
+        activityDetailsDTO.setEquipmentName(equipmentType.get().getName());
+        activityDetailsDTO.setSupplierName(supplier.get().getBusinessName());
+        activityDetailsDTO.setLocationName(locationName);
+        return activityDetailsDTO;
+    }
+
+    public SupplyDetailsDTO getSupplyDetails(String supplyId) {
+        var supply = dal(Supply.class).get(supplyId);
+        if (supply.isEmpty()) {
+            throw new OperationException("ra.error.supply-not-found");
+        }
+        var equipment = dal(Equipment.class).get(supply.get().getEquipmentId());
+        if (equipment.isEmpty()) {
+            throw new OperationException("ra.error.equipment-not-found");
+        }
+        var supplier = dal(Supplier.class).get(supply.get().getSupplierId());
+        if (supplier.isEmpty()) {
+            throw new OperationException("ra.error.supplier-not-found");
+        }
+        var protocol = dal(Protocol.class).get(supply.get().getProtocolId());
+        if (protocol.isEmpty()) {
+            throw new OperationException("ra.error.protocol-not-found");
+        }
+        var equipmentType = dal(EquipmentType.class).get(equipment.get().getEquipmentTypeId());
+        if (equipmentType.isEmpty()) {
+            throw new OperationException("ra.error.equipment-type-not-found");
+        }
+
+        SupplyDetailsDTO supplyDetailsDTO = new SupplyDetailsDTO();
+        supplyDetailsDTO.setEquipmentName(equipmentType.get().getName());
+        supplyDetailsDTO.setSupplierName(supplier.get().getBusinessName());
+        supplyDetailsDTO.setProtocolName(protocol.get().getTitle());
+        return supplyDetailsDTO;
+
+    }
+
+    public void startUsage(String supplyId){
+        Usage usage = Usage.start(supplyId);
+        var supply = dal(Supply.class).get(supplyId);
+        if (supply.isPresent()) {
+            if (supply.get().getUsageId() == null) {
+                dal(Usage.class).save(usage);
+                supply.get().setUsage(usage);
+                dal(Supply.class).save(supply.get());
+            }else {
+                throw new OperationException("ra.error.supply-already-in-use");
+            }
+        } else {
+            throw new OperationException("ra.error.supply-not-found");
+        }
+
+    }
+
+    public void stopUsage(String supplyId) {
+        var supply = dal(Supply.class).get(supplyId);
+
+        if (supply.isPresent()) {
+            var usage = dal(Usage.class).get(supply.get().getUsageId());
+            if (usage.isPresent()) {
+                usage.get().stop();
+                supply.get().stopUsage(usage.get());
+                dal(Supply.class).save(supply.get());
+                dal(Usage.class).save(usage.get());
+            } else {
+                throw new OperationException("ra.error.usage-not-found");
+            }
+        }else {
+            throw new OperationException("ra.error.supply-not-found");
+        }
+    }
+
+    public GetReportDataResponse getReportData(String code, GetReportDataRequest request) throws RuntimeException {
+        var report = reportFactory.get(code);
+        if (report == null) {
+            throw new RuntimeException("Report not found");
+        }
+        var data = report.execute(request);
+        return new GetReportDataResponse(data);
+    }
+
+    public void save(String gatewayId, String tagId, Long ts, Map<String, String> allParams) throws Exception {
+
+        allParams.remove("tag");
+        allParams.remove("timestamp");
+        allParams.remove("gate");
+
+        if (gatewayId == null || gatewayId.isEmpty()) {
+            log.error("Invalid gateway");
+            throw new Exception("Invalid gateway");
+        }
+
+        if (tagId == null || tagId.isEmpty()) {
+            log.error("Invalid tag");
+            throw new Exception("Invalid tag");
+        }
+
+        if (ts == null) {
+            ts = System.currentTimeMillis();
+        }
+
+        String additionalDataJson;
+        try {
+            additionalDataJson = objectMapper.writeValueAsString(allParams);
+        } catch (Exception e) {
+            log.error("Error converting additional params to JSON", e);
+            throw new Exception("Error processing additional data");
+        }
+
+        var tag = rfidDeviceRepository.findFirstByCodeAndType(tagId, RFIDDeviceType.TAG).orElse(null);
+
+        if (tag == null) {
+            log.error("Tag not found");
+            throw new Exception("Tag not found");
+        }
+        var gateway = rfidDeviceRepository.findFirstByCodeAndType(gatewayId, RFIDDeviceType.GATEWAY).orElse(null);
+        if (gateway == null) {
+            log.error("Gateway not found");
+            throw new Exception("Gateway not found");
+        }
+        var supply = supplyRepository.findFirstByRfidDeviceId(tag.getId()).orElse(null);
+        if (supply == null) {
+            log.error("Supply not found");
+            throw new Exception("Supply not found");
+        }
+        var area = areaRepository.findFirstByRfidDeviceId(gateway.getId()).orElse(null);
+        if (area == null) {
+            log.error("Area not found");
+            throw new Exception("Area not found");
+        }
+
+        RFIDDeviceTrack track = new RFIDDeviceTrack(
+                null,
+                tag.getId(),
+                supply.getId(),
+                gateway.getId(),
+                area.getId(),
+                additionalDataJson,
+                Instant.ofEpochMilli(ts).atZone(ZoneId.systemDefault()).toLocalDateTime(),
+                LocalDateTime.now()
+        );
+
+        rfidDeviceTrackRepository.save(track);
+        supply.setLatestRfidTrack(track);
+        supplyRepository.save(supply);
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/services/GeoInitializationService.java b/edera-api/api/src/main/java/applica/app/services/GeoInitializationService.java
new file mode 100644 (file)
index 0000000..ac073f2
--- /dev/null
@@ -0,0 +1,209 @@
+package applica.app.services;
+
+import applica.app.domain.geo.City;
+import applica.app.domain.geo.Nation;
+import applica.app.domain.geo.Province;
+import applica.app.domain.geo.Region;
+import applica.crud.datalayer.DataLayerAwareAdapter;
+import applica.crud.query.builders.CrudQueryBuilder;
+import lombok.SneakyThrows;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.UUID;
+
+@Service
+public class GeoInitializationService extends DataLayerAwareAdapter implements InitializingBean {
+
+    private final Log logger = LogFactory.getLog(getClass());
+
+    private final String nationCsvPath;
+    private final String regionCsvPath;
+    private final String provinceCsvPath;
+    private final String cityCsvPath;
+
+    private final MongoTemplate mongoTemplate;
+
+    public GeoInitializationService(
+                                 @Value("${geo.csv.nation}") String nationCsvPath,
+                                 @Value("${geo.csv.region}") String regionCsvPath,
+                                 @Value("${geo.csv.province}") String provinceCsvPath,
+                                 @Value("${geo.csv.city}") String cityCsvPath,
+                                 MongoTemplate mongoTemplate) {
+        this.nationCsvPath = nationCsvPath;
+        this.regionCsvPath = regionCsvPath;
+        this.provinceCsvPath = provinceCsvPath;
+        this.cityCsvPath = cityCsvPath;
+        this.mongoTemplate = mongoTemplate;
+    }
+
+    @Override
+    @ConditionalOnProperty(name = "app.test", havingValue = "false")
+    public void afterPropertiesSet() {
+        importNations();
+        importRegions();
+        importProvinces();
+        importCities();
+    }
+
+    private String[] loadCsvResourceFile(String resourceName) {
+        var file = GeoInitializationService.class.getResourceAsStream(resourceName);
+        assert file != null;
+        var streamReader = new InputStreamReader(file);
+        var reader = new BufferedReader(streamReader);
+        return reader.lines().skip(1).toArray(String[]::new);
+    }
+
+
+    @Transactional
+    @SneakyThrows
+    public void importNations() {
+        logger.info("Importing nations");
+        var lines = loadCsvResourceFile(nationCsvPath);
+        var query = new Query();
+        var count = mongoTemplate.count(query, Nation.class);
+        if (lines.length == count) {
+            logger.info("%s nations already loaded".formatted(count));
+            return;
+        }
+        for(var line : lines) {
+            String[] values = line.split(";");
+            String code = values[0];
+            String description = values[1];
+
+            Nation existingNation = dal(Nation.class)
+                    .find(CrudQueryBuilder.build().eq("code", code).get())
+                    .findFirst().orElse(null);
+
+            if (existingNation == null) {
+                Nation nation = new Nation(UUID.randomUUID().toString(), code, description);
+                dal(Nation.class).save(nation);
+            }
+        }
+
+        logger.info("%s nations created".formatted(lines.length));
+    }
+
+
+
+    @Transactional
+    @SneakyThrows
+    public void importRegions() {
+        logger.info("Importing regions");
+        var lines = loadCsvResourceFile(regionCsvPath);
+        var query = new Query();
+        var count = mongoTemplate.count(query, Region.class);
+        if (lines.length == count) {
+            logger.info("%s regions already loaded".formatted(count));
+            return;
+        }
+
+        var created = 0;
+        for (var line : lines) {
+            String[] values = line.split(";");
+            String code = values[0];
+            String name = values[1];
+            String nationCode = values[2];
+
+            Region existingRegion = dal(Region.class).find(CrudQueryBuilder.build().eq("name", name).get())
+                    .findFirst().orElse(null);
+
+            if (existingRegion == null) {
+                Nation nation = dal(Nation.class).find(CrudQueryBuilder.build().eq("code", nationCode).get())
+                        .findFirst().orElseThrow(() -> new Exception("Nation not found"));
+                Region region = new Region(UUID.randomUUID().toString(), nation.getId(), code, name);
+                dal(Region.class).save(region);
+                created++;
+            }
+        }
+
+        logger.info("%s regions created".formatted(created));
+    }
+
+
+    @Transactional
+    @SneakyThrows
+    public void importProvinces() {
+        logger.info("Importing provinces");
+        var lines = loadCsvResourceFile(provinceCsvPath);
+        var query = new Query();
+        var count = mongoTemplate.count(query, Province.class);
+        if (lines.length == count) {
+            logger.info("%s provinces already loaded".formatted(count));
+            return;
+        }
+        var created = 0;
+        for (var line : lines) {
+            String[] values = line.split(";");
+            String name = values[0];
+            String regionName = values[1];
+            String code = values[2];
+
+            Province existingProvince = dal(Province.class).find(CrudQueryBuilder.build().eq("name", name).get())
+                    .findFirst().orElse(null);
+
+            if (existingProvince == null) {
+                Region region = dal(Region.class).find(CrudQueryBuilder.build().eq("name", regionName).get())
+                        .findFirst().orElseThrow(() -> new Exception("Region not found"));
+                Province province = new Province(UUID.randomUUID().toString(), region.getId(), name, code);
+                dal(Province.class).save(province);
+                created++;
+            }
+        }
+
+        logger.info("%s provinces created".formatted(created));
+    }
+
+
+    @Transactional
+    @SneakyThrows
+    public void importCities() {
+        logger.info("Importing cities");
+        var lines = loadCsvResourceFile(cityCsvPath);
+        var query = new Query();
+        var count = mongoTemplate.count(query, City.class);
+        if (lines.length == count) {
+            logger.info("%s cities already loaded".formatted(count));
+            return;
+        }
+
+        var created = 0;
+        for (var line : lines) {
+            String[] values = line.split(",");
+            String name = values[0];
+            String istatCode = values[1];
+            String provinceName = values[2];
+            String provinceCode = values[3];
+            String postalCode = values[6];
+
+            City existingCity = dal(City.class).find(CrudQueryBuilder.build()
+                    .eq("postalCode", postalCode)
+                    .eq("name", name).get())
+                    .findFirst()
+                    .orElse(null);
+
+            if (existingCity == null) {
+                Province province = dal(Province.class).find(CrudQueryBuilder.build().eq("name", provinceName).get())
+                        .findFirst()
+                        .orElse(
+                                dal(Province.class)
+                                        .find(CrudQueryBuilder.build().eq("code", provinceCode).get())
+                                        .findFirst().orElseThrow(() -> new Exception("Province not found")));
+                City city = new City(UUID.randomUUID().toString(), province.getId(), name, postalCode, istatCode);
+                dal(City.class).save(city);
+                created++;
+            }
+        }
+        logger.info("%s cities created".formatted(created));
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/services/InitializationService.java b/edera-api/api/src/main/java/applica/app/services/InitializationService.java
new file mode 100644 (file)
index 0000000..abc57a3
--- /dev/null
@@ -0,0 +1,154 @@
+package applica.app.services;
+
+import applica.app.domain.I18nMessage;
+import applica.app.domain.I18nMessageRepository;
+import applica.app.domain.Notification;
+import applica.crud.acl.CrudPermission;
+import applica.crud.acl.CrudSecurityConfigurer;
+import applica.crud.annotations.CrudEntity;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.CrudEntitiesRegistry;
+import applica.crud.query.builders.CrudQueryBuilder;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.requests.SearchUsersRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+@SuppressWarnings("unused")
+@Service
+public class InitializationService implements InitializingBean {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final IAMService iamService;
+    private final CrudFactory crudFactory;
+    private final I18nMessageRepository i18nMessageRepository;
+    private final boolean test;
+    private static final Random RANDOM = new Random();
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
+
+    public InitializationService(IAMService iam, CrudFactory crudFactory, I18nMessageRepository i18nMessageRepository, @Value("${app.test}") boolean test) {
+        this.iamService = iam;
+        this.crudFactory = crudFactory;
+        this.i18nMessageRepository = i18nMessageRepository;
+        this.test = test;
+    }
+
+    @Override
+    @ConditionalOnProperty(name = "app.test", havingValue = "false")
+    public void afterPropertiesSet() throws InterruptedException {
+        if (!test) {
+            initializeI18nMessages();
+            initializeWelcomeNotification();
+        }
+        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+
+        tryInitializeWebEntitiesSecurity();
+    }
+
+    private void tryInitializeWebEntitiesSecurity() throws InterruptedException {
+        int retries = 0;
+        while (retries < 10) {
+            try {
+                var response = iamService.initializeAdmin();
+                if (response == null) {
+                    return;
+                }
+                initializeWebEntitiesSecurity();
+                break;
+            } catch (Exception e) {
+                retries++;
+                logger.warn("Error initializing IAM, retrying: {}", e.getMessage());
+                int randomSeconds = (int) (Math.random() * 3) + 3;
+                logger.warn("Waiting {} seconds to allow IAM to initialize", randomSeconds);
+                Thread.sleep(randomSeconds * 1000L);
+            }
+        }
+    }
+
+    private void initializeWebEntitiesSecurity() {
+        var adminRole = iamService.getIamOptions().getAdminRole();
+        var roles = iamService.getIamOptions().getRoles();
+        roles.forEach(role -> {
+            if (role.equals(adminRole)){
+                CrudEntitiesRegistry.instance().getDefinitions().forEach(d -> {
+                    CrudSecurityConfigurer.instance().configure(d.getId(), adminRole, CrudPermission.values());
+                });
+            } else {
+                CrudEntitiesRegistry.instance().getDefinitions().forEach(d -> {
+                    CrudEntity annotation = d.getType().getAnnotation(CrudEntity.class);
+                    List<CrudPermission> _permissions = new ArrayList<>();
+
+                    if (Arrays.asList(annotation.completePermissionsRoles()).contains(role)){
+                        _permissions.addAll(Arrays.stream(CrudPermission.values()).toList());
+                    } else {
+                        if (Arrays.asList(annotation.creationPermissionsRoles()).contains(role)){
+                            _permissions.add(CrudPermission.NEW);
+                        }
+                        if (Arrays.asList(annotation.deletePermissionsRoles()).contains(role)){
+                            _permissions.add(CrudPermission.DELETE);
+                        }
+                        if (Arrays.asList(annotation.savePermissionsRoles()).contains(role)){
+                            _permissions.add(CrudPermission.EDIT);
+                            _permissions.add(CrudPermission.SAVE);
+                        }
+                        if (Arrays.asList(annotation.listPermissionsRoles()).contains(role)){
+                            _permissions.add(CrudPermission.LIST);
+                        }
+                    }
+
+                    CrudPermission[] permissions = new CrudPermission[_permissions.size()];
+                    _permissions.toArray(permissions);
+                    CrudSecurityConfigurer.instance().configure(d.getId(), role, permissions);
+                });
+            }
+        });
+
+        logger.info("Web entities security initialized");
+    }
+
+    @Transactional
+    private void initializeI18nMessages() {
+        var messages = I18nMessage.loadFromResource();
+        i18nMessageRepository.deleteAll();
+        i18nMessageRepository.saveAll(messages);
+        logger.info("%s i18n messages loaded".formatted(messages.size()));
+    }
+
+    private void initializeWelcomeNotification() {
+        var searchUsersRequest = new SearchUsersRequest();
+        searchUsersRequest.setPageable(Pageable.ofSize(100));
+        searchUsersRequest.setRole("ROLE_ADMIN");
+
+        var searchUsersResponse = iamService.searchUsers(searchUsersRequest);
+        var users = searchUsersResponse.getUsers();
+        var dal = crudFactory.createDataLayer(Notification.class);
+        for (var user : users) {
+            var query = CrudQueryBuilder
+                    .build()
+                    .eq("userId", user.getId())
+                    .eq("title", "Welcome to Applica")
+                    .get();
+            var notification = dal.find(query).findFirst().orElse(null);
+            if (notification != null) {
+                continue;
+            }
+            notification = Notification.create(
+                    user.getId(),
+                    "Welcome to Applica",
+                    "This is a welcome notification",
+                    "#/profile");
+
+            dal.save(notification);
+        }
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/services/reporting/Report.java b/edera-api/api/src/main/java/applica/app/services/reporting/Report.java
new file mode 100644 (file)
index 0000000..c425c06
--- /dev/null
@@ -0,0 +1,12 @@
+package applica.app.services.reporting;
+
+import applica.app.domain.reporting.FilterSet;
+import lombok.Data;
+
+@Data
+public abstract class Report {
+
+    public abstract String getCode();
+
+    public abstract ReportData execute(FilterSet filter);
+}
diff --git a/edera-api/api/src/main/java/applica/app/services/reporting/ReportData.java b/edera-api/api/src/main/java/applica/app/services/reporting/ReportData.java
new file mode 100644 (file)
index 0000000..86ab820
--- /dev/null
@@ -0,0 +1,13 @@
+package applica.app.services.reporting;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ReportData extends HashMap<String, Object> {
+    public ReportData() {
+    }
+
+    public ReportData(Map map) {
+        super(map);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/services/reporting/ReportFactory.java b/edera-api/api/src/main/java/applica/app/services/reporting/ReportFactory.java
new file mode 100644 (file)
index 0000000..d8ef2d1
--- /dev/null
@@ -0,0 +1,18 @@
+package applica.app.services.reporting;
+
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+public class ReportFactory {
+    private final List<Report> reports;
+
+    public ReportFactory(List<Report> reports) {
+        this.reports = reports;
+    }
+
+    public Report get(String code) {
+        return reports.stream().filter(r -> r.getCode().equals(code)).findFirst().orElse(null);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/services/reporting/impl/SupplyReport.java b/edera-api/api/src/main/java/applica/app/services/reporting/impl/SupplyReport.java
new file mode 100644 (file)
index 0000000..b0ee40a
--- /dev/null
@@ -0,0 +1,141 @@
+package applica.app.services.reporting.impl;
+
+import applica.app.domain.equipment.EquipmentRepository;
+import applica.app.domain.equipment.EquipmentType;
+import applica.app.domain.equipment.EquipmentTypeRepository;
+import applica.app.domain.reporting.*;
+import applica.app.domain.supply.Supply;
+import applica.app.domain.supply.SupplyRepository;
+import applica.app.ml.MLClient;
+import applica.app.ml.MLRequest;
+import applica.app.services.reporting.Report;
+import applica.app.services.reporting.ReportData;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Component
+public class SupplyReport extends Report {
+
+    public static final String CODE = "supply";
+    private static final int DAYS = 30;
+    private final SupplyRepository supplyRepository;
+    private final EquipmentRepository equipmentRepository;
+    private final EquipmentTypeRepository equipmentTypeRepository;
+    private final MLClient mlClient;
+
+    public SupplyReport(SupplyRepository supplyRepository,
+                        EquipmentRepository equipmentRepository,
+                        EquipmentTypeRepository equipmentTypeRepository,
+                        MLClient mlClient) {
+        this.supplyRepository = supplyRepository;
+        this.equipmentRepository = equipmentRepository;
+        this.equipmentTypeRepository = equipmentTypeRepository;
+        this.mlClient = mlClient;
+    }
+
+    @Override
+    public String getCode() {
+        return CODE;
+    }
+
+    @Override
+    public ReportData execute(FilterSet filter) {
+        var reportData = new ReportData();
+
+        var supplyId = filter.getSupplyId().orElseThrow();
+        var supply = supplyRepository.findById(supplyId).orElseThrow();
+        var equipment = equipmentRepository.findById(supply.getEquipmentId()).orElseThrow();
+        var equipmentType = equipmentTypeRepository.findById(equipment.getEquipmentTypeId()).orElseThrow();
+        if (filter.getDays() <= 60) {
+            filter.setGroupBy(Optional.of(GroupBy.HOUR));
+        }
+
+        var currentMonth = filter.currentMonth();
+        var currentYear = filter.currentYear();
+
+        var customUsage = supplyRepository.getCustomUsage(filter);
+        var monthUsage = supplyRepository.getTotalUsage(currentMonth);
+        var yearUsage = supplyRepository.getTotalUsage(currentYear);
+        var averageMonthUsage = supplyRepository.getAverageUsage(currentMonth);
+        var averageYearUsage = supplyRepository.getAverageUsage(currentYear);
+        var monthlyUtilizationPercentage = supplyRepository.getMonthlyUtilizationPercentage(currentMonth);
+        var history = supplyRepository.getCustomUsage(filter.lastXDays(DAYS));
+
+        reportData.put("customUsage", customUsage);
+        reportData.put("monthUsage", monthUsage);
+        reportData.put("yearUsage", yearUsage);
+        reportData.put("averageMonthUsage", averageMonthUsage);
+        reportData.put("averageYearUsage", averageYearUsage);
+        reportData.put("monthlyUtilizationPercentage", monthlyUtilizationPercentage);
+        reportData.put("predictions", createPredictions(history, supply, equipmentType, filter));
+        reportData.put("breakpointInHours", supply.getBreakpointInHours() > 0
+                ? supply.getBreakpointInHours()
+                : equipmentType.getBreakpointInHours());
+
+        return reportData;
+    }
+
+
+
+    private DataPointSet createPredictions(DataPointSet history, Supply supply, EquipmentType equipmentType, TemporalFilter filter) {
+        var breakpointInHours = supply.getBreakpointInHours() > 0
+                ? supply.getBreakpointInHours()
+                : equipmentType.getBreakpointInHours();
+
+        var predictions = new DataPointSet(history
+                .getPoints()
+                .stream()
+                .peek(dataPoint -> dataPoint
+                        .set("broken", 0)
+                        .set("prediction", 0)
+                )
+                .toList());
+
+        var decremented = supply.getAccumulatedHours();
+        for (var i = predictions.getPoints().size() - 1; i >= 0; i--) {
+            var point = predictions.getPoints().get(i);
+            decremented -= point.getInt("total");
+            point.set("accumulated", decremented);
+        }
+
+        var points = predictions.getPoints();
+        var lastDay = !points.isEmpty() ? points.get(points.size() - 1) : DataPoint.create();
+        var cursor = !points.isEmpty() ? LocalDateTime.of(
+                        lastDay.getInt("year"),
+                        lastDay.getInt("month"),
+                        lastDay.getInt("day"),
+                        0, 0, 0)
+                .plusDays(1) : filter.getFrom();
+
+        var mlRequest = MLRequest.of(equipmentType.getProgressive().intValue(), cursor.getDayOfYear())
+                .withAccumulatedHours((int)supply.getAccumulatedHours())
+                .withBreakpointHours(breakpointInHours)
+                .withThreshold(0.51f)
+                .withMaxDays(DAYS)
+                .withMaxHours(24);
+
+        var mlPredictions = mlClient.predict(mlRequest);
+        var accumulatedHours = supply.getAccumulatedHours();
+
+        for (var mlPrediction : mlPredictions) {
+            accumulatedHours += mlPrediction.getHours();
+            var dataPoint = DataPoint.create()
+                    .set("year", cursor.getYear())
+                    .set("month", cursor.getMonthValue())
+                    .set("day", cursor.getDayOfMonth())
+                    .set("total", mlPrediction.getHours())
+                    .set("accumulated", accumulatedHours)
+                    .set("prediction", 1)
+                    .set("broken", mlPrediction.isBroken() ? 1 : 0);
+
+            cursor = cursor.plusDays(1);
+            predictions.add(dataPoint);
+        }
+
+        return predictions;
+    }
+}
+
diff --git a/edera-api/api/src/main/java/applica/app/services/reporting/impl/SupplyTracksReport.java b/edera-api/api/src/main/java/applica/app/services/reporting/impl/SupplyTracksReport.java
new file mode 100644 (file)
index 0000000..2c6ad7f
--- /dev/null
@@ -0,0 +1,53 @@
+package applica.app.services.reporting.impl;
+
+import applica.app.domain.reporting.FilterSet;
+import applica.app.domain.reporting.TemporalFilter;
+import applica.app.domain.rfid.RFIDDeviceTrackRepository;
+import applica.app.domain.supply.SupplyRepository;
+import applica.app.services.reporting.Report;
+import applica.app.services.reporting.ReportData;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+
+@Component
+public class SupplyTracksReport extends Report {
+
+    public static final String CODE = "supply-tracks";
+    private final RFIDDeviceTrackRepository rfidDeviceTrackRepository;
+    private final SupplyRepository supplyRepository;
+
+    public SupplyTracksReport(RFIDDeviceTrackRepository rfidDeviceTrackRepository, SupplyRepository supplyRepository) {
+        this.rfidDeviceTrackRepository = rfidDeviceTrackRepository;
+        this.supplyRepository = supplyRepository;
+    }
+
+    @Override
+    public String getCode() {
+        return CODE;
+    }
+
+    @Override
+    public ReportData execute(FilterSet filter) {
+        var reportData = new ReportData();
+        var currentMonth = filter.currentMonth();
+        var currentYear = filter.currentYear();
+
+        var latestTrackedSupplies = supplyRepository.getLatestTrackedSupplies(filter);
+        var monthMovementTracking = rfidDeviceTrackRepository.getTotalTrackedRFID(currentMonth);
+        var yearMovementTracking = rfidDeviceTrackRepository.getTotalTrackedRFID(currentYear);
+        var monthlyTotalTrackedRFID = rfidDeviceTrackRepository.getMonthlyTotalTrackedRFID(currentMonth);
+        var monthlyTotalTrackedRFIDPercentage = supplyRepository.getMonthlyTrackingPercentage(currentMonth);
+        var tagsAndGatewaysNames = rfidDeviceTrackRepository.getTagsAndGatewaysNames(filter);
+
+        reportData.put("latestTrackedSupplies", latestTrackedSupplies);
+        reportData.put("monthMovementTracking", monthMovementTracking);
+        reportData.put("yearMovementTracking", yearMovementTracking);
+        reportData.put("monthlyTotalTrackedRFID", monthlyTotalTrackedRFID);
+        reportData.put("monthlyTotalTrackedRFIDPercentage", monthlyTotalTrackedRFIDPercentage);
+        reportData.put("tagsAndGatewaysNames", tagsAndGatewaysNames);
+
+
+        return reportData;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/tasks/ProtocolServiceNotifyTask.java b/edera-api/api/src/main/java/applica/app/tasks/ProtocolServiceNotifyTask.java
new file mode 100644 (file)
index 0000000..dbcb42b
--- /dev/null
@@ -0,0 +1,178 @@
+package applica.app.tasks;
+
+import applica.app.domain.I18nMessageRepository;
+import applica.app.domain.Notification;
+import applica.app.domain.customer.CustomerRepository;
+import applica.app.domain.equipment.EquipmentRepository;
+import applica.app.domain.equipment.EquipmentTypeRepository;
+import applica.app.domain.maintenance.Activity;
+import applica.app.domain.maintenance.ActivityRepository;
+import applica.app.domain.maintenance.ActivityTypeRepository;
+import applica.app.domain.protocol.Protocol;
+import applica.app.domain.protocol.ProtocolRepository;
+import applica.app.domain.protocol.ProtocolService;
+import applica.app.domain.supplier.SupplierRepository;
+import applica.app.domain.supply.Supply;
+import applica.app.domain.supply.SupplyRepository;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.requests.SearchUsersRequest;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.data.domain.Pageable;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
+import java.util.Objects;
+
+@Component
+@SuppressWarnings("unused")
+public class ProtocolServiceNotifyTask {
+
+    private final CrudFactory crudFactory;
+    private final ProtocolRepository protocolRepository;
+    private final ActivityRepository activityRepository;
+    private final SupplyRepository supplyRepository;
+    private final ActivityTypeRepository activityTypeRepository;
+    private final CustomerRepository customerRepository;
+    private final EquipmentRepository equipmentRepository;
+    private final EquipmentTypeRepository equipmentTypeRepository;
+    private final SupplierRepository supplierRepository;
+    private final Log log = LogFactory.getLog(getClass());
+    private final IAMService iamService;
+    private static final String DEFAULT_LOCALE = "it";
+    private final I18nMessageRepository i18nMessageRepository;
+
+    public ProtocolServiceNotifyTask(ProtocolRepository protocolRepository,
+                                     IAMService iamService,
+                                     CrudFactory crudFactory,
+                                     ActivityRepository activityRepository,
+                                     SupplyRepository supplyRepository,
+                                     ActivityTypeRepository activityTypeRepository,
+                                     CustomerRepository customerRepository,
+                                     I18nMessageRepository i18nMessageRepository,
+                                     EquipmentRepository equipmentRepository,
+                                     EquipmentTypeRepository equipmentTypeRepository,
+                                     SupplierRepository supplierRepository) {
+        this.protocolRepository = protocolRepository;
+        this.iamService = iamService;
+        this.crudFactory = crudFactory;
+        this.activityRepository = activityRepository;
+        this.supplyRepository = supplyRepository;
+        this.activityTypeRepository = activityTypeRepository;
+        this.customerRepository = customerRepository;
+        this.i18nMessageRepository = i18nMessageRepository;
+        this.equipmentRepository = equipmentRepository;
+        this.equipmentTypeRepository = equipmentTypeRepository;
+        this.supplierRepository = supplierRepository;
+
+        log.info("Protocol service checker configured successfully");
+    }
+
+    @Scheduled(cron = "0 0 0 * * ?")
+    public void execute() {
+        var today = LocalDate.now();
+        var notificationDal = crudFactory.createDataLayer(Notification.class);
+        var protocols = protocolRepository.findAll();
+        for (var protocol : protocols) {
+            if (protocol.getServices() != null) {
+                for (var service : protocol.getServices()) {
+                    var endDate = service.getEnd();
+                    long frequency = service.getFrequency();
+
+                    if (today.isAfter(endDate)) continue;
+
+                    var supplies = supplyRepository.findByProtocolId(protocol.getId());
+                    if (supplies.isEmpty()) continue;
+                    for (var supply : supplies) {
+                        var supplyActivities = activityRepository.findFirstBySupplyIdAndActivityTypeIdOrderByCreatedDesc(
+                                supply.getId(),
+                                service.getFromActivityTypeId());
+                        if (supplyActivities.isPresent()) {
+                            var mostRecentActivity = supplyActivities.get();
+                            var activityDate = mostRecentActivity.getDate();
+                            long daysSinceActivity = ChronoUnit.DAYS.between(activityDate, today);
+                            if (daysSinceActivity == frequency) {
+                                sendNotifications(mostRecentActivity, notificationDal, service, supply, protocol);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void sendNotifications(Activity activity, DataLayer<Notification> dal, ProtocolService service, Supply supply, Protocol protocol) {
+        var roles = Arrays.asList("ROLE_ADMIN", "ROLE_OPERATOR", "ROLE_USER");
+        var title = i18nMessageRepository.findById(String.format("%s.%s", DEFAULT_LOCALE, "ra.activity.schedule.title"));
+        var description = i18nMessageRepository.findById(String.format("%s.%s", DEFAULT_LOCALE, "ra.activity.schedule.description"));
+        if (title.isEmpty() || description.isEmpty()) {
+            log.error(String.format("Missing i18n message for key %s or %s", "ra.activity.schedule.title", "ra.activity.schedule.description"));
+            return;
+        }
+
+        var fromActivityType = activityTypeRepository.findById(service.getFromActivityTypeId()).orElse(null);
+        var toActivityType = service.getToActivityTypeId() == null
+                ? null
+                : activityTypeRepository.findById(service.getToActivityTypeId()).orElse(null);
+        if (fromActivityType == null) {
+            log.error(String.format("Activity type %s not found", service.getFromActivityTypeId()));
+            return;
+        }
+
+        var customer = customerRepository.findById(protocol.getCustomerId()).orElse(null);
+        if (customer == null) {
+            log.error(String.format("Customer %s not found", protocol.getCustomerId()));
+            return;
+        }
+        var equipment = equipmentRepository.findById(supply.getEquipmentId()).orElse(null);
+        if (equipment == null) {
+            log.error(String.format("Equipment %s not found", supply.getEquipmentId()));
+            return;
+        }
+        var equipmentType = equipmentTypeRepository.findById(equipment.getEquipmentTypeId()).orElse(null);
+        if (equipmentType == null) {
+            log.error(String.format("Equipment type %s not found", equipment.getEquipmentTypeId()));
+            return;
+        }
+        var supplier = supplierRepository.findById(supply.getSupplierId()).orElse(null);
+        if (supplier == null) {
+            log.error(String.format("Supplier %s not found", supply.getSupplierId()));
+            return;
+        }
+
+        for (String role : roles) {
+            var searchUsersRequest = new SearchUsersRequest();
+            searchUsersRequest.setPageable(Pageable.ofSize(100));
+            searchUsersRequest.setRole(role);
+
+            var searchUsersResponse = iamService.searchUsers(searchUsersRequest);
+            var users = searchUsersResponse.getUsers();
+
+            for (var user : users) {
+                var notification = Notification.create(
+                        user.getId(),
+                        title.get().getText(),
+                        String.format(description.get().getText(),
+                                service.getToActivityTypeId() == null
+                                        ? fromActivityType.getDescription()
+                                        : Objects.requireNonNull(toActivityType).getDescription(),
+                                customer.getName(),
+                                equipmentType.getName(),
+                                supplier.getBusinessName(),
+                                protocol.getProtocolNumber()),
+                        String.format("#/entities/activity/create?activity-id=%s&activity-type-id=%s",
+                                activity.getId(),
+                                toActivityType == null ? fromActivityType.getId() : toActivityType.getId())
+                );
+
+                dal.save(notification);
+            }
+        }
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/utils/SecurityUtils.java b/edera-api/api/src/main/java/applica/app/utils/SecurityUtils.java
new file mode 100644 (file)
index 0000000..3195fe9
--- /dev/null
@@ -0,0 +1,36 @@
+package applica.app.utils;
+
+import applica.iam.sdk.readmodel.UserView;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import java.util.Optional;
+
+
+public class SecurityUtils {
+    public static String tokenFromAuthorizationHeader(String authorization) {
+        if (StringUtils.isNotEmpty(authorization) && authorization.toLowerCase().startsWith("bearer")) {
+            return authorization.substring("bearer ".length());
+        }
+
+        return null;
+    }
+
+    public static boolean isLogged() {
+        return SecurityContextHolder.getContext().getAuthentication() != null &&
+                SecurityContextHolder.getContext().getAuthentication().isAuthenticated();
+    }
+
+    public static boolean isAnonymous() {
+        return !isLogged() || SecurityContextHolder.getContext().getAuthentication().getPrincipal().equals("anonymousUser");
+    }
+
+    public static Optional<UserView> getLoggedUser() {
+        if (!isLogged() || isAnonymous()) {
+            return Optional.empty();
+        }
+
+        var user = ((UserView) SecurityContextHolder.getContext().getAuthentication().getPrincipal());
+        return Optional.ofNullable(user);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/annotations/CustomerFilterable.java b/edera-api/api/src/main/java/applica/app/web/annotations/CustomerFilterable.java
new file mode 100644 (file)
index 0000000..c28e5cc
--- /dev/null
@@ -0,0 +1,18 @@
+package applica.app.web.annotations;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Puoi applicare questa annotazione a una qualsiasi entità.
+ * In tal caso, se passi dal Crud Controller di Applica, il sistema provvederà
+ * automaticamente ad applicare un filtro per customerId se l'utente corrente non Ã¨ admin
+ * ed ha un customerId valido.
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CustomerFilterable {
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/DocxController.java b/edera-api/api/src/main/java/applica/app/web/controllers/DocxController.java
new file mode 100644 (file)
index 0000000..10c7e3c
--- /dev/null
@@ -0,0 +1,108 @@
+package applica.app.web.controllers;
+
+import applica.app.domain.docx.DocxDataSource;
+import applica.app.domain.docx.DocxFactory;
+import applica.app.domain.docx.DocxTemplate;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.query.builders.CrudQueryBuilder;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.ResponseCodes;
+import applica.iam.sdk.requests.ValidateTokenRequest;
+import jakarta.servlet.ServletRequest;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/docx")
+@SuppressWarnings("unused")
+public class DocxController {
+    private final DocxFactory docxFactory;
+    private final DataLayer<DocxTemplate> docxTemplateDataLayer;
+    private final Log logger = LogFactory.getLog(DocxController.class);
+    private final IAMService IAMService;
+
+    public DocxController(DocxFactory docxFactory, CrudFactory crudFactory, IAMService IAMService) {
+        this.docxFactory = docxFactory;
+        this.docxTemplateDataLayer = crudFactory.createDataLayer(DocxTemplate.class);
+        this.IAMService = IAMService;
+    }
+
+    @GetMapping("/generate/{type}/{templateId}/{id}.docx")
+    public ResponseEntity<InputStreamResource> generate(@PathVariable String type, @PathVariable String templateId, @PathVariable String id, ServletRequest request) {
+        var token = request.getParameter("_token");
+        if (StringUtils.isEmpty(token)) {
+            return ResponseEntity.badRequest().build();
+        }
+
+        var result = IAMService.validateToken(new ValidateTokenRequest(token));
+        if (!result.getResponseCode().equals(ResponseCodes.OK)) {
+            return ResponseEntity.badRequest().build();
+        }
+
+        var template = docxTemplateDataLayer.get(templateId);
+        if (template.isEmpty()) {
+            return ResponseEntity.notFound().build();
+        }
+
+        try {
+            var docx = docxFactory.generate(template.get(), id);
+            return ResponseEntity.ok()
+                    .header("Content-Disposition", String.format("attachment; filename=\"%s.docx\"", template.get().getName()))
+                    .header("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
+                    .body(new InputStreamResource(docx));
+        } catch (Exception e) {
+            logger.error("Error generating docx", e);
+            return ResponseEntity.badRequest().build();
+        }
+    }
+
+    @GetMapping("/helper/{type}.docx")
+    public ResponseEntity<InputStreamResource> helper(@PathVariable String type) {
+        try {
+            var typeDef = docxFactory.get(type);
+            if (typeDef == null) {
+                return ResponseEntity.notFound().build();
+            }
+
+            var descriptor = typeDef.getDescriptor();
+            var dataSource = new DocxDataSource();
+            for (var field : descriptor) {
+                var row = dataSource.createRow();
+                row.put("TYPE", typeDef.getType());
+                row.put("NAME", String.format("{#%s#}", field.getName()));
+                row.put("DESCRIPTION", field.getDescription());
+                row.put("COLUMN_TYPE", field.isBody() ? "BODY" : "HEADER");
+            }
+            var template = getClass().getClassLoader().getResourceAsStream("docx/helper.docx");
+            var docx = docxFactory.generate(template, dataSource);
+            var filename = String.format("%s.docx", type);
+
+            return ResponseEntity.ok()
+                    .header("Content-Disposition", String.format("attachment; filename=\"%s\"", filename))
+                    .header("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
+                    .body(new InputStreamResource(docx));
+        } catch (Exception e) {
+            logger.error("Error generating docx", e);
+            return ResponseEntity.badRequest().build();
+        }
+    }
+
+    @GetMapping("/templates/{type}")
+    public List<DocxTemplate> getTemplates(@PathVariable String type) {
+        var query = CrudQueryBuilder
+                .build()
+                .eq("type", type)
+                .rowsPerPage(1000);
+        return docxTemplateDataLayer.find(query).getRows();
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/I18nController.java b/edera-api/api/src/main/java/applica/app/web/controllers/I18nController.java
new file mode 100644 (file)
index 0000000..f70e930
--- /dev/null
@@ -0,0 +1,48 @@
+package applica.app.web.controllers;
+
+import applica.app.domain.I18nMessage;
+import applica.crud.factory.CrudFactory;
+import applica.crud.query.builders.CrudQueryBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/i18n")
+public class I18nController {
+    @Autowired
+    CrudFactory crudFactory;
+
+    @PutMapping("/message")
+    public ResponseEntity<I18nMessage> putMessage(@RequestBody Map<String, Object> payload) {
+        String lang = (String) payload.get("lang");
+        String code = (String) payload.get("code");
+        String text = (String) payload.get("text");
+        if (lang == null || code == null || text == null) {
+            return ResponseEntity.badRequest().build();
+        }
+        var dal = crudFactory.createDataLayer(I18nMessage.class);
+        var query = CrudQueryBuilder.build().eq("lang", lang).eq("code", code);
+        var message = dal.find(query).findFirst().orElse(null);
+        if (message == null) {
+            message = I18nMessage.create(lang, code, text);
+            dal.save(message);
+        }
+
+        return ResponseEntity.ok(message);
+    }
+
+    @GetMapping("/messages")
+    public @ResponseBody List<I18nMessage> getMessages() {
+        var dal = crudFactory.createDataLayer(I18nMessage.class);
+        var query = CrudQueryBuilder.build();
+        var rows = dal.find(query)
+                .getRows()
+                .stream()
+                .filter(m -> m.isValid()).toList();
+        return rows;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/IAMController.java b/edera-api/api/src/main/java/applica/app/web/controllers/IAMController.java
new file mode 100644 (file)
index 0000000..8902f1f
--- /dev/null
@@ -0,0 +1,206 @@
+package applica.app.web.controllers;
+
+import applica.app.utils.SecurityUtils;
+import applica.app.web.requests.PatchProfileRequest;
+import applica.app.web.responses.CrudLoginResponse;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.ResponseCodes;
+import applica.iam.sdk.requests.*;
+import applica.iam.sdk.responses.*;
+import jakarta.servlet.http.HttpServletRequest;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static applica.iam.sdk.ResponseCodes.OK;
+import static applica.iam.sdk.ResponseCodes.TOKEN_EXPIRED;
+import static org.springframework.http.ResponseEntity.*;
+
+@SuppressWarnings("unused")
+@RestController
+@RequestMapping("/iam")
+public class IAMController {
+
+    final IAMService iamService;
+
+    public IAMController(IAMService iamService) {
+        this.iamService = iamService;
+    }
+
+    @PostMapping("/login")
+    public ResponseEntity<CrudLoginResponse> login(HttpServletRequest request, String mail, String password) {
+        var response = iamService.login(new LoginRequest(mail, password));
+
+        if (response.getResponseCode().equals(OK)) {
+            var authentication = new UsernamePasswordAuthenticationToken(
+                    response.getUser(),
+                    null,
+                    response.getUser().getRoles().stream().map(SimpleGrantedAuthority::new).toList()
+            );
+            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+
+            var crudResponse = new CrudLoginResponse(
+                    response.getResponseCode(),
+                    response.getUser(),
+                    response.getToken()
+            );
+
+            return ok(crudResponse);
+        }
+
+        return status(HttpStatus.UNAUTHORIZED).build();
+    }
+
+    @PostMapping("/impersonate")
+    @PreAuthorize("hasRole('ROLE_ADMIN')")
+    public ResponseEntity<CrudLoginResponse> login(HttpServletRequest request, String id) {
+        var response = iamService.impersonate(new ImpersonateRequest(id));
+        if (response.getResponseCode().equals(OK)) {
+            var authentication = new UsernamePasswordAuthenticationToken(
+                    response.getUser(),
+                    null,
+                    response.getUser().getRoles().stream().map(SimpleGrantedAuthority::new).toList()
+            );
+            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+
+            var crudResponse = new CrudLoginResponse(
+                    response.getResponseCode(),
+                    response.getUser(),
+                    response.getToken()
+            );
+
+            return ok(crudResponse);
+        }
+
+        return status(HttpStatus.UNAUTHORIZED).build();
+    }
+
+
+    @PostMapping("/token-login")
+    public ResponseEntity<CrudLoginResponse> tokenLogin(String token) {
+        ValidateTokenResponse validateTokenResponse = iamService.validateToken(new ValidateTokenRequest(token));
+        if (!validateTokenResponse.getResponseCode().equals(ResponseCodes.OK)) {
+            return status(HttpStatus.UNAUTHORIZED).body(new CrudLoginResponse(TOKEN_EXPIRED, null, null));
+        }
+
+        FreshTokenResponse freshTokenResponse = iamService.freshToken(new FreshTokenRequest(token));
+        if (!validateTokenResponse.getResponseCode().equals(ResponseCodes.OK)) {
+            return status(HttpStatus.UNAUTHORIZED).body(new CrudLoginResponse(TOKEN_EXPIRED, null, null));
+        }
+
+        return ok().body(new CrudLoginResponse(OK, validateTokenResponse.getUser(), freshTokenResponse.getToken()));
+    }
+
+    @GetMapping("/fresh-token")
+    public ResponseEntity<FreshTokenResponse> freshToken(HttpServletRequest request) {
+        var authorization = request.getHeader("Authorization");
+        var currentToken = SecurityUtils.tokenFromAuthorizationHeader(authorization);
+        if (StringUtils.isEmpty(currentToken)) {
+            return status(HttpStatus.BAD_REQUEST).build();
+        }
+        var response = iamService.freshToken(new FreshTokenRequest(currentToken));
+        if (response.getResponseCode().equals(OK)) {
+            return ok().body(response);
+        }
+
+        return internalServerError().body(response);
+    }
+
+    @PostMapping("/reset-pin")
+    @PreAuthorize("hasRole('ROLE_ADMIN')")
+    public ResponseEntity<ResetPinResponse> resetPin(String userId, String pinCode) {
+        var resetPinRequest = new ResetPinRequest(userId, pinCode);
+        var resetPinResponse = iamService.resetPin(resetPinRequest);
+        return ok().body(resetPinResponse);
+    }
+
+    @PostMapping("/register")
+    public ResponseEntity<RegistrationResponse> register(String name, String email, String password) {
+        var request = new RegistrationRequest(email, password, null, true, Map.of("name", name)) {{
+           setActiveByDefault(true);
+        }};
+        var response = iamService.register(request);
+        if (response.getResponseCode().equals(OK)) {
+            return ok().body(response);
+        }
+
+        return badRequest().body(response);
+    }
+
+    @PostMapping("/activate")
+    public ResponseEntity<ActivationResponse> activate(String activationCode) {
+        var response = iamService.activate(new ActivationRequest(activationCode));
+        if (response.getResponseCode().equals(OK)) {
+            return ok().body(response);
+        }
+
+        return badRequest().body(response);
+    }
+
+    @PostMapping("/recover")
+    public ResponseEntity<RecoverPasswordResponse> recover(String email) {
+        var response = iamService.recoverPassword(new RecoverPasswordRequest(email));
+        if (response.getResponseCode().equals(OK)) {
+            return ok().body(response);
+        }
+
+        return badRequest().body(response);
+    }
+
+    @PostMapping("/change-password")
+    public ResponseEntity<ChangePasswordResponse> changePassword(String currentPassword, String password) {
+        var loggedUser = SecurityUtils.getLoggedUser().orElse(null);
+        if (loggedUser == null) {
+            return status(HttpStatus.UNAUTHORIZED).build();
+        }
+
+        var response = iamService.changePassword(new ChangePasswordRequest(loggedUser.getId(), currentPassword, password));
+        if (response.getResponseCode().equals(OK)) {
+            return ok().body(response);
+        }
+
+        return status(HttpStatus.UNAUTHORIZED).build();
+    }
+
+    @PostMapping("/register-device")
+    @ResponseBody public ResponseEntity<RegisterDeviceResponse> registerDevice(@RequestBody RegisterDeviceRequest request) {
+        var getDeviceByCodeRequest = new GetDeviceByCodeRequest(request.getCode());
+        var getDeviceByCodeResponse = iamService.getDeviceByCode(getDeviceByCodeRequest);
+        if (!getDeviceByCodeResponse.getResponseCode().equals(OK)) {
+            var registerDeviceResponse = iamService.registerDevice(request);
+            return ok().body(registerDeviceResponse);
+        }
+
+        return ok().body(new RegisterDeviceResponse(OK, getDeviceByCodeResponse.getDevice()));
+    }
+
+    @PostMapping("/pin-login")
+    @ResponseBody public ResponseEntity<PinLoginResponse> pinLogin(@RequestBody PinLoginRequest request) {
+        var response = iamService.pinLogin(request);
+        return ok().body(response);
+    }
+
+    @PostMapping("/update-profile")
+    @ResponseBody public ResponseEntity<UpdateProfileResponse> updateProfile(@RequestBody PatchProfileRequest request) {
+        var user = SecurityUtils.getLoggedUser().orElse(null);
+
+        if (user == null) {
+            return status(HttpStatus.UNAUTHORIZED).build();
+        }
+
+        var updateProfileRequest = new UpdateProfileRequest(user.getId(), new HashMap<>(Map.of("name", request.getName())));
+        var response = iamService.updateProfile(updateProfileRequest);
+        return ok().body(response);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/MaintenanceController.java b/edera-api/api/src/main/java/applica/app/web/controllers/MaintenanceController.java
new file mode 100644 (file)
index 0000000..7a4e119
--- /dev/null
@@ -0,0 +1,33 @@
+package applica.app.web.controllers;
+
+import applica.app.services.EderaService;
+import applica.app.web.responses.ActivityInfoResponse;
+import applica.app.web.responses.SupplyInfoResponse;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/maintenance")
+public class MaintenanceController {
+
+    final EderaService ederaService;
+
+    public MaintenanceController(EderaService ederaService) {
+        this.ederaService = ederaService;
+    }
+
+
+    @GetMapping("/activity-info/{activityId}")
+    public ActivityInfoResponse allActivityInfo(@PathVariable String activityId) {
+        var response = new ActivityInfoResponse(ederaService.getActivityDetails(activityId));
+        return response;
+    }
+
+    @GetMapping("/supply-info/{supplyId}")
+    public SupplyInfoResponse allSupplyInfo(@PathVariable String supplyId) {
+        var response = new SupplyInfoResponse(ederaService.getSupplyDetails(supplyId));
+        return response;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/ProfileController.java b/edera-api/api/src/main/java/applica/app/web/controllers/ProfileController.java
new file mode 100644 (file)
index 0000000..cab6def
--- /dev/null
@@ -0,0 +1,53 @@
+package applica.app.web.controllers;
+
+import applica.app.utils.SecurityUtils;
+import applica.fs.FsClient;
+import applica.fs.MimeUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+
+import static org.springframework.http.ResponseEntity.*;
+
+@RestController
+@RequestMapping("/profile")
+public class ProfileController {
+
+    final FsClient fsClient;
+
+    public ProfileController(FsClient fsClient) {
+        this.fsClient = fsClient;
+    }
+
+    @GetMapping("/me/image")
+    public ResponseEntity<byte[]> image() {
+        try {
+            var user = SecurityUtils
+                    .getLoggedUser()
+                    .orElseThrow(() -> new AuthenticationCredentialsNotFoundException(null));
+
+            var image = user.getProfile().getOrDefault("image", null);
+            if (!StringUtils.hasLength(image)) {
+                return notFound().build();
+            }
+
+            return ok()
+                    .header("Content-Type", MimeUtils.getMimeType(FilenameUtils.getExtension(image)))
+                    .header("Content-Disposition", "attachment; filename=" + "profile." + FilenameUtils.getExtension(image))
+                    .body(IOUtils.toByteArray(fsClient.getImage(image, "100x100")));
+        } catch (AuthenticationCredentialsNotFoundException e) {
+            return status(HttpStatus.UNAUTHORIZED).build();
+        } catch (IOException e) {
+            return internalServerError().build();
+        }
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/ProtectedRestController.java b/edera-api/api/src/main/java/applica/app/web/controllers/ProtectedRestController.java
new file mode 100644 (file)
index 0000000..8d5c424
--- /dev/null
@@ -0,0 +1,26 @@
+package applica.app.web.controllers;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static org.springframework.http.ResponseEntity.ok;
+
+@RestController
+public class ProtectedRestController {
+
+    @GetMapping("/tests/protected/user")
+    @PreAuthorize("hasRole('USER')")
+    public ResponseEntity<Void> protectedUserMethod() {
+        return ok().build();
+    }
+
+    @GetMapping("/tests/protected/admin")
+    @PreAuthorize("hasRole('ADMIN')")
+    public ResponseEntity<Void> protectedAdminMethod(HttpServletRequest request) {
+        request.isUserInRole("ADMIN");
+        return ok().build();
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/RFIDDeviceTrackController.java b/edera-api/api/src/main/java/applica/app/web/controllers/RFIDDeviceTrackController.java
new file mode 100644 (file)
index 0000000..29ae861
--- /dev/null
@@ -0,0 +1,39 @@
+package applica.app.web.controllers;
+
+import applica.app.services.EderaService;
+import applica.crud.responses.Response;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+@RestController
+public class RFIDDeviceTrackController {
+
+    private final EderaService ederaService;
+    private final Log log = LogFactory.getLog(getClass());
+
+    public RFIDDeviceTrackController(EderaService ederaService) {
+        this.ederaService = ederaService;
+    }
+
+    @GetMapping("/track")
+    public Response acquire(
+            @RequestParam("tag") String tagId,
+            @RequestParam("gate") String gatewayId,
+            @RequestParam(value = "timestamp", required = false) Long ts,
+            @RequestParam Map<String,String> allParams) {
+
+        try {
+            ederaService.save(gatewayId, tagId, ts, allParams);
+        } catch (Exception e) {
+            log.error("Error while saving tracking data", e);
+            return Response.error(e.getMessage());
+        }
+        return Response.ok();
+    }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/ReportController.java b/edera-api/api/src/main/java/applica/app/web/controllers/ReportController.java
new file mode 100644 (file)
index 0000000..19438e5
--- /dev/null
@@ -0,0 +1,31 @@
+package applica.app.web.controllers;
+
+import applica.app.services.EderaService;
+import applica.app.web.requests.GetReportDataRequest;
+import applica.app.web.responses.GetReportDataResponse;
+import applica.crud.responses.Response;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/report")
+@SuppressWarnings("unused")
+public class ReportController {
+    private final EderaService ederaService;
+    private final Log logger = LogFactory.getLog(getClass());
+
+    public ReportController(EderaService ederaService) {
+        this.ederaService = ederaService;
+    }
+
+    @PostMapping("/{code}/execute")
+    public GetReportDataResponse execute(@PathVariable String code, @RequestBody GetReportDataRequest request) {
+        try {
+            return ederaService.getReportData(code, request);
+        } catch (RuntimeException e) {
+            logger.error("Error executing report", e);
+            return new GetReportDataResponse(Response.ERROR);
+        }
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/SupplyController.java b/edera-api/api/src/main/java/applica/app/web/controllers/SupplyController.java
new file mode 100644 (file)
index 0000000..c3fa337
--- /dev/null
@@ -0,0 +1,33 @@
+package applica.app.web.controllers;
+
+import applica.app.services.EderaService;
+import applica.crud.responses.Response;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/supply")
+@SuppressWarnings("unused")
+public class SupplyController{
+
+    final EderaService ederaService;
+
+    public SupplyController(EderaService ederaService) {
+        this.ederaService = ederaService;
+    }
+
+
+    @GetMapping("/supply-start/{supplyId}")
+    public Response startUsage(@PathVariable String supplyId) {
+        ederaService.startUsage(supplyId);
+        return Response.ok();
+    }
+
+    @GetMapping("/supply-stop/{supplyId}")
+    public Response stopUsage(@PathVariable String supplyId) {
+        ederaService.stopUsage(supplyId);
+        return Response.ok();
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/controllers/ValuesController.java b/edera-api/api/src/main/java/applica/app/web/controllers/ValuesController.java
new file mode 100644 (file)
index 0000000..a63a6d3
--- /dev/null
@@ -0,0 +1,55 @@
+package applica.app.web.controllers;
+
+import applica.app.domain.docx.DocxFactory;
+import applica.app.domain.maintenance.ActivityStatus;
+import applica.app.domain.rfid.RFIDDeviceType;
+import applica.app.domain.supply.UsageStatus;
+import applica.crud.model.SimpleItem;
+import applica.crud.responses.Response;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.springframework.http.ResponseEntity.ok;
+
+@RestController
+@RequestMapping("/values")
+@SuppressWarnings("unused")
+public class ValuesController {
+
+    private final List<String> roles = List.of("ROLE_ADMIN", "ROLE_USER");
+    private final DocxFactory docxFactory;
+
+    public ValuesController(DocxFactory docxFactory) {
+        this.docxFactory = docxFactory;
+    }
+
+    @GetMapping("/roles")
+    public ResponseEntity<Response> allRoles() {
+        return ok().body(Response.value(roles.stream().map(SimpleItem::new).toList()));
+    }
+
+    @GetMapping("/rfid-device-types")
+    public ResponseEntity<Response> allRfidDeviceTypes() {
+        return ok().body(Response.value(Arrays.stream(RFIDDeviceType.values()).map(t -> new SimpleItem(t.name(), t.name())).toList()));
+    }
+
+    @GetMapping("/activity-status")
+    public ResponseEntity<Response> allActivityStatus() {
+        return ok().body(Response.value(Arrays.stream(ActivityStatus.values()).map(t -> new SimpleItem(t.name(), t.name())).toList()));
+    }
+
+    @GetMapping("/supply-status")
+    public ResponseEntity<Response> allSupplyStatus() {
+        return ok().body(Response.value(Arrays.stream(UsageStatus.values()).map(t -> new SimpleItem(t.name(), t.name())).toList()));
+    }
+
+    @GetMapping("/docx-types")
+    public ResponseEntity<Response> allDocxTypes() {
+        return ok().body(Response.value(docxFactory.getTypesNames().stream().map(SimpleItem::new).toList()));
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/ActivityDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/ActivityDataLayer.java
new file mode 100644 (file)
index 0000000..ecde39f
--- /dev/null
@@ -0,0 +1,87 @@
+package applica.app.web.crud;
+
+import applica.app.domain.maintenance.Activity;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.mongodb.MongoAggregateQuery;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.CrudQueryConverter;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static applica.crud.mongodb.MongoAggregationUtils.getTotalRows;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@Component
+@SuppressWarnings("unused")
+public class ActivityDataLayer implements DataLayer<Activity> {
+    private final DataLayer<Activity> dal;
+    private final MongoTemplate mongoTemplate;
+    private final CrudQueryConverter<MongoAggregateQuery> queryConverter;
+
+    public ActivityDataLayer(CrudFactory crudFactory, MongoTemplate mongoTemplate, CrudQueryConverter<MongoAggregateQuery> queryConverter) {
+        this.dal = crudFactory.createDataLayer(Activity.class);
+        this.mongoTemplate = mongoTemplate;
+        this.queryConverter = queryConverter;
+    }
+
+    @Override
+    public Optional<Activity> get(Object id) {
+        var query = CrudQuery.build().eq("_id", id).get();
+        return find(query)
+                .getRows()
+                .stream()
+                .findFirst();
+    }
+
+    @Override
+    public Result<Activity> find(CrudQuery query) {
+        var customerIdFilter = query.popFilter("customerId");
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            addAll(queryConverter.convert(query));
+            add(lookup("supply", "supplyId", "_id", "supply"));
+            add(unwind("supply"));
+            add(lookup("protocol", "supply.protocolId", "_id", "protocol"));
+            add(unwind("protocol"));
+            add(lookup("customer", "protocol.customerId", "_id", "customer"));
+            add(unwind("customer"));
+
+            customerIdFilter.ifPresent(filter -> add(match(where("customer._id").is(filter.getValue()))));
+        }};
+
+        var totalRows = getTotalRows(mongoTemplate, pipeline, Activity.class);
+        var pageable = query.getPageable();
+        var pageSize = pageable.getPageSize();
+        var page = pageable.getPageNumber();
+
+
+        pipeline.add(skip((long) (page) * pageSize));
+        pipeline.add(limit(pageSize));
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var results = mongoTemplate.aggregate(aggregation, Activity.class, Activity.class);
+        return new Result<>(results.getMappedResults(), totalRows, page, pageSize);
+    }
+
+    @Override
+    public void save(Activity entity) {
+        dal.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+        dal.delete(id);
+    }
+
+    @Override
+    public Class<Activity> getEntityType() {
+        return Activity.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/AuditLogDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/AuditLogDataLayer.java
new file mode 100644 (file)
index 0000000..ed2f71e
--- /dev/null
@@ -0,0 +1,92 @@
+package applica.app.web.crud;
+
+import applica.app.domain.audit.AuditLog;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.mongodb.MongoAggregateQuery;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.CrudQueryConverter;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static applica.crud.mongodb.MongoAggregationUtils.getTotalRows;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@Component
+@SuppressWarnings("unused")
+public class AuditLogDataLayer implements DataLayer<AuditLog> {
+    private final DataLayer<AuditLog> dal;
+    private final MongoTemplate mongoTemplate;
+    private final CrudQueryConverter<MongoAggregateQuery> queryConverter;
+
+    public AuditLogDataLayer(CrudFactory crudFactory, MongoTemplate mongoTemplate, CrudQueryConverter<MongoAggregateQuery> queryConverter) {
+        this.dal = crudFactory.createDataLayer(AuditLog.class);
+        this.mongoTemplate = mongoTemplate;
+        this.queryConverter = queryConverter;
+    }
+
+
+    @Override
+    public Optional<AuditLog> get(Object id) {
+        return dal.get(id);
+    }
+
+    @Override
+    public Result<AuditLog> find(CrudQuery query) {
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            var from = query.popFilter("from");
+            var to = query.popFilter("to");
+
+            if (from.isPresent() && to.isPresent()) {
+                var fromDate = LocalDateTime.parse(String.valueOf(from.get().getValue()));
+                var toDate = LocalDateTime.parse(String.valueOf(to.get().getValue()));
+                add(match(where("created").gte(fromDate).lte(toDate)));
+            } else if (from.isPresent()) {
+                var fromDate = LocalDateTime.parse(String.valueOf(from.get().getValue()));
+                add(match(where("created").gte(fromDate)));
+            } else if (to.isPresent()) {
+                var toDate = LocalDateTime.parse(String.valueOf(to.get().getValue()));
+                add(match(where("created").lte(toDate)));
+            }
+
+            addAll(queryConverter.convert(query));
+        }};
+
+        var totalRows = getTotalRows(mongoTemplate, pipeline, AuditLog.class);
+        var pageable = query.getPageable();
+        var pageSize = pageable.getPageSize();
+        var page = pageable.getPageNumber();
+        var sort = pageable.getSort();
+
+        pipeline.add(sort(sort));
+        pipeline.add(skip((long) (page) * pageSize));
+        pipeline.add(limit(pageSize));
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var results = mongoTemplate.aggregate(aggregation, AuditLog.class, AuditLog.class);
+        return new Result<>(results.getMappedResults(), totalRows, page, pageSize);
+    }
+
+    @Override
+    public void save(AuditLog entity) {
+        dal.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+        dal.delete(id);
+    }
+
+    @Override
+    public Class<AuditLog> getEntityType() {
+        return AuditLog.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/DefaultDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/DefaultDataLayer.java
new file mode 100644 (file)
index 0000000..29d0604
--- /dev/null
@@ -0,0 +1,34 @@
+package applica.app.web.crud;
+
+import applica.crud.mongodb.MongoDataLayer;
+import applica.crud.query.CrudQueryConverter;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Query;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+public class DefaultDataLayer<T> extends MongoDataLayer<T> {
+    public DefaultDataLayer(MongoTemplate mongoTemplate, CrudQueryConverter<Query> queryConverter) {
+        super(mongoTemplate, queryConverter);
+    }
+
+    @Override
+    public void save(Object entity) {
+        Class<?> clazz = entity.getClass();
+        while (clazz != null) {
+            for (Field field : clazz.getDeclaredFields()) {
+                if (Modifier.isTransient(field.getModifiers())) {
+                    field.setAccessible(true);
+                    try {
+                        field.set(entity, null);
+                    } catch (IllegalAccessException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+            clazz = clazz.getSuperclass();
+        }
+        super.save(entity);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/EquipmentDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/EquipmentDataLayer.java
new file mode 100644 (file)
index 0000000..7ec6171
--- /dev/null
@@ -0,0 +1,127 @@
+package applica.app.web.crud;
+
+import applica.app.domain.equipment.Equipment;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.mongodb.MongoAggregateQuery;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.CrudQueryConverter;
+import org.bson.Document;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static applica.crud.mongodb.MongoAggregationUtils.getTotalRows;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@Component
+@SuppressWarnings("unused")
+public class EquipmentDataLayer implements DataLayer<Equipment> {
+    private final DataLayer<Equipment> dal;
+    private final MongoTemplate mongoTemplate;
+    private final CrudQueryConverter<MongoAggregateQuery> queryConverter;
+
+    public EquipmentDataLayer(CrudFactory crudFactory, MongoTemplate mongoTemplate, CrudQueryConverter<MongoAggregateQuery> queryConverter) {
+        this.dal = crudFactory.createDataLayer(Equipment.class);
+        this.mongoTemplate = mongoTemplate;
+        this.queryConverter = queryConverter;
+    }
+
+    @Override
+    public Optional<Equipment> get(Object id) {
+        var query = CrudQuery.build().eq("_id", id).get();
+        var item = find(query)
+                .getRows()
+                .stream()
+                .findFirst();
+
+        return item;
+    }
+
+    @Override
+    public Result<Equipment> find(CrudQuery query) {
+        var customerIdFilter = query.popFilter("customerId");
+        var priceListIdFilter = query.popFilter("priceListId");
+        var supplierIdFilter = query.popFilter("supplierId");
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            addAll(queryConverter.convert(query));
+            add(lookup("supply", "_id", "equipmentId", "supply"));
+            add(lookup("protocol", "supply.protocolId", "_id", "protocol"));
+            add(lookup("customer", "protocol.customerId", "_id", "customer"));
+            add(lookup("equipmentType", "equipmentTypeId", "_id", "equipmentType"));
+            add(unwind("equipmentType"));
+
+            if (customerIdFilter.isPresent()) {
+                add(match(where("customer._id").is(customerIdFilter.get().getValue())));
+            }
+
+            if (priceListIdFilter.isPresent()) {
+                var priceListIdValue = priceListIdFilter.get().getValue();
+                add(lookup("supplier", "supplierId", "_id", "supplier"));
+                add(unwind("supplier"));
+                add(ctx -> new Document("$lookup", new Document()
+                        .append("from", "priceList")
+                        .append("localField", "supplier._id")
+                        .append("foreignField", "supplierId")
+                        .append("pipeline", new ArrayList<Document>() {{
+                            add(new Document("$match", new Document("_id", priceListIdValue)));
+                        }})
+                        .append("as", "priceList")
+                ));
+                add(unwind("priceList"));
+                add(ctx -> new Document("$lookup", new Document()
+                        .append("from", "priceListItem")
+                        .append("localField", "_id")
+                        .append("foreignField", "equipmentId")
+                        .append("pipeline", new ArrayList<Document>() {{
+                            add(new Document("$match", new Document("priceListId", priceListIdValue)));
+                        }})
+                        .append("as", "priceListItem")
+                ));
+                add(unwind("priceListItem", true));
+                add(match(Criteria.where("priceListItem.equipmentId").is(null)));
+            }
+
+            if (supplierIdFilter.isPresent()) {
+                add(lookup("supplier", "supplierId", "_id", "supplier"));
+                add(unwind("supplier"));
+                add(match(where("supplier._id").is(supplierIdFilter.get().getValue())));
+            }
+        }};
+
+        var totalRows = getTotalRows(mongoTemplate, pipeline, Equipment.class);
+        var pageable = query.getPageable();
+        var pageSize = pageable.getPageSize();
+        var page = pageable.getPageNumber();
+
+
+        pipeline.add(skip((long) (page) * pageSize));
+        pipeline.add(limit(pageSize));
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var results = mongoTemplate.aggregate(aggregation, Equipment.class, Equipment.class);
+        return new Result<>(results.getMappedResults(), totalRows, page, pageSize);
+    }
+
+    @Override
+    public void save(Equipment entity) {
+        dal.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+        dal.delete(id);
+    }
+
+    @Override
+    public Class<Equipment> getEntityType() {
+        return Equipment.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/EquipmentTypeDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/EquipmentTypeDataLayer.java
new file mode 100644 (file)
index 0000000..6a7bdb7
--- /dev/null
@@ -0,0 +1,84 @@
+package applica.app.web.crud;
+
+import applica.app.domain.equipment.EquipmentType;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.mongodb.MongoAggregateQuery;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.CrudQueryConverter;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static applica.crud.mongodb.MongoAggregationUtils.getTotalRows;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+@Component
+@SuppressWarnings("unused")
+public class EquipmentTypeDataLayer implements DataLayer<EquipmentType> {
+    private final DataLayer<EquipmentType> dal;
+    private final MongoTemplate mongoTemplate;
+    private final CrudQueryConverter<MongoAggregateQuery> queryConverter;
+
+    public EquipmentTypeDataLayer(CrudFactory crudFactory, MongoTemplate mongoTemplate, CrudQueryConverter<MongoAggregateQuery> queryConverter) {
+        this.dal = crudFactory.createDataLayer(EquipmentType.class);
+        this.mongoTemplate = mongoTemplate;
+        this.queryConverter = queryConverter;
+    }
+
+    @Override
+    public Optional<EquipmentType> get(Object id) {
+        var query = CrudQuery.build().eq("_id", id).get();
+        var item = find(query)
+                .getRows()
+                .stream()
+                .findFirst();
+
+        return item;
+    }
+
+    @Override
+    public Result<EquipmentType> find(CrudQuery query) {
+        var supplierId = query.popFilter("supplierId");
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            addAll(queryConverter.convert(query));
+            if (supplierId.isPresent()) {
+                add(lookup("equipment", "_id", "equipmentTypeId", "usedEquipments"));
+                add(match(Criteria.where("usedEquipments.supplierId").ne(supplierId.get().getValue())));
+            }
+        }};
+
+        var totalRows = getTotalRows(mongoTemplate, pipeline, EquipmentType.class);
+        var pageable = query.getPageable();
+        var pageSize = pageable.getPageSize();
+        var page = pageable.getPageNumber();
+
+
+        pipeline.add(skip((long) (page) * pageSize));
+        pipeline.add(limit(pageSize));
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var results = mongoTemplate.aggregate(aggregation, EquipmentType.class, EquipmentType.class);
+        return new Result<>(results.getMappedResults(), totalRows, page, pageSize);
+    }
+
+    @Override
+    public void save(EquipmentType entity) {
+            dal.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+            dal.delete(id);
+    }
+
+    @Override
+    public Class<EquipmentType> getEntityType() {
+        return EquipmentType.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/MaintenanceDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/MaintenanceDataLayer.java
new file mode 100644 (file)
index 0000000..afed2db
--- /dev/null
@@ -0,0 +1,93 @@
+package applica.app.web.crud;
+
+import applica.app.domain.maintenance.Maintenance;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.mongodb.MongoAggregateQuery;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.CrudQueryConverter;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static applica.crud.mongodb.MongoAggregationUtils.getTotalRows;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@Component
+@SuppressWarnings("unused")
+public class MaintenanceDataLayer implements DataLayer<Maintenance> {
+    private final DataLayer<Maintenance> dal;
+    private final MongoTemplate mongoTemplate;
+    private final CrudQueryConverter<MongoAggregateQuery> queryConverter;
+
+    public MaintenanceDataLayer(CrudFactory crudFactory, MongoTemplate mongoTemplate, CrudQueryConverter<MongoAggregateQuery> queryConverter) {
+        this.dal = crudFactory.createDataLayer(Maintenance.class);
+        this.mongoTemplate = mongoTemplate;
+        this.queryConverter = queryConverter;
+    }
+
+    @Override
+    public Optional<Maintenance> get(Object id) {
+        var query = CrudQuery.build().eq("_id", id).get();
+        var item = find(query)
+                .getRows()
+                .stream()
+                .findFirst();
+
+        return item;
+    }
+
+    @Override
+    public Result<Maintenance> find(CrudQuery query) {
+        var customerIdFilter = query.popFilter("customerId");
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            addAll(queryConverter.convert(query));
+            add(lookup("activity", "activityId", "_id", "activity"));
+            add(unwind("activity"));
+            add(lookup("supply", "activity.supplyId", "_id", "supply"));
+            add(unwind("supply"));
+            add(lookup("protocol", "supply.protocolId", "_id", "protocol"));
+            add(unwind("protocol"));
+            add(lookup("customer", "protocol.customerId", "_id", "customer"));
+            add(unwind("customer"));
+
+            if (customerIdFilter.isPresent()) {
+                add(match(where("customer._id").is(customerIdFilter.get().getValue())));
+            }
+        }};
+
+        var totalRows = getTotalRows(mongoTemplate, pipeline, Maintenance.class);
+        var pageable = query.getPageable();
+        var pageSize = pageable.getPageSize();
+        var page = pageable.getPageNumber();
+
+
+        pipeline.add(skip((long) (page) * pageSize));
+        pipeline.add(limit(pageSize));
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var results = mongoTemplate.aggregate(aggregation, Maintenance.class, Maintenance.class);
+        return new Result<>(results.getMappedResults(), totalRows, page, pageSize);
+    }
+
+    @Override
+    public void save(Maintenance entity) {
+        dal.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+        dal.delete(id);
+    }
+
+    @Override
+    public Class<Maintenance> getEntityType() {
+        return Maintenance.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/PriceListItemDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/PriceListItemDataLayer.java
new file mode 100644 (file)
index 0000000..ae83dbf
--- /dev/null
@@ -0,0 +1,88 @@
+package applica.app.web.crud;
+
+import applica.app.domain.pricing.PriceListItem;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.mongodb.MongoAggregateQuery;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.CrudQueryConverter;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static applica.crud.mongodb.MongoAggregationUtils.getTotalRows;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@Component
+@SuppressWarnings("unused")
+public class PriceListItemDataLayer implements DataLayer<PriceListItem> {
+    private final DataLayer<PriceListItem> dal;
+    private final MongoTemplate mongoTemplate;
+    private final CrudQueryConverter<MongoAggregateQuery> queryConverter;
+
+    public PriceListItemDataLayer(CrudFactory crudFactory, MongoTemplate mongoTemplate, CrudQueryConverter<MongoAggregateQuery> queryConverter) {
+        this.dal = crudFactory.createDataLayer(PriceListItem.class);
+        this.mongoTemplate = mongoTemplate;
+        this.queryConverter = queryConverter;
+    }
+
+    @Override
+    public Optional<PriceListItem> get(Object id) {
+        var query = CrudQuery.build().eq("_id", id).get();
+        var item = find(query)
+                .getRows()
+                .stream()
+                .findFirst();
+
+        return item;
+    }
+
+    @Override
+    public Result<PriceListItem> find(CrudQuery query) {
+        var priceListIdFilter = query.popFilter("priceListId");
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            addAll(queryConverter.convert(query));
+            add(lookup("priceList", "priceListId", "_id", "priceList"));
+            add(unwind("priceList"));
+            add(lookup("equipment", "equipmentId", "_id", "equipment"));
+            add(lookup("equipmentType", "equipment.equipmentTypeId", "_id", "equipmentType"));
+            add(unwind("equipmentType"));
+            if (priceListIdFilter.isPresent()) {
+                add(match(where("priceList._id").is(priceListIdFilter.get().getValue())));
+            }
+        }};
+
+        var totalRows = getTotalRows(mongoTemplate, pipeline, PriceListItem.class);
+        var pageable = query.getPageable();
+        var pageSize = pageable.getPageSize();
+        var page = pageable.getPageNumber();
+
+        pipeline.add(skip((long) (page) * pageSize));
+        pipeline.add(limit(pageSize));
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var results = mongoTemplate.aggregate(aggregation, PriceListItem.class, PriceListItem.class);
+        return new Result<>(results.getMappedResults(), totalRows, page, pageSize);
+    }
+
+    @Override
+    public void save(PriceListItem entity) {
+        dal.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+        dal.delete(id);
+    }
+
+    @Override
+    public Class<PriceListItem> getEntityType() {
+        return PriceListItem.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/ProtocolDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/ProtocolDataLayer.java
new file mode 100644 (file)
index 0000000..c5699ca
--- /dev/null
@@ -0,0 +1,86 @@
+package applica.app.web.crud;
+
+import applica.app.domain.protocol.Protocol;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.mongodb.MongoAggregateQuery;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.CrudQueryConverter;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static applica.crud.mongodb.MongoAggregationUtils.getTotalRows;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@Component
+@SuppressWarnings("unused")
+public class ProtocolDataLayer implements DataLayer<Protocol> {
+    private final DataLayer<Protocol> dal;
+    private final MongoTemplate mongoTemplate;
+    private final CrudQueryConverter<MongoAggregateQuery> queryConverter;
+
+    public ProtocolDataLayer(CrudFactory crudFactory, MongoTemplate mongoTemplate, CrudQueryConverter<MongoAggregateQuery> queryConverter) {
+        this.dal = crudFactory.createDataLayer(Protocol.class);
+        this.mongoTemplate = mongoTemplate;
+        this.queryConverter = queryConverter;
+    }
+
+    @Override
+    public Optional<Protocol> get(Object id) {
+        var query = CrudQuery.build().eq("_id", id).get();
+        var item = find(query)
+                .getRows()
+                .stream()
+                .findFirst();
+
+        return item;
+    }
+
+    @Override
+    public Result<Protocol> find(CrudQuery query) {
+        var customerIdFilter = query.popFilter("customerId");
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            addAll(queryConverter.convert(query));
+            add(lookup("customer", "customerId", "_id", "customer"));
+            add(unwind("customer"));
+
+            if (customerIdFilter.isPresent()) {
+                add(match(where("customer._id").is(customerIdFilter.get().getValue())));
+            }
+        }};
+
+        var totalRows = getTotalRows(mongoTemplate, pipeline, Protocol.class);
+        var pageable = query.getPageable();
+        var pageSize = pageable.getPageSize();
+        var page = pageable.getPageNumber();
+
+        pipeline.add(skip((long) (page) * pageSize));
+        pipeline.add(limit(pageSize));
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var results = mongoTemplate.aggregate(aggregation, Protocol.class, Protocol.class);
+        return new Result<>(results.getMappedResults(), totalRows, page, pageSize);
+    }
+
+    @Override
+    public void save(Protocol entity) {
+        dal.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+        dal.delete(id);
+    }
+
+    @Override
+    public Class<Protocol> getEntityType() {
+        return Protocol.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/RFIDDeviceTrackDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/RFIDDeviceTrackDataLayer.java
new file mode 100644 (file)
index 0000000..423dad1
--- /dev/null
@@ -0,0 +1,113 @@
+package applica.app.web.crud;
+
+import applica.app.domain.rfid.RFIDDeviceTrack;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.mongodb.MongoAggregateQuery;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.CrudQueryConverter;
+import applica.crud.query.builders.CrudQueryBuilder;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static applica.crud.mongodb.MongoAggregationUtils.getTotalRows;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@Component
+@SuppressWarnings("unused")
+public class RFIDDeviceTrackDataLayer implements DataLayer<RFIDDeviceTrack> {
+    private final DataLayer<RFIDDeviceTrack> dal;
+    private final CrudQueryConverter<MongoAggregateQuery> queryConverter;
+    private final MongoTemplate mongoTemplate;
+
+    public RFIDDeviceTrackDataLayer(CrudFactory crudFactory, CrudQueryConverter<MongoAggregateQuery> queryConverter, MongoTemplate mongoTemplate) {
+        this.dal = crudFactory.createDataLayer(RFIDDeviceTrack.class);
+        this.queryConverter = queryConverter;
+        this.mongoTemplate = mongoTemplate;
+    }
+
+    @Override
+    public Optional<RFIDDeviceTrack> get(Object id) {
+        return dal.get(id);
+    }
+
+    @Override
+    public Result<RFIDDeviceTrack> find(CrudQuery query) {
+        var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            var from = query.popFilter("from");
+            var to = query.popFilter("to");
+            if (from.isPresent() && to.isPresent()) {
+                var fromValue = from.get().getValue();
+                var toValue = to.get().getValue();
+                if (fromValue instanceof String && toValue instanceof String) {
+                    var fromDateTime = LocalDateTime.parse((String) fromValue, formatter);
+                    var toDateTime = LocalDateTime.parse((String) toValue, formatter);
+                    add(match(where("ts").gte(fromDateTime).lte(toDateTime)));
+                }
+            }
+            else if (from.isPresent()) {
+                var fromValue = from.get().getValue();
+                if (fromValue instanceof String) {
+                    var fromDateTime = LocalDateTime.parse((String) fromValue, formatter);
+                    add(match(where("ts").gte(fromDateTime)));
+                }
+            }
+            else if (to.isPresent()) {
+                var toValue = to.get().getValue();
+                if (toValue instanceof String) {
+                    var toDateTime = LocalDateTime.parse((String) toValue, formatter);
+                    add(match(where("ts").lte(toDateTime)));
+                }
+            }
+        }};
+
+        pipeline.addAll(queryConverter.convert(query));
+
+        var sorts = query.getSorts();
+        if (!sorts.isEmpty()) {
+            for (var sort : sorts) {
+                var sortField = sort.getProperty();
+                var sortDirection = sort.isDescending() ? Sort.Direction.DESC: Sort.Direction.ASC;
+                pipeline.add(sort(sortDirection, sortField));
+            }
+        }
+
+        var totalRows = getTotalRows(mongoTemplate, pipeline, RFIDDeviceTrack.class);
+        var pageable = query.getPageable();
+        var pageSize = pageable.getPageSize();
+        var page = pageable.getPageNumber();
+        pipeline.add(skip((long) (page) * pageSize));
+        pipeline.add(limit(pageSize));
+
+        var resultsAggregate = Aggregation.newAggregation(pipeline);
+        var results = mongoTemplate.aggregate(resultsAggregate, RFIDDeviceTrack.class, RFIDDeviceTrack.class);
+        return new Result<>(results.getMappedResults(), totalRows, page, pageSize);
+    }
+
+    @Override
+    public void save(RFIDDeviceTrack entity) {
+        dal.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+        dal.delete(id);
+    }
+
+    @Override
+    public Class<RFIDDeviceTrack> getEntityType() {
+        return RFIDDeviceTrack.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/crud/SupplyDataLayer.java b/edera-api/api/src/main/java/applica/app/web/crud/SupplyDataLayer.java
new file mode 100644 (file)
index 0000000..cbb5478
--- /dev/null
@@ -0,0 +1,124 @@
+package applica.app.web.crud;
+
+import applica.app.domain.supply.Supply;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.model.Result;
+import applica.crud.mongodb.MongoAggregateQuery;
+import applica.crud.query.CrudQuery;
+import applica.crud.query.CrudQueryConverter;
+import com.mongodb.BasicDBObject;
+import org.bson.Document;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static applica.crud.mongodb.MongoAggregationUtils.getTotalRows;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@Component
+@SuppressWarnings("unused")
+public class SupplyDataLayer implements DataLayer<Supply> {
+    private final DataLayer<Supply> dal;
+    private final MongoTemplate mongoTemplate;
+    private final CrudQueryConverter<MongoAggregateQuery> queryConverter;
+
+    public SupplyDataLayer(CrudFactory crudFactory, MongoTemplate mongoTemplate, CrudQueryConverter<MongoAggregateQuery> queryConverter) {
+        this.dal = crudFactory.createDataLayer(Supply.class);
+        this.mongoTemplate = mongoTemplate;
+        this.queryConverter = queryConverter;
+    }
+
+    @Override
+    public Optional<Supply> get(Object id) {
+        var query = CrudQuery.build().eq("_id", id).get();
+        var item = find(query)
+                .getRows()
+                .stream()
+                .findFirst();
+
+        return item;
+    }
+
+    @Override
+    public Result<Supply> find(CrudQuery query) {
+        var customerIdFilter = query.popFilter("customerId");
+        var keyword = query.getKeyword();
+
+        final Criteria criteria;
+
+        if ( keyword != null ) {
+            BasicDBObject regexObject = new BasicDBObject("$regex", keyword).append("$options", "i");
+
+            criteria = new Criteria().orOperator(
+                    Criteria.where("supplier.businessName").is(regexObject),
+                    Criteria.where("equipmentType.name").is(regexObject),
+                    Criteria.where("protocol.title").is(regexObject),
+                    Criteria.where("protocol.protocolNumber").is(regexObject),
+                    Criteria.where("ddtNumber").is(regexObject)
+            );
+        } else {
+            criteria = new Criteria();
+        }
+
+
+        var pipeline = new ArrayList<AggregationOperation>() {{
+            addAll(queryConverter.convert(query));
+            add(lookup("protocol", "protocolId", "_id", "protocol"));
+            add(unwind("protocol"));
+            add(lookup("customer", "protocol.customerId", "_id", "customer"));
+            add(unwind("customer"));
+
+            if (customerIdFilter.isPresent()) {
+                add(match(where("customer._id").is(customerIdFilter.get().getValue())));
+            }
+
+            add(lookup("equipment", "equipmentId", "_id", "equipment"));
+            add(unwind("equipment"));
+            add(lookup("equipmentType", "equipment.equipmentTypeId", "_id", "equipmentType"));
+            add(unwind("equipmentType"));
+            add(lookup("supplier", "equipment.supplierId", "_id", "supplier"));
+            add(unwind("supplier"));
+            add(lookup("usage", "_id", "supplyId", "usages"));
+            add(lookup("area", "latestRfidTrack.areaId", "_id", "latestArea"));
+            add(unwind("latestArea", true));
+
+            if (keyword != null) {
+                add(match(criteria));
+            }
+        }};
+
+        var totalRows = getTotalRows(mongoTemplate, pipeline, Supply.class);
+        var pageable = query.getPageable();
+        var pageSize = pageable.getPageSize();
+        var page = pageable.getPageNumber();
+
+        pipeline.add(skip((long) (page) * pageSize));
+        pipeline.add(limit(pageSize));
+
+        var aggregation = Aggregation.newAggregation(pipeline);
+        var results = mongoTemplate.aggregate(aggregation, Supply.class, Supply.class);
+        return new Result<>(results.getMappedResults(), totalRows, page, pageSize);
+    }
+
+    @Override
+    public void save(Supply entity) {
+        dal.save(entity);
+    }
+
+    @Override
+    public void delete(Object id) {
+        dal.delete(id);
+    }
+
+    @Override
+    public Class<Supply> getEntityType() {
+        return Supply.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/filters/AuditLogTraceFilter.java b/edera-api/api/src/main/java/applica/app/web/filters/AuditLogTraceFilter.java
new file mode 100644 (file)
index 0000000..1cfd302
--- /dev/null
@@ -0,0 +1,96 @@
+package applica.app.web.filters;
+
+import applica.app.domain.audit.AuditLog;
+import applica.app.domain.audit.AuditLogMessageBuilderFactory;
+import applica.app.utils.SecurityUtils;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import com.google.gson.Gson;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.springframework.web.util.ContentCachingRequestWrapper;
+import org.springframework.web.util.ContentCachingResponseWrapper;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+@Component
+@SuppressWarnings("unused")
+public class AuditLogTraceFilter extends OncePerRequestFilter {
+    public static final String BODY_PARAM = "body";
+
+    private final Gson gson = new Gson();
+
+    private Log logger = LogFactory.getLog(getClass());
+    private final DataLayer<AuditLog> auditLogDataLayer;
+    private final AuditLogMessageBuilderFactory auditLogMessageBuilderFactory;
+
+    public AuditLogTraceFilter(CrudFactory crudFactory, AuditLogMessageBuilderFactory auditLogMessageBuilderFactory) {
+        this.auditLogDataLayer = crudFactory.createDataLayer(AuditLog.class);
+        this.auditLogMessageBuilderFactory = auditLogMessageBuilderFactory;
+    }
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+        var requestWrapper = new ContentCachingRequestWrapper(request);
+        var responseWrapper = new ContentCachingResponseWrapper(response);
+        var requestData = new HashMap<String, Object>();
+        var skip = AuditLog.skip(request);
+        try {
+            if (!skip) {
+                trace(request, requestData);
+            }
+            filterChain.doFilter(requestWrapper, responseWrapper);
+        }
+        finally {
+            responseWrapper.copyBodyToResponse();
+            if (skip) {
+                return;
+            }
+            var body = new String(requestWrapper.getContentAsByteArray());
+
+            try {
+                var jsonBody = gson.fromJson(body, HashMap.class);
+                requestData.put(BODY_PARAM, jsonBody);
+            }
+            catch(Exception e) {
+                requestData.put(BODY_PARAM, body);
+            }
+            var auditLog = AuditLog.create(
+                    requestWrapper,
+                    gson.toJson(requestData),
+                    new String(responseWrapper.getContentAsByteArray()),
+                    SecurityUtils.getLoggedUser(),
+                    auditLogMessageBuilderFactory);
+
+            auditLogDataLayer.save(auditLog);
+        }
+    }
+
+
+
+    private void trace(HttpServletRequest request, HashMap<String, Object> requestBody) throws ServletException, IOException {
+        if (isMultiPart(request)) {
+            var requestParts = request.getParts();
+            for (var requestPart : requestParts) {
+                if (requestPart.getContentType().equals("application/json")) {
+                    var partBody = new String(requestPart.getInputStream().readAllBytes());
+                    var json = gson.fromJson(partBody, HashMap.class);
+                    requestBody.put(requestPart.getName(), json);
+                }
+            }
+        }
+    }
+
+    private boolean isMultiPart(HttpServletRequest request) {
+        return request.getContentType() != null && (
+                request.getContentType().startsWith("multipart/form-data")
+                        || request.getContentType().startsWith("multipart/mixed"));
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/filters/JwtAuthenticationFilter.java b/edera-api/api/src/main/java/applica/app/web/filters/JwtAuthenticationFilter.java
new file mode 100644 (file)
index 0000000..bcd9b91
--- /dev/null
@@ -0,0 +1,60 @@
+package applica.app.web.filters;
+
+import applica.app.utils.SecurityUtils;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.ResponseCodes;
+import applica.iam.sdk.requests.ValidateTokenRequest;
+import io.micrometer.common.util.StringUtils;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+@Order(1)
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+       final IAMService IAMService;
+
+       public JwtAuthenticationFilter(IAMService IAMService) {
+               this.IAMService = IAMService;
+       }
+
+       @Override
+       protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
+               var authorization = request.getHeader("Authorization");
+               var token = SecurityUtils.tokenFromAuthorizationHeader(authorization);
+               if (StringUtils.isEmpty(token)) {
+                       chain.doFilter(request, response);
+                       return;
+               }
+
+               var result = IAMService.validateToken(new ValidateTokenRequest(token));
+               if (!result.getResponseCode().equals(ResponseCodes.OK)) {
+                       chain.doFilter(request, response);
+                       return;
+               }
+
+               var authentication = UsernamePasswordAuthenticationToken.authenticated(
+                               result.getUser(),
+                               null,
+                               result.getUser()
+                                               .getRoles()
+                                               .stream()
+                                               .map(SimpleGrantedAuthority::new)
+                                               .toList()
+               );
+               authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+               SecurityContextHolder.getContext().setAuthentication(authentication);
+
+               chain.doFilter(request, response);
+       }
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/operations/EquipmentTypeGetOperation.java b/edera-api/api/src/main/java/applica/app/web/operations/EquipmentTypeGetOperation.java
new file mode 100644 (file)
index 0000000..5677042
--- /dev/null
@@ -0,0 +1,60 @@
+package applica.app.web.operations;
+
+import applica.app.domain.equipment.EquipmentType;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.mapping.EntityMapper;
+import applica.crud.operations.BaseGetOperation;
+import applica.crud.operations.OperationException;
+import applica.crud.operations.OperationsRouter;
+import applica.crud.operations.decorators.GetDecorator;
+import applica.crud.query.builders.CrudQueryBuilder;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+@Component
+public class EquipmentTypeGetOperation extends BaseGetOperation {
+
+    final CrudFactory crudFactory;
+    public EquipmentTypeGetOperation(EntityMapper entityMapper, Optional<OperationsRouter> router, CrudFactory crudFactory, GetDecorator[] decorators) {
+        super(entityMapper, router, crudFactory, decorators);
+        this.crudFactory = crudFactory;
+    }
+
+    @Override
+    protected void finishNode(Object entity, ObjectNode node) throws OperationException {
+        super.finishNode(entity, node);
+
+        List<Map<String, String>> parents = loadParents((EquipmentType) entity, new ArrayList<>(), crudFactory.createDataLayer(EquipmentType.class));
+        ArrayNode parentArray = node.putArray("parents");
+
+        for (Map<String, String> parent : parents) {
+            ObjectNode parentObj = parentArray.addObject();
+            parentObj.put("id", parent.get("id"));
+            parentObj.put("name", parent.get("name"));
+        }
+    }
+
+    private List<Map<String, String>> loadParents(EquipmentType equipmentType, List<Map<String, String>> parents, DataLayer<EquipmentType> dal) {
+        if (equipmentType.getParentId() != null) {
+            var parent = dal.find(CrudQueryBuilder.build().eq("id", equipmentType.getParentId())).getRows().get(0);
+
+            Map<String, String> parentData = new HashMap<>();
+            parentData.put("id", parent.getId());
+            parentData.put("name", parent.getName());
+            parents.add(parentData);
+
+            loadParents(parent, parents, dal);
+        }
+        return parents;
+    }
+
+
+    @Override
+    public Class<?> getEntityType() {
+        return EquipmentType.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/operations/I18nMessageDeleteOperation.java b/edera-api/api/src/main/java/applica/app/web/operations/I18nMessageDeleteOperation.java
new file mode 100644 (file)
index 0000000..4e137c8
--- /dev/null
@@ -0,0 +1,41 @@
+package applica.app.web.operations;
+
+import applica.app.domain.I18nMessage;
+import applica.crud.attachments.AttachmentsResolver;
+import applica.crud.constraints.ConstraintsChecker;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.operations.BaseDeleteOperation;
+import applica.crud.operations.OperationException;
+import applica.crud.operations.OperationsRouter;
+import applica.crud.operations.decorators.DeleteDecorator;
+import applica.crud.query.CrudQuery;
+import applica.fs.FsClient;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Optional;
+
+@Component
+@SuppressWarnings("unused")
+public class I18nMessageDeleteOperation extends BaseDeleteOperation {
+    final DataLayer<I18nMessage> dataLayer;
+
+    public I18nMessageDeleteOperation(FsClient fileServer, CrudFactory crudFactory, Optional<ConstraintsChecker> constraintsChecker, AttachmentsResolver attachmentsResolver, Optional<OperationsRouter> router, DeleteDecorator[] decorators) {
+        super(fileServer, crudFactory, constraintsChecker, attachmentsResolver, router, decorators);
+
+        this.dataLayer = crudFactory.createDataLayer(I18nMessage.class);
+    }
+
+    @Override
+    public void delete(List<String> ids) throws OperationException {
+        super.delete(ids);
+
+        I18nMessage.persistAsResource(dataLayer.find(CrudQuery.build()).getRows());
+    }
+
+    @Override
+    public Class<?> getEntityType() {
+        return I18nMessage.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/operations/I18nMessageSaveOperation.java b/edera-api/api/src/main/java/applica/app/web/operations/I18nMessageSaveOperation.java
new file mode 100644 (file)
index 0000000..d267d15
--- /dev/null
@@ -0,0 +1,41 @@
+package applica.app.web.operations;
+
+import applica.app.domain.I18nMessage;
+import applica.crud.attachments.AttachmentsResolver;
+import applica.crud.datalayer.DataLayer;
+import applica.crud.factory.CrudFactory;
+import applica.crud.mapping.EntityMapper;
+import applica.crud.operations.BaseSaveOperation;
+import applica.crud.operations.OperationException;
+import applica.crud.operations.OperationsRouter;
+import applica.crud.operations.decorators.SaveDecorator;
+import applica.crud.query.CrudQuery;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import jakarta.validation.Validator;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component
+@SuppressWarnings("unused")
+public class I18nMessageSaveOperation extends BaseSaveOperation {
+    final DataLayer<I18nMessage> dataLayer;
+
+    public I18nMessageSaveOperation(EntityMapper entityMapper, Optional<OperationsRouter> router, Validator validator, CrudFactory crudFactory, SaveDecorator[] decorators, AttachmentsResolver attachmentsResolver) {
+        super(entityMapper, router, validator, crudFactory, decorators, attachmentsResolver);
+
+        this.dataLayer = crudFactory.createDataLayer(I18nMessage.class);
+    }
+
+    @Override
+    protected void afterSave(ObjectNode node, Object entity) throws OperationException {
+        super.afterSave(node, entity);
+
+        I18nMessage.persistAsResource(dataLayer.find(CrudQuery.build()).getRows());
+    }
+
+    @Override
+    public Class<?> getEntityType() {
+        return I18nMessage.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/operations/NotificationFindOperation.java b/edera-api/api/src/main/java/applica/app/web/operations/NotificationFindOperation.java
new file mode 100644 (file)
index 0000000..c6647c4
--- /dev/null
@@ -0,0 +1,38 @@
+package applica.app.web.operations;
+
+import applica.app.domain.Notification;
+import applica.app.utils.SecurityUtils;
+import applica.crud.factory.CrudFactory;
+import applica.crud.mapping.EntityMapper;
+import applica.crud.operations.BaseFindOperation;
+import applica.crud.operations.OperationException;
+import applica.crud.operations.OperationsRouter;
+import applica.crud.operations.decorators.FindDecorator;
+import applica.crud.query.CrudQuery;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component
+@SuppressWarnings("unused")
+public class NotificationFindOperation extends BaseFindOperation {
+    public NotificationFindOperation(Optional<OperationsRouter> router, CrudFactory factory, EntityMapper entityMapper, FindDecorator[] decorators) {
+        super(router, factory, entityMapper, decorators);
+    }
+
+    @Override
+    public ObjectNode find(CrudQuery query) throws OperationException {
+        var loggedUser = SecurityUtils.getLoggedUser();
+        if (loggedUser.isPresent()) {
+            var builder = CrudQuery.build(query);
+            builder.eq("userId", loggedUser.get().getId());
+        }
+        return super.find(query);
+    }
+
+    @Override
+    public Class<?> getEntityType() {
+        return Notification.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/operations/PriceListItemSaveOperation.java b/edera-api/api/src/main/java/applica/app/web/operations/PriceListItemSaveOperation.java
new file mode 100644 (file)
index 0000000..024ae4c
--- /dev/null
@@ -0,0 +1,43 @@
+package applica.app.web.operations;
+
+import applica.app.domain.pricing.PriceListItem;
+import applica.crud.attachments.AttachmentsResolver;
+import applica.crud.factory.CrudFactory;
+import applica.crud.mapping.EntityMapper;
+import applica.crud.operations.BaseSaveOperation;
+import applica.crud.operations.OperationException;
+import applica.crud.operations.OperationsRouter;
+import applica.crud.operations.decorators.SaveDecorator;
+import applica.crud.query.builders.CrudQueryBuilder;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import jakarta.validation.Validator;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component
+public class PriceListItemSaveOperation extends BaseSaveOperation {
+
+    final CrudFactory crudFactory;
+    public PriceListItemSaveOperation(EntityMapper entityMapper, Optional<OperationsRouter> router, Validator validator, CrudFactory crudFactory, SaveDecorator[] decorators, AttachmentsResolver attachmentsResolver) {
+        super(entityMapper, router, validator, crudFactory, decorators, attachmentsResolver);
+        this.crudFactory = crudFactory;
+    }
+
+    @Override
+    protected void beforeSave(ObjectNode data, Object entity) throws OperationException {
+        super.beforeSave(data, entity);
+        PriceListItem priceListItem = (PriceListItem) entity;
+
+        if (priceListItem.getId() == null) {
+            if (crudFactory.createDataLayer(PriceListItem.class).find(CrudQueryBuilder.build().eq("equipmentId", priceListItem.getEquipmentId()).eq("priceListId", priceListItem.getPriceListId())).getRows().size() > 0) {
+              throw new OperationException("ra.error.price-list-item.save.equipmentId-already-exists");
+            }
+        }
+    }
+
+    @Override
+    public Class<?> getEntityType() {
+        return PriceListItem.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/operations/PriceListSaveOperation.java b/edera-api/api/src/main/java/applica/app/web/operations/PriceListSaveOperation.java
new file mode 100644 (file)
index 0000000..a41ae00
--- /dev/null
@@ -0,0 +1,38 @@
+package applica.app.web.operations;
+
+import applica.app.domain.pricing.PriceList;
+import applica.crud.attachments.AttachmentsResolver;
+import applica.crud.factory.CrudFactory;
+import applica.crud.mapping.EntityMapper;
+import applica.crud.operations.BaseSaveOperation;
+import applica.crud.operations.OperationException;
+import applica.crud.operations.OperationsRouter;
+import applica.crud.operations.decorators.SaveDecorator;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import jakarta.validation.Validator;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component
+public class PriceListSaveOperation extends BaseSaveOperation {
+
+    final CrudFactory crudFactory;
+    public PriceListSaveOperation(EntityMapper entityMapper, Optional<OperationsRouter> router, Validator validator, CrudFactory crudFactory, SaveDecorator[] decorators, AttachmentsResolver attachmentsResolver) {
+        super(entityMapper, router, validator, crudFactory, decorators, attachmentsResolver);
+        this.crudFactory = crudFactory;
+    }
+
+    @Override
+    protected void beforeSave(ObjectNode data, Object entity) throws OperationException {
+        PriceList priceList = (PriceList) entity;
+        if (priceList.getStart().isAfter(priceList.getEnd())) {
+            throw new OperationException("ra.error.price-list.save.start-after-end");
+        }
+    }
+
+    @Override
+    public Class<?> getEntityType() {
+        return PriceList.class;
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/operations/decorators/CustomerFilterableFindDecorator.java b/edera-api/api/src/main/java/applica/app/web/operations/decorators/CustomerFilterableFindDecorator.java
new file mode 100644 (file)
index 0000000..1170252
--- /dev/null
@@ -0,0 +1,25 @@
+package applica.app.web.operations.decorators;
+
+import applica.app.utils.SecurityUtils;
+import applica.app.web.annotations.CustomerFilterable;
+import applica.crud.operations.OperationException;
+import applica.crud.operations.decorators.FindDecoratorAdapter;
+import applica.crud.query.CrudQuery;
+import org.springframework.util.StringUtils;
+
+public class CustomerFilterableFindDecorator extends FindDecoratorAdapter {
+    @Override
+    public void onBeforeFind(Class<?> entityType, CrudQuery query) throws OperationException {
+        if (entityType.isAnnotationPresent(CustomerFilterable.class)) {
+            var loggedUser = SecurityUtils.getLoggedUser();
+            if (loggedUser.isPresent()) {
+                var customerId = loggedUser.get().getProfile().get("customerId");
+                if (StringUtils.hasLength(customerId)){
+                    var builder = CrudQuery.build(query);
+                    builder.eq("customerId", customerId);
+                }
+            }
+        }
+        super.onBeforeFind(entityType, query);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/requests/GetReportDataRequest.java b/edera-api/api/src/main/java/applica/app/web/requests/GetReportDataRequest.java
new file mode 100644 (file)
index 0000000..36836fd
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.app.web.requests;
+
+import applica.app.domain.reporting.FilterSet;
+
+public class GetReportDataRequest extends FilterSet {
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/requests/PatchProfileRequest.java b/edera-api/api/src/main/java/applica/app/web/requests/PatchProfileRequest.java
new file mode 100644 (file)
index 0000000..0d0668d
--- /dev/null
@@ -0,0 +1,12 @@
+package applica.app.web.requests;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class PatchProfileRequest {
+    String name;
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/responses/ActivityInfoResponse.java b/edera-api/api/src/main/java/applica/app/web/responses/ActivityInfoResponse.java
new file mode 100644 (file)
index 0000000..c5407cc
--- /dev/null
@@ -0,0 +1,23 @@
+package applica.app.web.responses;
+
+import applica.app.domain.dto.ActivityDetailsDTO;
+import applica.crud.responses.Response;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper=false)
+public class ActivityInfoResponse extends Response {
+    private ActivityDetailsDTO activityDetailsDTO;
+
+    public ActivityInfoResponse(ActivityDetailsDTO activityDetailsDTO) {
+        super(Response.OK);
+        this.activityDetailsDTO = activityDetailsDTO;
+    }
+
+    public ActivityInfoResponse(String responseCode) {
+        super(responseCode);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/responses/CrudLoginResponse.java b/edera-api/api/src/main/java/applica/app/web/responses/CrudLoginResponse.java
new file mode 100644 (file)
index 0000000..0ae05d3
--- /dev/null
@@ -0,0 +1,30 @@
+package applica.app.web.responses;
+
+import applica.crud.acl.CrudSecurityConfigurer;
+import applica.iam.sdk.readmodel.UserView;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+
+import java.util.List;
+
+@Getter
+@ToString
+@EqualsAndHashCode
+public class CrudLoginResponse {
+    public CrudLoginResponse(String responseCode, UserView user, String token) {
+        this.responseCode = responseCode;
+        this.user = user;
+        this.token = token;
+
+        if (user != null) {
+            crudPermissions = CrudSecurityConfigurer.instance().getCrudPermissionsByRoles(user.getRoles());
+        }
+    }
+
+    String responseCode;
+    UserView user;
+    String token;
+    List<String> crudPermissions;
+
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/responses/GetReportDataResponse.java b/edera-api/api/src/main/java/applica/app/web/responses/GetReportDataResponse.java
new file mode 100644 (file)
index 0000000..c7e521a
--- /dev/null
@@ -0,0 +1,21 @@
+package applica.app.web.responses;
+
+import applica.crud.responses.Response;
+import lombok.Data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+public class GetReportDataResponse extends Response {
+    Map<String, Object> data = new HashMap<>();
+
+        public GetReportDataResponse(Map map) {
+            super(Response.OK);
+            this.data = map;
+        }
+
+    public GetReportDataResponse(String responseCode) {
+        super(responseCode);
+    }
+}
diff --git a/edera-api/api/src/main/java/applica/app/web/responses/SupplyInfoResponse.java b/edera-api/api/src/main/java/applica/app/web/responses/SupplyInfoResponse.java
new file mode 100644 (file)
index 0000000..31b7e92
--- /dev/null
@@ -0,0 +1,23 @@
+package applica.app.web.responses;
+
+import applica.app.domain.dto.SupplyDetailsDTO;
+import applica.crud.responses.Response;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode(callSuper=false)
+public class SupplyInfoResponse extends Response {
+    private SupplyDetailsDTO supplyDetailsDTO;
+
+    public SupplyInfoResponse(SupplyDetailsDTO supplyDetailsDTO) {
+        super(Response.OK);
+        this.supplyDetailsDTO = supplyDetailsDTO;
+    }
+
+    public SupplyInfoResponse(String responseCode) {
+        super(responseCode);
+    }
+}
\ No newline at end of file
diff --git a/edera-api/api/src/main/java/applica/app/web/security/JwtAuthenticationEntryPoint.java b/edera-api/api/src/main/java/applica/app/web/security/JwtAuthenticationEntryPoint.java
new file mode 100644 (file)
index 0000000..41ab871
--- /dev/null
@@ -0,0 +1,18 @@
+package applica.app.web.security;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+       @Override
+       public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
+               response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
+       }
+}
diff --git a/edera-api/api/src/main/resources/application-production.yaml b/edera-api/api/src/main/resources/application-production.yaml
new file mode 100644 (file)
index 0000000..b01a5fb
--- /dev/null
@@ -0,0 +1,13 @@
+spring:
+  data:
+    mongodb:
+      uri: mongodb+srv://edera:pS9GGYdSRDm94Pdr@edera.a8nnng4.mongodb.net
+      database: production
+
+ml:
+  endpoint: http://ml:5000
+
+app:
+  mode: production
+  test: false
+
diff --git a/edera-api/api/src/main/resources/application-test.yaml b/edera-api/api/src/main/resources/application-test.yaml
new file mode 100644 (file)
index 0000000..b4a4856
--- /dev/null
@@ -0,0 +1,11 @@
+spring:
+  data:
+    mongodb:
+      uri: mongodb+srv://edera:pS9GGYdSRDm94Pdr@edera.a8nnng4.mongodb.net
+      database: test
+      # uri: mongodb://localhost:27017
+      # database: edera-test
+      auto-index-creation: true
+app:
+  mode: test
+  test: true
\ No newline at end of file
diff --git a/edera-api/api/src/main/resources/application.yaml b/edera-api/api/src/main/resources/application.yaml
new file mode 100644 (file)
index 0000000..a4003b3
--- /dev/null
@@ -0,0 +1,96 @@
+server:
+  port: 8080
+  servlet:
+    context-path: /api
+
+spring:
+  servlet:
+    multipart:
+      max-file-size: 8MB
+  data:
+    mongodb:
+      uri: mongodb://localhost:27017
+      database: edera
+      auto-index-creation: true
+    rest:
+      basePath: /crud
+  mail:
+    host: smtp-relay.sendinblue.com
+    port: 587
+    username: bruno.fortunato@applica.guru
+    password: pAbxCmgwLRFWHcJB
+    smtp:
+      auth: true
+      starttls:
+        enable: true
+jwt:
+  token:
+    secret: applica
+    expire-seconds: 3000
+
+mail:
+  from: "noreply@applica.guru"
+  templates:
+    path: "templates"
+
+fs:
+  local:
+    base-path: "/tmp"
+    images:
+      max-size: "2532x2532"
+  gcp:
+    credentials: "classpath:/credentials/service-account.json"
+    project-id: "edera"
+    bucket-name: "edera-bucket"
+
+cors:
+  allowed-origins: "*"
+
+iam:
+  max-login-attempts: 3
+  default-role: ROLE_USER   # Utilizzatore
+  setup:
+    roles:
+      - ROLE_ADMIN      # Administrator
+      - ROLE_OPERATOR   # Operatore
+      - ROLE_USER       # Utilizzatore
+      - ROLE_MAINTAINER # Manutentore
+      - ROLE_CUSTOMER   # Cliente
+    admin:
+      email: admin@applica.guru
+      password: YXBwbGljYQ== #base64
+      role: ROLE_ADMIN
+  http:
+    url: "http://localhost:8082/iam"
+
+management:
+  endpoints:
+    web:
+      cors:
+        allowed-origins: "*"
+        allowed-methods: "OPTIONS,GET,POST"
+      exposure:
+        include: "info,health,httptrace"
+  endpoint:
+    health:
+      probes:
+        enabled: true
+  health:
+    livenessState:
+      enabled: true
+    readinessState:
+      enabled: true
+
+app:
+  test: false
+  mode: development
+
+geo:
+  csv:
+    nation: "/csv/nations.csv"
+    region: "/csv/regions.csv"
+    province: "/csv/provinces.csv"
+    city: "/csv/cities.csv"
+
+ml:
+  endpoint: http://localhost:5000
\ No newline at end of file
diff --git a/edera-api/api/src/main/resources/credentials/service-account.json b/edera-api/api/src/main/resources/credentials/service-account.json
new file mode 100644 (file)
index 0000000..5ed0315
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "type": "service_account",
+  "project_id": "applica-general",
+  "private_key_id": "497c6f5597f45275f7e9fe4031c4b829e2687121",
+  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCt4kcPwQRzmD7o\npyUAPeNpgKMSXlS3nBbemIAeri7vnFxrH0UxFRgvdMU9sE2z/HyYsS0cgGTwcMEw\nBUUaQwQjT0UqvPk7prciNYDRMuMAv51nXZJQgDXz+LHmyVWahMUE9Ls38c2qyAij\nU0cjHjNll2jwKby9bU5ETqiSxKqtMEEYpIAsWi/XmbzLz6UmJyZvnzd+dDSQA57h\ncCl8ASvPiA7HyusoYsUbvKlQKKfrwmWsARYDV7QsKzlJlGEbXMlZZ3w0uT+3VLW9\nMc2KQhHjoijhMorF7Uv5V22kMi2/exFB7/q+nYBz59+S1QveFjBwq9/H0A4rMzTk\nmfCc8c1fAgMBAAECggEANGWlvTkQItcjSc8oJrD/iKizOxNC2wtFlvEEVnpteVW4\nQa1sF7TAE3jQAN1SJOT2FLr7GYYVJKENje9gmd/M7OjzskO8pL2BnOTbeu6nGfAj\nueSn9OsVlv1/ekh8K7JLfklS6zJJo+dgNvseyhXNLhVYkVo6XiiEd6/uOz/ZIJOU\nLYG7XOTq15hJrfZ3kkt0AFWIscJIuElPeQ2Nh5VcuRyRpQT0hP45IQQ6EtIAYzx9\nRtFQaawkiYQjgDdzQNBmmPsveg8cbdAyVeYqr+ZVAb6MiZ2v7MQGHrxAArxRytLf\n2gvUIt+zovbAzj7Cxtb5/KrCtu00/ZY7074qLLgGSQKBgQDv5td6lVmH9qywM8Ss\n6p94NeMQkmOo0N5J0TR+1LaHNpdv0ZcptlBCKIBMz+U0AsJtpmuvZFLgGrjzSq6p\nTYFR+42W562F2J1ZiJfggX7nmjwNhT77kue0n5VlMbLpT7B3j0zWqmsHjgOeehCn\nxB09zJqrDboz2EwPeoFHCV0ytwKBgQC5jVjIwmEwMOSh9PqwC2yIPXy/5RJFLn2/\n5lsRhQnI+NLSeBcZtHWPb9g25QzHUUudQ4hbajGh35GkyHkHr2y0ecu1TwjUN+F0\no7az4szI8ky3FOlEJous8zqMAk+bxEGXkQCdZab/ImKK/GWQ+mQqyCWDmRAoGsUq\nLBMuHJ9ymQKBgF+FrADcXOTdXI9gXYx4c3zbAAmGMHZ0jD4aNevWaMNYAl58ttLe\nDAD7vXJYSSw3eRFN9YzFxpYDLed5sigpezeYkR0oLJih0q1mzQqQpWY0rHq5toVX\nTjlGXacIbfOmTl6cyXykKK++ZUSB2AXk+bu0r1UyxxSDqG11WupdGSXrAoGBAJ6a\nroB0foSl1liFwd7G9Q+Dl2Wj1bki40QsED3qfRG3duWG1yAWu8JOtP8/TGv3Fm4n\nsp+JJ0GZi7HR1np2PbIKxdCF7SMRXPrJkbszqx481sxL6JRjalL8WVgiBZA88mAv\nBtqDcHp3Fsp8sgh5rzNOf5p8NG5Da7L/l6l7t+NJAoGBAOgz0s5Xa+G6KNiU7B2H\nsIv2dM36o6VwkmOOqct+dU/2pmvdc8hZClVFapPyfxv1jVscH5+dnEhRHi13awHS\nW772t9YGA5jFoggjEOENPmTvAL1wMM/A1D+fjuZYP+8EChE4BafmSwYfFW2QBlDx\nfXh8sfOAg28n0G5AcFqvUHRM\n-----END PRIVATE KEY-----\n",
+  "client_email": "bitbucket-pipelines@applica-general.iam.gserviceaccount.com",
+  "client_id": "108394815677481426260",
+  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
+  "token_uri": "https://oauth2.googleapis.com/token",
+  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/bitbucket-pipelines%40applica-general.iam.gserviceaccount.com",
+  "universe_domain": "googleapis.com"
+}
diff --git a/edera-api/api/src/main/resources/csv/cities.csv b/edera-api/api/src/main/resources/csv/cities.csv
new file mode 100644 (file)
index 0000000..f9f113a
--- /dev/null
@@ -0,0 +1,7905 @@
+comune,pro_com_t,den_prov,sigla,den_reg,cod_reg,cap
+Agliè,1001,Torino,TO,Piemonte,1,10011
+Airasca,1002,Torino,TO,Piemonte,1,10060
+Ala di Stura,1003,Torino,TO,Piemonte,1,10070
+Albiano d'Ivrea,1004,Torino,TO,Piemonte,1,10010
+Almese,1006,Torino,TO,Piemonte,1,10040
+Alpette,1007,Torino,TO,Piemonte,1,10080
+Alpignano,1008,Torino,TO,Piemonte,1,10091
+Andezeno,1009,Torino,TO,Piemonte,1,10020
+Andrate,1010,Torino,TO,Piemonte,1,10010
+Angrogna,1011,Torino,TO,Piemonte,1,10060
+Arignano,1012,Torino,TO,Piemonte,1,10020
+Avigliana,1013,Torino,TO,Piemonte,1,10051
+Azeglio,1014,Torino,TO,Piemonte,1,10010
+Bairo,1015,Torino,TO,Piemonte,1,10010
+Balangero,1016,Torino,TO,Piemonte,1,10070
+Baldissero Canavese,1017,Torino,TO,Piemonte,1,10080
+Baldissero Torinese,1018,Torino,TO,Piemonte,1,10020
+Balme,1019,Torino,TO,Piemonte,1,10070
+Banchette,1020,Torino,TO,Piemonte,1,10010
+Barbania,1021,Torino,TO,Piemonte,1,10070
+Bardonecchia,1022,Torino,TO,Piemonte,1,10052
+Barone Canavese,1023,Torino,TO,Piemonte,1,10010
+Beinasco,1024,Torino,TO,Piemonte,1,10092
+Bibiana,1025,Torino,TO,Piemonte,1,10060
+Bobbio Pellice,1026,Torino,TO,Piemonte,1,10060
+Bollengo,1027,Torino,TO,Piemonte,1,10012
+Borgaro Torinese,1028,Torino,TO,Piemonte,1,10071
+Borgiallo,1029,Torino,TO,Piemonte,1,10080
+Borgofranco d'Ivrea,1030,Torino,TO,Piemonte,1,10013
+Borgomasino,1031,Torino,TO,Piemonte,1,10031
+Borgone Susa,1032,Torino,TO,Piemonte,1,10050
+Bosconero,1033,Torino,TO,Piemonte,1,10080
+Brandizzo,1034,Torino,TO,Piemonte,1,10032
+Bricherasio,1035,Torino,TO,Piemonte,1,10060
+Brosso,1036,Torino,TO,Piemonte,1,10080
+Brozolo,1037,Torino,TO,Piemonte,1,10020
+Bruino,1038,Torino,TO,Piemonte,1,10090
+Brusasco,1039,Torino,TO,Piemonte,1,10020
+Bruzolo,1040,Torino,TO,Piemonte,1,10050
+Buriasco,1041,Torino,TO,Piemonte,1,10060
+Burolo,1042,Torino,TO,Piemonte,1,10010
+Busano,1043,Torino,TO,Piemonte,1,10080
+Bussoleno,1044,Torino,TO,Piemonte,1,10053
+Buttigliera Alta,1045,Torino,TO,Piemonte,1,10090
+Cafasse,1046,Torino,TO,Piemonte,1,10070
+Caluso,1047,Torino,TO,Piemonte,1,10014
+Cambiano,1048,Torino,TO,Piemonte,1,10020
+Campiglione Fenile,1049,Torino,TO,Piemonte,1,10060
+Candia Canavese,1050,Torino,TO,Piemonte,1,10010
+Candiolo,1051,Torino,TO,Piemonte,1,10060
+Canischio,1052,Torino,TO,Piemonte,1,10080
+Cantalupa,1053,Torino,TO,Piemonte,1,10060
+Cantoira,1054,Torino,TO,Piemonte,1,10070
+Caprie,1055,Torino,TO,Piemonte,1,10040
+Caravino,1056,Torino,TO,Piemonte,1,10010
+Carema,1057,Torino,TO,Piemonte,1,10010
+Carignano,1058,Torino,TO,Piemonte,1,10041
+Carmagnola,1059,Torino,TO,Piemonte,1,10022
+Casalborgone,1060,Torino,TO,Piemonte,1,10020
+Cascinette d'Ivrea,1061,Torino,TO,Piemonte,1,10010
+Caselette,1062,Torino,TO,Piemonte,1,10040
+Caselle Torinese,1063,Torino,TO,Piemonte,1,10072
+Castagneto Po,1064,Torino,TO,Piemonte,1,10090
+Castagnole Piemonte,1065,Torino,TO,Piemonte,1,10060
+Castellamonte,1066,Torino,TO,Piemonte,1,10081
+Castelnuovo Nigra,1067,Torino,TO,Piemonte,1,10080
+Castiglione Torinese,1068,Torino,TO,Piemonte,1,10090
+Cavagnolo,1069,Torino,TO,Piemonte,1,10020
+Cavour,1070,Torino,TO,Piemonte,1,10061
+Cercenasco,1071,Torino,TO,Piemonte,1,10060
+Ceres,1072,Torino,TO,Piemonte,1,10070
+Ceresole Reale,1073,Torino,TO,Piemonte,1,10080
+Cesana Torinese,1074,Torino,TO,Piemonte,1,10054
+Chialamberto,1075,Torino,TO,Piemonte,1,10070
+Chianocco,1076,Torino,TO,Piemonte,1,10050
+Chiaverano,1077,Torino,TO,Piemonte,1,10010
+Chieri,1078,Torino,TO,Piemonte,1,10023
+Chiesanuova,1079,Torino,TO,Piemonte,1,10080
+Chiomonte,1080,Torino,TO,Piemonte,1,10050
+Chiusa di San Michele,1081,Torino,TO,Piemonte,1,10050
+Chivasso,1082,Torino,TO,Piemonte,1,10034
+Ciconio,1083,Torino,TO,Piemonte,1,10080
+Cintano,1084,Torino,TO,Piemonte,1,10080
+Cinzano,1085,Torino,TO,Piemonte,1,10090
+Ciriè,1086,Torino,TO,Piemonte,1,10073
+Claviere,1087,Torino,TO,Piemonte,1,10050
+Coassolo Torinese,1088,Torino,TO,Piemonte,1,10070
+Coazze,1089,Torino,TO,Piemonte,1,10050
+Collegno,1090,Torino,TO,Piemonte,1,10093
+Colleretto Castelnuovo,1091,Torino,TO,Piemonte,1,10080
+Colleretto Giacosa,1092,Torino,TO,Piemonte,1,10010
+Condove,1093,Torino,TO,Piemonte,1,10055
+Corio,1094,Torino,TO,Piemonte,1,10070
+Cossano Canavese,1095,Torino,TO,Piemonte,1,10010
+Cuceglio,1096,Torino,TO,Piemonte,1,10090
+Cumiana,1097,Torino,TO,Piemonte,1,10040
+Cuorgnè,1098,Torino,TO,Piemonte,1,10082
+Druento,1099,Torino,TO,Piemonte,1,10040
+Exilles,1100,Torino,TO,Piemonte,1,10050
+Favria,1101,Torino,TO,Piemonte,1,10083
+Feletto,1102,Torino,TO,Piemonte,1,10080
+Fenestrelle,1103,Torino,TO,Piemonte,1,10060
+Fiano,1104,Torino,TO,Piemonte,1,10070
+Fiorano Canavese,1105,Torino,TO,Piemonte,1,10010
+Foglizzo,1106,Torino,TO,Piemonte,1,10090
+Forno Canavese,1107,Torino,TO,Piemonte,1,10084
+Frassinetto,1108,Torino,TO,Piemonte,1,10080
+Front,1109,Torino,TO,Piemonte,1,10070
+Frossasco,1110,Torino,TO,Piemonte,1,10060
+Garzigliana,1111,Torino,TO,Piemonte,1,10060
+Gassino Torinese,1112,Torino,TO,Piemonte,1,10090
+Germagnano,1113,Torino,TO,Piemonte,1,10070
+Giaglione,1114,Torino,TO,Piemonte,1,10050
+Giaveno,1115,Torino,TO,Piemonte,1,10094
+Givoletto,1116,Torino,TO,Piemonte,1,10040
+Gravere,1117,Torino,TO,Piemonte,1,10050
+Groscavallo,1118,Torino,TO,Piemonte,1,10070
+Grosso,1119,Torino,TO,Piemonte,1,10070
+Grugliasco,1120,Torino,TO,Piemonte,1,10095
+Ingria,1121,Torino,TO,Piemonte,1,10080
+Inverso Pinasca,1122,Torino,TO,Piemonte,1,10060
+Isolabella,1123,Torino,TO,Piemonte,1,10046
+Issiglio,1124,Torino,TO,Piemonte,1,10080
+Ivrea,1125,Torino,TO,Piemonte,1,10015
+La Cassa,1126,Torino,TO,Piemonte,1,10040
+La Loggia,1127,Torino,TO,Piemonte,1,10040
+Lanzo Torinese,1128,Torino,TO,Piemonte,1,10074
+Lauriano,1129,Torino,TO,Piemonte,1,10020
+Leini,1130,Torino,TO,Piemonte,1,10040
+Lemie,1131,Torino,TO,Piemonte,1,10070
+Lessolo,1132,Torino,TO,Piemonte,1,10010
+Levone,1133,Torino,TO,Piemonte,1,10070
+Locana,1134,Torino,TO,Piemonte,1,10080
+Lombardore,1135,Torino,TO,Piemonte,1,10040
+Lombriasco,1136,Torino,TO,Piemonte,1,10040
+Loranzè,1137,Torino,TO,Piemonte,1,10010
+Luserna San Giovanni,1139,Torino,TO,Piemonte,1,10062
+Lusernetta,1140,Torino,TO,Piemonte,1,10060
+Lusigliè,1141,Torino,TO,Piemonte,1,10080
+Macello,1142,Torino,TO,Piemonte,1,10060
+Maglione,1143,Torino,TO,Piemonte,1,10030
+Marentino,1144,Torino,TO,Piemonte,1,10020
+Massello,1145,Torino,TO,Piemonte,1,10060
+Mathi,1146,Torino,TO,Piemonte,1,10075
+Mattie,1147,Torino,TO,Piemonte,1,10050
+Mazzè,1148,Torino,TO,Piemonte,1,10035
+Meana di Susa,1149,Torino,TO,Piemonte,1,10050
+Mercenasco,1150,Torino,TO,Piemonte,1,10010
+Mezzenile,1152,Torino,TO,Piemonte,1,10070
+Mombello di Torino,1153,Torino,TO,Piemonte,1,10020
+Mompantero,1154,Torino,TO,Piemonte,1,10059
+Monastero di Lanzo,1155,Torino,TO,Piemonte,1,10070
+Moncalieri,1156,Torino,TO,Piemonte,1,10024
+Moncenisio,1157,Torino,TO,Piemonte,1,10050
+Montaldo Torinese,1158,Torino,TO,Piemonte,1,10020
+Montalenghe,1159,Torino,TO,Piemonte,1,10090
+Montalto Dora,1160,Torino,TO,Piemonte,1,10016
+Montanaro,1161,Torino,TO,Piemonte,1,10017
+Monteu da Po,1162,Torino,TO,Piemonte,1,10020
+Moriondo Torinese,1163,Torino,TO,Piemonte,1,10020
+Nichelino,1164,Torino,TO,Piemonte,1,10042
+Noasca,1165,Torino,TO,Piemonte,1,10080
+Nole,1166,Torino,TO,Piemonte,1,10076
+Nomaglio,1167,Torino,TO,Piemonte,1,10010
+None,1168,Torino,TO,Piemonte,1,10060
+Novalesa,1169,Torino,TO,Piemonte,1,10050
+Oglianico,1170,Torino,TO,Piemonte,1,10080
+Orbassano,1171,Torino,TO,Piemonte,1,10043
+Orio Canavese,1172,Torino,TO,Piemonte,1,10010
+Osasco,1173,Torino,TO,Piemonte,1,10060
+Osasio,1174,Torino,TO,Piemonte,1,10040
+Oulx,1175,Torino,TO,Piemonte,1,10056
+Ozegna,1176,Torino,TO,Piemonte,1,10080
+Palazzo Canavese,1177,Torino,TO,Piemonte,1,10010
+Pancalieri,1178,Torino,TO,Piemonte,1,10060
+Parella,1179,Torino,TO,Piemonte,1,10010
+Pavarolo,1180,Torino,TO,Piemonte,1,10020
+Pavone Canavese,1181,Torino,TO,Piemonte,1,10018
+Pecetto Torinese,1183,Torino,TO,Piemonte,1,10020
+Perosa Argentina,1184,Torino,TO,Piemonte,1,10063
+Perosa Canavese,1185,Torino,TO,Piemonte,1,10010
+Perrero,1186,Torino,TO,Piemonte,1,10060
+Pertusio,1187,Torino,TO,Piemonte,1,10080
+Pessinetto,1188,Torino,TO,Piemonte,1,10070
+Pianezza,1189,Torino,TO,Piemonte,1,10044
+Pinasca,1190,Torino,TO,Piemonte,1,10060
+Pinerolo,1191,Torino,TO,Piemonte,1,10064
+Pino Torinese,1192,Torino,TO,Piemonte,1,10025
+Piobesi Torinese,1193,Torino,TO,Piemonte,1,10040
+Piossasco,1194,Torino,TO,Piemonte,1,10045
+Piscina,1195,Torino,TO,Piemonte,1,10060
+Piverone,1196,Torino,TO,Piemonte,1,10010
+Poirino,1197,Torino,TO,Piemonte,1,10046
+Pomaretto,1198,Torino,TO,Piemonte,1,10063
+Pont Canavese,1199,Torino,TO,Piemonte,1,10085
+Porte,1200,Torino,TO,Piemonte,1,10060
+Pragelato,1201,Torino,TO,Piemonte,1,10060
+Prali,1202,Torino,TO,Piemonte,1,10060
+Pralormo,1203,Torino,TO,Piemonte,1,10040
+Pramollo,1204,Torino,TO,Piemonte,1,10065
+Prarostino,1205,Torino,TO,Piemonte,1,10060
+Prascorsano,1206,Torino,TO,Piemonte,1,10080
+Pratiglione,1207,Torino,TO,Piemonte,1,10080
+Quagliuzzo,1208,Torino,TO,Piemonte,1,10010
+Quassolo,1209,Torino,TO,Piemonte,1,10010
+Quincinetto,1210,Torino,TO,Piemonte,1,10010
+Reano,1211,Torino,TO,Piemonte,1,10090
+Ribordone,1212,Torino,TO,Piemonte,1,10080
+Rivalba,1213,Torino,TO,Piemonte,1,10090
+Rivalta di Torino,1214,Torino,TO,Piemonte,1,10040
+Riva presso Chieri,1215,Torino,TO,Piemonte,1,10020
+Rivara,1216,Torino,TO,Piemonte,1,10080
+Rivarolo Canavese,1217,Torino,TO,Piemonte,1,10086
+Rivarossa,1218,Torino,TO,Piemonte,1,10040
+Rivoli,1219,Torino,TO,Piemonte,1,10098
+Robassomero,1220,Torino,TO,Piemonte,1,10070
+Rocca Canavese,1221,Torino,TO,Piemonte,1,10070
+Roletto,1222,Torino,TO,Piemonte,1,10060
+Romano Canavese,1223,Torino,TO,Piemonte,1,10090
+Ronco Canavese,1224,Torino,TO,Piemonte,1,10080
+Rondissone,1225,Torino,TO,Piemonte,1,10030
+Rorà,1226,Torino,TO,Piemonte,1,10060
+Roure,1227,Torino,TO,Piemonte,1,10060
+Rosta,1228,Torino,TO,Piemonte,1,10090
+Rubiana,1229,Torino,TO,Piemonte,1,10040
+Rueglio,1230,Torino,TO,Piemonte,1,10010
+Salassa,1231,Torino,TO,Piemonte,1,10080
+Salbertrand,1232,Torino,TO,Piemonte,1,10050
+Salerano Canavese,1233,Torino,TO,Piemonte,1,10010
+Salza di Pinerolo,1234,Torino,TO,Piemonte,1,10060
+Samone,1235,Torino,TO,Piemonte,1,10010
+San Benigno Canavese,1236,Torino,TO,Piemonte,1,10080
+San Carlo Canavese,1237,Torino,TO,Piemonte,1,10070
+San Colombano Belmonte,1238,Torino,TO,Piemonte,1,10080
+San Didero,1239,Torino,TO,Piemonte,1,10050
+San Francesco al Campo,1240,Torino,TO,Piemonte,1,10070
+Sangano,1241,Torino,TO,Piemonte,1,10090
+San Germano Chisone,1242,Torino,TO,Piemonte,1,10065
+San Gillio,1243,Torino,TO,Piemonte,1,10040
+San Giorgio Canavese,1244,Torino,TO,Piemonte,1,10090
+San Giorio di Susa,1245,Torino,TO,Piemonte,1,10050
+San Giusto Canavese,1246,Torino,TO,Piemonte,1,10090
+San Martino Canavese,1247,Torino,TO,Piemonte,1,10010
+San Maurizio Canavese,1248,Torino,TO,Piemonte,1,10077
+San Mauro Torinese,1249,Torino,TO,Piemonte,1,10099
+San Pietro Val Lemina,1250,Torino,TO,Piemonte,1,10060
+San Ponso,1251,Torino,TO,Piemonte,1,10080
+San Raffaele Cimena,1252,Torino,TO,Piemonte,1,10090
+San Sebastiano da Po,1253,Torino,TO,Piemonte,1,10020
+San Secondo di Pinerolo,1254,Torino,TO,Piemonte,1,10060
+Sant'Ambrogio di Torino,1255,Torino,TO,Piemonte,1,10057
+Sant'Antonino di Susa,1256,Torino,TO,Piemonte,1,10050
+Santena,1257,Torino,TO,Piemonte,1,10026
+Sauze di Cesana,1258,Torino,TO,Piemonte,1,10054
+Sauze d'Oulx,1259,Torino,TO,Piemonte,1,10050
+Scalenghe,1260,Torino,TO,Piemonte,1,10060
+Scarmagno,1261,Torino,TO,Piemonte,1,10010
+Sciolze,1262,Torino,TO,Piemonte,1,10090
+Sestriere,1263,Torino,TO,Piemonte,1,10058
+Settimo Rottaro,1264,Torino,TO,Piemonte,1,10010
+Settimo Torinese,1265,Torino,TO,Piemonte,1,10036
+Settimo Vittone,1266,Torino,TO,Piemonte,1,10010
+Sparone,1267,Torino,TO,Piemonte,1,10080
+Strambinello,1268,Torino,TO,Piemonte,1,10010
+Strambino,1269,Torino,TO,Piemonte,1,10019
+Susa,1270,Torino,TO,Piemonte,1,10059
+Tavagnasco,1271,Torino,TO,Piemonte,1,10010
+Torino,1272,Torino,TO,Piemonte,1,10138
+Torrazza Piemonte,1273,Torino,TO,Piemonte,1,10037
+Torre Canavese,1274,Torino,TO,Piemonte,1,10010
+Torre Pellice,1275,Torino,TO,Piemonte,1,10066
+Trana,1276,Torino,TO,Piemonte,1,10090
+Traversella,1278,Torino,TO,Piemonte,1,10080
+Traves,1279,Torino,TO,Piemonte,1,10070
+Trofarello,1280,Torino,TO,Piemonte,1,10028
+Usseaux,1281,Torino,TO,Piemonte,1,10060
+Usseglio,1282,Torino,TO,Piemonte,1,10070
+Vaie,1283,Torino,TO,Piemonte,1,10050
+Val della Torre,1284,Torino,TO,Piemonte,1,10040
+Valgioie,1285,Torino,TO,Piemonte,1,10094
+Vallo Torinese,1286,Torino,TO,Piemonte,1,10070
+Valperga,1287,Torino,TO,Piemonte,1,10087
+Valprato Soana,1288,Torino,TO,Piemonte,1,10080
+Varisella,1289,Torino,TO,Piemonte,1,10070
+Vauda Canavese,1290,Torino,TO,Piemonte,1,10070
+Venaus,1291,Torino,TO,Piemonte,1,10050
+Venaria Reale,1292,Torino,TO,Piemonte,1,10078
+Verolengo,1293,Torino,TO,Piemonte,1,10038
+Verrua Savoia,1294,Torino,TO,Piemonte,1,10020
+Vestignè,1295,Torino,TO,Piemonte,1,10030
+Vialfrè,1296,Torino,TO,Piemonte,1,10090
+Vidracco,1298,Torino,TO,Piemonte,1,10080
+Vigone,1299,Torino,TO,Piemonte,1,10067
+Villafranca Piemonte,1300,Torino,TO,Piemonte,1,10068
+Villanova Canavese,1301,Torino,TO,Piemonte,1,10070
+Villarbasse,1302,Torino,TO,Piemonte,1,10090
+Villar Dora,1303,Torino,TO,Piemonte,1,10040
+Villareggia,1304,Torino,TO,Piemonte,1,10030
+Villar Focchiardo,1305,Torino,TO,Piemonte,1,10050
+Villar Pellice,1306,Torino,TO,Piemonte,1,10060
+Villar Perosa,1307,Torino,TO,Piemonte,1,10069
+Villastellone,1308,Torino,TO,Piemonte,1,10029
+Vinovo,1309,Torino,TO,Piemonte,1,10048
+Virle Piemonte,1310,Torino,TO,Piemonte,1,10060
+Vische,1311,Torino,TO,Piemonte,1,10030
+Vistrorio,1312,Torino,TO,Piemonte,1,10080
+Viù,1313,Torino,TO,Piemonte,1,10070
+Volpiano,1314,Torino,TO,Piemonte,1,10088
+Volvera,1315,Torino,TO,Piemonte,1,10040
+Mappano,1316,Torino,TO,Piemonte,1,10072
+Val di Chy,1317,Torino,TO,Piemonte,1,10010
+Valchiusa,1318,Torino,TO,Piemonte,1,10080
+Alagna Valsesia,2002,Vercelli,VC,Piemonte,1,13021
+Albano Vercellese,2003,Vercelli,VC,Piemonte,1,13030
+Alice Castello,2004,Vercelli,VC,Piemonte,1,13040
+Arborio,2006,Vercelli,VC,Piemonte,1,13031
+Asigliano Vercellese,2007,Vercelli,VC,Piemonte,1,13032
+Balmuccia,2008,Vercelli,VC,Piemonte,1,13020
+Balocco,2009,Vercelli,VC,Piemonte,1,13040
+Bianzè,2011,Vercelli,VC,Piemonte,1,13041
+Boccioleto,2014,Vercelli,VC,Piemonte,1,13022
+Borgo d'Ale,2015,Vercelli,VC,Piemonte,1,13040
+Borgosesia,2016,Vercelli,VC,Piemonte,1,13011
+Borgo Vercelli,2017,Vercelli,VC,Piemonte,1,13012
+Buronzo,2021,Vercelli,VC,Piemonte,1,13040
+Campertogno,2025,Vercelli,VC,Piemonte,1,13023
+Carcoforo,2029,Vercelli,VC,Piemonte,1,13026
+Caresana,2030,Vercelli,VC,Piemonte,1,13010
+Caresanablot,2031,Vercelli,VC,Piemonte,1,13030
+Carisio,2032,Vercelli,VC,Piemonte,1,13040
+Casanova Elvo,2033,Vercelli,VC,Piemonte,1,13030
+San Giacomo Vercellese,2035,Vercelli,VC,Piemonte,1,13030
+Cervatto,2041,Vercelli,VC,Piemonte,1,13025
+Cigliano,2042,Vercelli,VC,Piemonte,1,13043
+Civiasco,2043,Vercelli,VC,Piemonte,1,13010
+Collobiano,2045,Vercelli,VC,Piemonte,1,13030
+Costanzana,2047,Vercelli,VC,Piemonte,1,13033
+Cravagliana,2048,Vercelli,VC,Piemonte,1,13020
+Crescentino,2049,Vercelli,VC,Piemonte,1,13044
+Crova,2052,Vercelli,VC,Piemonte,1,13040
+Desana,2054,Vercelli,VC,Piemonte,1,13034
+Fobello,2057,Vercelli,VC,Piemonte,1,13025
+Fontanetto Po,2058,Vercelli,VC,Piemonte,1,13040
+Formigliana,2059,Vercelli,VC,Piemonte,1,13030
+Gattinara,2061,Vercelli,VC,Piemonte,1,13045
+Ghislarengo,2062,Vercelli,VC,Piemonte,1,13030
+Greggio,2065,Vercelli,VC,Piemonte,1,13030
+Guardabosone,2066,Vercelli,VC,Piemonte,1,13010
+Lamporo,2067,Vercelli,VC,Piemonte,1,13046
+Lenta,2068,Vercelli,VC,Piemonte,1,13035
+Lignana,2070,Vercelli,VC,Piemonte,1,13034
+Livorno Ferraris,2071,Vercelli,VC,Piemonte,1,13046
+Lozzolo,2072,Vercelli,VC,Piemonte,1,13045
+Mollia,2078,Vercelli,VC,Piemonte,1,13020
+Moncrivello,2079,Vercelli,VC,Piemonte,1,13040
+Motta de' Conti,2082,Vercelli,VC,Piemonte,1,13010
+Olcenengo,2088,Vercelli,VC,Piemonte,1,13047
+Oldenico,2089,Vercelli,VC,Piemonte,1,13030
+Palazzolo Vercellese,2090,Vercelli,VC,Piemonte,1,13040
+Pertengo,2091,Vercelli,VC,Piemonte,1,13030
+Pezzana,2093,Vercelli,VC,Piemonte,1,13010
+Pila,2096,Vercelli,VC,Piemonte,1,13020
+Piode,2097,Vercelli,VC,Piemonte,1,13020
+Postua,2102,Vercelli,VC,Piemonte,1,13010
+Prarolo,2104,Vercelli,VC,Piemonte,1,13012
+Quarona,2107,Vercelli,VC,Piemonte,1,13017
+Quinto Vercellese,2108,Vercelli,VC,Piemonte,1,13030
+Rassa,2110,Vercelli,VC,Piemonte,1,13020
+Rimella,2113,Vercelli,VC,Piemonte,1,13020
+Rive,2115,Vercelli,VC,Piemonte,1,13030
+Roasio,2116,Vercelli,VC,Piemonte,1,13060
+Ronsecco,2118,Vercelli,VC,Piemonte,1,13036
+Rossa,2121,Vercelli,VC,Piemonte,1,13020
+Rovasenda,2122,Vercelli,VC,Piemonte,1,13040
+Salasco,2126,Vercelli,VC,Piemonte,1,13040
+Sali Vercellese,2127,Vercelli,VC,Piemonte,1,13040
+Saluggia,2128,Vercelli,VC,Piemonte,1,13040
+San Germano Vercellese,2131,Vercelli,VC,Piemonte,1,13047
+Santhià,2133,Vercelli,VC,Piemonte,1,13048
+Scopa,2134,Vercelli,VC,Piemonte,1,13027
+Scopello,2135,Vercelli,VC,Piemonte,1,13028
+Serravalle Sesia,2137,Vercelli,VC,Piemonte,1,13037
+Stroppiana,2142,Vercelli,VC,Piemonte,1,13010
+Tricerro,2147,Vercelli,VC,Piemonte,1,13038
+Trino,2148,Vercelli,VC,Piemonte,1,13039
+Tronzano Vercellese,2150,Vercelli,VC,Piemonte,1,13049
+Valduggia,2152,Vercelli,VC,Piemonte,1,13018
+Varallo,2156,Vercelli,VC,Piemonte,1,13019
+Vercelli,2158,Vercelli,VC,Piemonte,1,13100
+Villarboit,2163,Vercelli,VC,Piemonte,1,13030
+Villata,2164,Vercelli,VC,Piemonte,1,13010
+Vocca,2166,Vercelli,VC,Piemonte,1,13020
+Alto Sermenza,2170,Vercelli,VC,Piemonte,1,13026
+Cellio con Breia,2171,Vercelli,VC,Piemonte,1,13024
+Agrate Conturbia,3001,Novara,NO,Piemonte,1,28010
+Ameno,3002,Novara,NO,Piemonte,1,28010
+Armeno,3006,Novara,NO,Piemonte,1,28011
+Arona,3008,Novara,NO,Piemonte,1,28041
+Barengo,3012,Novara,NO,Piemonte,1,28010
+Bellinzago Novarese,3016,Novara,NO,Piemonte,1,28043
+Biandrate,3018,Novara,NO,Piemonte,1,28061
+Boca,3019,Novara,NO,Piemonte,1,28010
+Bogogno,3021,Novara,NO,Piemonte,1,28010
+Bolzano Novarese,3022,Novara,NO,Piemonte,1,28010
+Borgolavezzaro,3023,Novara,NO,Piemonte,1,28071
+Borgomanero,3024,Novara,NO,Piemonte,1,28021
+Borgo Ticino,3025,Novara,NO,Piemonte,1,28040
+Briga Novarese,3026,Novara,NO,Piemonte,1,28010
+Briona,3027,Novara,NO,Piemonte,1,28072
+Caltignaga,3030,Novara,NO,Piemonte,1,28010
+Cameri,3032,Novara,NO,Piemonte,1,28062
+Carpignano Sesia,3036,Novara,NO,Piemonte,1,28064
+Casalbeltrame,3037,Novara,NO,Piemonte,1,28060
+Casaleggio Novara,3039,Novara,NO,Piemonte,1,28060
+Casalino,3040,Novara,NO,Piemonte,1,28060
+Casalvolone,3041,Novara,NO,Piemonte,1,28060
+Castellazzo Novarese,3042,Novara,NO,Piemonte,1,28060
+Castelletto sopra Ticino,3043,Novara,NO,Piemonte,1,28053
+Cavaglietto,3044,Novara,NO,Piemonte,1,28010
+Cavaglio d'Agogna,3045,Novara,NO,Piemonte,1,28010
+Cavallirio,3047,Novara,NO,Piemonte,1,28010
+Cerano,3049,Novara,NO,Piemonte,1,28065
+Colazza,3051,Novara,NO,Piemonte,1,28010
+Comignago,3052,Novara,NO,Piemonte,1,28060
+Cressa,3055,Novara,NO,Piemonte,1,28012
+Cureggio,3058,Novara,NO,Piemonte,1,28060
+Divignano,3060,Novara,NO,Piemonte,1,28010
+Dormelletto,3062,Novara,NO,Piemonte,1,28040
+Fara Novarese,3065,Novara,NO,Piemonte,1,28073
+Fontaneto d'Agogna,3066,Novara,NO,Piemonte,1,28010
+Galliate,3068,Novara,NO,Piemonte,1,28066
+Garbagna Novarese,3069,Novara,NO,Piemonte,1,28070
+Gargallo,3070,Novara,NO,Piemonte,1,28010
+Ghemme,3073,Novara,NO,Piemonte,1,28074
+Gozzano,3076,Novara,NO,Piemonte,1,28024
+Granozzo con Monticello,3077,Novara,NO,Piemonte,1,28060
+Grignasco,3079,Novara,NO,Piemonte,1,28075
+Invorio,3082,Novara,NO,Piemonte,1,28045
+Landiona,3083,Novara,NO,Piemonte,1,28064
+Lesa,3084,Novara,NO,Piemonte,1,28040
+Maggiora,3088,Novara,NO,Piemonte,1,28014
+Mandello Vitta,3090,Novara,NO,Piemonte,1,28060
+Marano Ticino,3091,Novara,NO,Piemonte,1,28040
+Massino Visconti,3093,Novara,NO,Piemonte,1,28040
+Meina,3095,Novara,NO,Piemonte,1,28046
+Mezzomerico,3097,Novara,NO,Piemonte,1,28040
+Miasino,3098,Novara,NO,Piemonte,1,28010
+Momo,3100,Novara,NO,Piemonte,1,28015
+Nebbiuno,3103,Novara,NO,Piemonte,1,28010
+Nibbiola,3104,Novara,NO,Piemonte,1,28070
+Novara,3106,Novara,NO,Piemonte,1,28100
+Oleggio,3108,Novara,NO,Piemonte,1,28047
+Oleggio Castello,3109,Novara,NO,Piemonte,1,28040
+Orta San Giulio,3112,Novara,NO,Piemonte,1,28016
+Paruzzaro,3114,Novara,NO,Piemonte,1,28040
+Pella,3115,Novara,NO,Piemonte,1,28010
+Pettenasco,3116,Novara,NO,Piemonte,1,28028
+Pisano,3119,Novara,NO,Piemonte,1,28010
+Pogno,3120,Novara,NO,Piemonte,1,28076
+Pombia,3121,Novara,NO,Piemonte,1,28050
+Prato Sesia,3122,Novara,NO,Piemonte,1,28077
+Recetto,3129,Novara,NO,Piemonte,1,28060
+Romagnano Sesia,3130,Novara,NO,Piemonte,1,28078
+Romentino,3131,Novara,NO,Piemonte,1,28068
+San Maurizio d'Opaglio,3133,Novara,NO,Piemonte,1,28017
+San Nazzaro Sesia,3134,Novara,NO,Piemonte,1,28060
+San Pietro Mosezzo,3135,Novara,NO,Piemonte,1,28060
+Sillavengo,3138,Novara,NO,Piemonte,1,28064
+Sizzano,3139,Novara,NO,Piemonte,1,28070
+Soriso,3140,Novara,NO,Piemonte,1,28010
+Sozzago,3141,Novara,NO,Piemonte,1,28060
+Suno,3143,Novara,NO,Piemonte,1,28019
+Terdobbiate,3144,Novara,NO,Piemonte,1,28070
+Tornaco,3146,Novara,NO,Piemonte,1,28070
+Trecate,3149,Novara,NO,Piemonte,1,28069
+Vaprio d'Agogna,3153,Novara,NO,Piemonte,1,28010
+Varallo Pombia,3154,Novara,NO,Piemonte,1,28040
+Vespolate,3158,Novara,NO,Piemonte,1,28079
+Vicolungo,3159,Novara,NO,Piemonte,1,28060
+Vinzaglio,3164,Novara,NO,Piemonte,1,28060
+Gattico-Veruno,3166,Novara,NO,Piemonte,1,28013
+Acceglio,4001,Cuneo,CN,Piemonte,1,12021
+Aisone,4002,Cuneo,CN,Piemonte,1,12010
+Alba,4003,Cuneo,CN,Piemonte,1,12051
+Albaretto della Torre,4004,Cuneo,CN,Piemonte,1,12050
+Alto,4005,Cuneo,CN,Piemonte,1,12070
+Argentera,4006,Cuneo,CN,Piemonte,1,12010
+Arguello,4007,Cuneo,CN,Piemonte,1,12050
+Bagnasco,4008,Cuneo,CN,Piemonte,1,12071
+Bagnolo Piemonte,4009,Cuneo,CN,Piemonte,1,12031
+Baldissero d'Alba,4010,Cuneo,CN,Piemonte,1,12040
+Barbaresco,4011,Cuneo,CN,Piemonte,1,12050
+Barge,4012,Cuneo,CN,Piemonte,1,12032
+Barolo,4013,Cuneo,CN,Piemonte,1,12060
+Bastia Mondovì,4014,Cuneo,CN,Piemonte,1,12060
+Battifollo,4015,Cuneo,CN,Piemonte,1,12070
+Beinette,4016,Cuneo,CN,Piemonte,1,12081
+Bellino,4017,Cuneo,CN,Piemonte,1,12020
+Belvedere Langhe,4018,Cuneo,CN,Piemonte,1,12060
+Bene Vagienna,4019,Cuneo,CN,Piemonte,1,12041
+Benevello,4020,Cuneo,CN,Piemonte,1,12050
+Bergolo,4021,Cuneo,CN,Piemonte,1,12074
+Bernezzo,4022,Cuneo,CN,Piemonte,1,12010
+Bonvicino,4023,Cuneo,CN,Piemonte,1,12060
+Borgomale,4024,Cuneo,CN,Piemonte,1,12050
+Borgo San Dalmazzo,4025,Cuneo,CN,Piemonte,1,12011
+Bosia,4026,Cuneo,CN,Piemonte,1,12050
+Bossolasco,4027,Cuneo,CN,Piemonte,1,12060
+Boves,4028,Cuneo,CN,Piemonte,1,12012
+Bra,4029,Cuneo,CN,Piemonte,1,12042
+Briaglia,4030,Cuneo,CN,Piemonte,1,12080
+Briga Alta,4031,Cuneo,CN,Piemonte,1,18025
+Brondello,4032,Cuneo,CN,Piemonte,1,12030
+Brossasco,4033,Cuneo,CN,Piemonte,1,12020
+Busca,4034,Cuneo,CN,Piemonte,1,12022
+Camerana,4035,Cuneo,CN,Piemonte,1,12072
+Canale,4037,Cuneo,CN,Piemonte,1,12043
+Canosio,4038,Cuneo,CN,Piemonte,1,12020
+Caprauna,4039,Cuneo,CN,Piemonte,1,12070
+Caraglio,4040,Cuneo,CN,Piemonte,1,12023
+Caramagna Piemonte,4041,Cuneo,CN,Piemonte,1,12030
+Cardè,4042,Cuneo,CN,Piemonte,1,12030
+Carrù,4043,Cuneo,CN,Piemonte,1,12061
+Cartignano,4044,Cuneo,CN,Piemonte,1,12020
+Casalgrasso,4045,Cuneo,CN,Piemonte,1,12030
+Castagnito,4046,Cuneo,CN,Piemonte,1,12050
+Casteldelfino,4047,Cuneo,CN,Piemonte,1,12020
+Castelletto Stura,4049,Cuneo,CN,Piemonte,1,12040
+Castelletto Uzzone,4050,Cuneo,CN,Piemonte,1,12070
+Castellinaldo d'Alba,4051,Cuneo,CN,Piemonte,1,12050
+Castellino Tanaro,4052,Cuneo,CN,Piemonte,1,12060
+Castelmagno,4053,Cuneo,CN,Piemonte,1,12020
+Castelnuovo di Ceva,4054,Cuneo,CN,Piemonte,1,12070
+Castiglione Falletto,4055,Cuneo,CN,Piemonte,1,12060
+Castiglione Tinella,4056,Cuneo,CN,Piemonte,1,12053
+Castino,4057,Cuneo,CN,Piemonte,1,12050
+Cavallerleone,4058,Cuneo,CN,Piemonte,1,12030
+Cavallermaggiore,4059,Cuneo,CN,Piemonte,1,12030
+Celle di Macra,4060,Cuneo,CN,Piemonte,1,12020
+Centallo,4061,Cuneo,CN,Piemonte,1,12044
+Ceresole Alba,4062,Cuneo,CN,Piemonte,1,12040
+Cerretto Langhe,4063,Cuneo,CN,Piemonte,1,12050
+Cervasca,4064,Cuneo,CN,Piemonte,1,12010
+Cervere,4065,Cuneo,CN,Piemonte,1,12040
+Ceva,4066,Cuneo,CN,Piemonte,1,12073
+Cherasco,4067,Cuneo,CN,Piemonte,1,12062
+Chiusa di Pesio,4068,Cuneo,CN,Piemonte,1,12013
+Cigliè,4069,Cuneo,CN,Piemonte,1,12060
+Cissone,4070,Cuneo,CN,Piemonte,1,12050
+Clavesana,4071,Cuneo,CN,Piemonte,1,12060
+Corneliano d'Alba,4072,Cuneo,CN,Piemonte,1,12040
+Cortemilia,4073,Cuneo,CN,Piemonte,1,12074
+Cossano Belbo,4074,Cuneo,CN,Piemonte,1,12054
+Costigliole Saluzzo,4075,Cuneo,CN,Piemonte,1,12024
+Cravanzana,4076,Cuneo,CN,Piemonte,1,12050
+Crissolo,4077,Cuneo,CN,Piemonte,1,12030
+Cuneo,4078,Cuneo,CN,Piemonte,1,12100
+Demonte,4079,Cuneo,CN,Piemonte,1,12014
+Diano d'Alba,4080,Cuneo,CN,Piemonte,1,12055
+Dogliani,4081,Cuneo,CN,Piemonte,1,12063
+Dronero,4082,Cuneo,CN,Piemonte,1,12025
+Elva,4083,Cuneo,CN,Piemonte,1,12020
+Entracque,4084,Cuneo,CN,Piemonte,1,12010
+Envie,4085,Cuneo,CN,Piemonte,1,12030
+Farigliano,4086,Cuneo,CN,Piemonte,1,12060
+Faule,4087,Cuneo,CN,Piemonte,1,12030
+Feisoglio,4088,Cuneo,CN,Piemonte,1,12050
+Fossano,4089,Cuneo,CN,Piemonte,1,12045
+Frabosa Soprana,4090,Cuneo,CN,Piemonte,1,12082
+Frabosa Sottana,4091,Cuneo,CN,Piemonte,1,12083
+Frassino,4092,Cuneo,CN,Piemonte,1,12020
+Gaiola,4093,Cuneo,CN,Piemonte,1,12010
+Gambasca,4094,Cuneo,CN,Piemonte,1,12030
+Garessio,4095,Cuneo,CN,Piemonte,1,12075
+Genola,4096,Cuneo,CN,Piemonte,1,12040
+Gorzegno,4097,Cuneo,CN,Piemonte,1,12070
+Gottasecca,4098,Cuneo,CN,Piemonte,1,12070
+Govone,4099,Cuneo,CN,Piemonte,1,12040
+Grinzane Cavour,4100,Cuneo,CN,Piemonte,1,12060
+Guarene,4101,Cuneo,CN,Piemonte,1,12050
+Igliano,4102,Cuneo,CN,Piemonte,1,12060
+Isasca,4103,Cuneo,CN,Piemonte,1,12020
+Lagnasco,4104,Cuneo,CN,Piemonte,1,12030
+La Morra,4105,Cuneo,CN,Piemonte,1,12064
+Lequio Berria,4106,Cuneo,CN,Piemonte,1,12050
+Lequio Tanaro,4107,Cuneo,CN,Piemonte,1,12060
+Lesegno,4108,Cuneo,CN,Piemonte,1,12076
+Levice,4109,Cuneo,CN,Piemonte,1,12070
+Limone Piemonte,4110,Cuneo,CN,Piemonte,1,12015
+Lisio,4111,Cuneo,CN,Piemonte,1,12070
+Macra,4112,Cuneo,CN,Piemonte,1,12020
+Magliano Alfieri,4113,Cuneo,CN,Piemonte,1,12050
+Magliano Alpi,4114,Cuneo,CN,Piemonte,1,12060
+Mango,4115,Cuneo,CN,Piemonte,1,12056
+Manta,4116,Cuneo,CN,Piemonte,1,12030
+Marene,4117,Cuneo,CN,Piemonte,1,12030
+Margarita,4118,Cuneo,CN,Piemonte,1,12040
+Marmora,4119,Cuneo,CN,Piemonte,1,12020
+Marsaglia,4120,Cuneo,CN,Piemonte,1,12060
+Martiniana Po,4121,Cuneo,CN,Piemonte,1,12030
+Melle,4122,Cuneo,CN,Piemonte,1,12020
+Moiola,4123,Cuneo,CN,Piemonte,1,12010
+Mombarcaro,4124,Cuneo,CN,Piemonte,1,12070
+Mombasiglio,4125,Cuneo,CN,Piemonte,1,12070
+Monastero di Vasco,4126,Cuneo,CN,Piemonte,1,12080
+Monasterolo Casotto,4127,Cuneo,CN,Piemonte,1,12080
+Monasterolo di Savigliano,4128,Cuneo,CN,Piemonte,1,12030
+Monchiero,4129,Cuneo,CN,Piemonte,1,12060
+Mondovì,4130,Cuneo,CN,Piemonte,1,12084
+Monesiglio,4131,Cuneo,CN,Piemonte,1,12077
+Monforte d'Alba,4132,Cuneo,CN,Piemonte,1,12065
+Montà,4133,Cuneo,CN,Piemonte,1,12046
+Montaldo di Mondovì,4134,Cuneo,CN,Piemonte,1,12080
+Montaldo Roero,4135,Cuneo,CN,Piemonte,1,12040
+Montanera,4136,Cuneo,CN,Piemonte,1,12040
+Montelupo Albese,4137,Cuneo,CN,Piemonte,1,12050
+Montemale di Cuneo,4138,Cuneo,CN,Piemonte,1,12025
+Monterosso Grana,4139,Cuneo,CN,Piemonte,1,12020
+Monteu Roero,4140,Cuneo,CN,Piemonte,1,12040
+Montezemolo,4141,Cuneo,CN,Piemonte,1,12070
+Monticello d'Alba,4142,Cuneo,CN,Piemonte,1,12066
+Moretta,4143,Cuneo,CN,Piemonte,1,12033
+Morozzo,4144,Cuneo,CN,Piemonte,1,12040
+Murazzano,4145,Cuneo,CN,Piemonte,1,12060
+Murello,4146,Cuneo,CN,Piemonte,1,12030
+Narzole,4147,Cuneo,CN,Piemonte,1,12068
+Neive,4148,Cuneo,CN,Piemonte,1,12052
+Neviglie,4149,Cuneo,CN,Piemonte,1,12050
+Niella Belbo,4150,Cuneo,CN,Piemonte,1,12050
+Niella Tanaro,4151,Cuneo,CN,Piemonte,1,12060
+Novello,4152,Cuneo,CN,Piemonte,1,12060
+Nucetto,4153,Cuneo,CN,Piemonte,1,12070
+Oncino,4154,Cuneo,CN,Piemonte,1,12030
+Ormea,4155,Cuneo,CN,Piemonte,1,12078
+Ostana,4156,Cuneo,CN,Piemonte,1,12030
+Paesana,4157,Cuneo,CN,Piemonte,1,12034
+Pagno,4158,Cuneo,CN,Piemonte,1,12030
+Pamparato,4159,Cuneo,CN,Piemonte,1,12087
+Paroldo,4160,Cuneo,CN,Piemonte,1,12070
+Perletto,4161,Cuneo,CN,Piemonte,1,12070
+Perlo,4162,Cuneo,CN,Piemonte,1,12070
+Peveragno,4163,Cuneo,CN,Piemonte,1,12016
+Pezzolo Valle Uzzone,4164,Cuneo,CN,Piemonte,1,12070
+Pianfei,4165,Cuneo,CN,Piemonte,1,12080
+Piasco,4166,Cuneo,CN,Piemonte,1,12026
+Pietraporzio,4167,Cuneo,CN,Piemonte,1,12010
+Piobesi d'Alba,4168,Cuneo,CN,Piemonte,1,12040
+Piozzo,4169,Cuneo,CN,Piemonte,1,12060
+Pocapaglia,4170,Cuneo,CN,Piemonte,1,12060
+Polonghera,4171,Cuneo,CN,Piemonte,1,12030
+Pontechianale,4172,Cuneo,CN,Piemonte,1,12020
+Pradleves,4173,Cuneo,CN,Piemonte,1,12027
+Prazzo,4174,Cuneo,CN,Piemonte,1,12028
+Priero,4175,Cuneo,CN,Piemonte,1,12070
+Priocca,4176,Cuneo,CN,Piemonte,1,12040
+Priola,4177,Cuneo,CN,Piemonte,1,12070
+Prunetto,4178,Cuneo,CN,Piemonte,1,12077
+Racconigi,4179,Cuneo,CN,Piemonte,1,12035
+Revello,4180,Cuneo,CN,Piemonte,1,12036
+Rifreddo,4181,Cuneo,CN,Piemonte,1,12030
+Rittana,4182,Cuneo,CN,Piemonte,1,12010
+Roaschia,4183,Cuneo,CN,Piemonte,1,12010
+Roascio,4184,Cuneo,CN,Piemonte,1,12073
+Robilante,4185,Cuneo,CN,Piemonte,1,12017
+Roburent,4186,Cuneo,CN,Piemonte,1,12080
+Roccabruna,4187,Cuneo,CN,Piemonte,1,12020
+Rocca Cigliè,4188,Cuneo,CN,Piemonte,1,12060
+Rocca de' Baldi,4189,Cuneo,CN,Piemonte,1,12047
+Roccaforte Mondovì,4190,Cuneo,CN,Piemonte,1,12088
+Roccasparvera,4191,Cuneo,CN,Piemonte,1,12010
+Roccavione,4192,Cuneo,CN,Piemonte,1,12018
+Rocchetta Belbo,4193,Cuneo,CN,Piemonte,1,12050
+Roddi,4194,Cuneo,CN,Piemonte,1,12060
+Roddino,4195,Cuneo,CN,Piemonte,1,12050
+Rodello,4196,Cuneo,CN,Piemonte,1,12050
+Rossana,4197,Cuneo,CN,Piemonte,1,12020
+Ruffia,4198,Cuneo,CN,Piemonte,1,12030
+Sale delle Langhe,4199,Cuneo,CN,Piemonte,1,12070
+Sale San Giovanni,4200,Cuneo,CN,Piemonte,1,12070
+Saliceto,4201,Cuneo,CN,Piemonte,1,12079
+Salmour,4202,Cuneo,CN,Piemonte,1,12040
+Saluzzo,4203,Cuneo,CN,Piemonte,1,12037
+Sambuco,4204,Cuneo,CN,Piemonte,1,12010
+Sampeyre,4205,Cuneo,CN,Piemonte,1,12020
+San Benedetto Belbo,4206,Cuneo,CN,Piemonte,1,12050
+San Damiano Macra,4207,Cuneo,CN,Piemonte,1,12029
+Sanfrè,4208,Cuneo,CN,Piemonte,1,12040
+Sanfront,4209,Cuneo,CN,Piemonte,1,12030
+San Michele Mondovì,4210,Cuneo,CN,Piemonte,1,12080
+Sant'Albano Stura,4211,Cuneo,CN,Piemonte,1,12040
+Santa Vittoria d'Alba,4212,Cuneo,CN,Piemonte,1,12069
+Santo Stefano Belbo,4213,Cuneo,CN,Piemonte,1,12058
+Santo Stefano Roero,4214,Cuneo,CN,Piemonte,1,12040
+Savigliano,4215,Cuneo,CN,Piemonte,1,12038
+Scagnello,4216,Cuneo,CN,Piemonte,1,12070
+Scarnafigi,4217,Cuneo,CN,Piemonte,1,12030
+Serralunga d'Alba,4218,Cuneo,CN,Piemonte,1,12050
+Serravalle Langhe,4219,Cuneo,CN,Piemonte,1,12050
+Sinio,4220,Cuneo,CN,Piemonte,1,12050
+Somano,4221,Cuneo,CN,Piemonte,1,12060
+Sommariva del Bosco,4222,Cuneo,CN,Piemonte,1,12048
+Sommariva Perno,4223,Cuneo,CN,Piemonte,1,12040
+Stroppo,4224,Cuneo,CN,Piemonte,1,12020
+Tarantasca,4225,Cuneo,CN,Piemonte,1,12020
+Torre Bormida,4226,Cuneo,CN,Piemonte,1,12050
+Torre Mondovì,4227,Cuneo,CN,Piemonte,1,12080
+Torre San Giorgio,4228,Cuneo,CN,Piemonte,1,12030
+Torresina,4229,Cuneo,CN,Piemonte,1,12070
+Treiso,4230,Cuneo,CN,Piemonte,1,12050
+Trezzo Tinella,4231,Cuneo,CN,Piemonte,1,12050
+Trinità,4232,Cuneo,CN,Piemonte,1,12049
+Valdieri,4233,Cuneo,CN,Piemonte,1,12010
+Valgrana,4234,Cuneo,CN,Piemonte,1,12020
+Valloriate,4235,Cuneo,CN,Piemonte,1,12010
+Venasca,4237,Cuneo,CN,Piemonte,1,12020
+Verduno,4238,Cuneo,CN,Piemonte,1,12060
+Vernante,4239,Cuneo,CN,Piemonte,1,12019
+Verzuolo,4240,Cuneo,CN,Piemonte,1,12039
+Vezza d'Alba,4241,Cuneo,CN,Piemonte,1,12040
+Vicoforte,4242,Cuneo,CN,Piemonte,1,12080
+Vignolo,4243,Cuneo,CN,Piemonte,1,12010
+Villafalletto,4244,Cuneo,CN,Piemonte,1,12020
+Villanova Mondovì,4245,Cuneo,CN,Piemonte,1,12089
+Villanova Solaro,4246,Cuneo,CN,Piemonte,1,12030
+Villar San Costanzo,4247,Cuneo,CN,Piemonte,1,12020
+Vinadio,4248,Cuneo,CN,Piemonte,1,12010
+Viola,4249,Cuneo,CN,Piemonte,1,12070
+Vottignasco,4250,Cuneo,CN,Piemonte,1,12020
+Agliano Terme,5001,Asti,AT,Piemonte,1,14041
+Albugnano,5002,Asti,AT,Piemonte,1,14022
+Antignano,5003,Asti,AT,Piemonte,1,14010
+Aramengo,5004,Asti,AT,Piemonte,1,14020
+Asti,5005,Asti,AT,Piemonte,1,14100
+Azzano d'Asti,5006,Asti,AT,Piemonte,1,14030
+Baldichieri d'Asti,5007,Asti,AT,Piemonte,1,14011
+Belveglio,5008,Asti,AT,Piemonte,1,14040
+Berzano di San Pietro,5009,Asti,AT,Piemonte,1,14020
+Bruno,5010,Asti,AT,Piemonte,1,14046
+Bubbio,5011,Asti,AT,Piemonte,1,14051
+Buttigliera d'Asti,5012,Asti,AT,Piemonte,1,14021
+Calamandrana,5013,Asti,AT,Piemonte,1,14042
+Calliano,5014,Asti,AT,Piemonte,1,14031
+Calosso,5015,Asti,AT,Piemonte,1,14052
+Camerano Casasco,5016,Asti,AT,Piemonte,1,14020
+Canelli,5017,Asti,AT,Piemonte,1,14053
+Cantarana,5018,Asti,AT,Piemonte,1,14010
+Capriglio,5019,Asti,AT,Piemonte,1,14014
+Casorzo,5020,Asti,AT,Piemonte,1,14032
+Cassinasco,5021,Asti,AT,Piemonte,1,14050
+Castagnole delle Lanze,5022,Asti,AT,Piemonte,1,14054
+Castagnole Monferrato,5023,Asti,AT,Piemonte,1,14030
+Castel Boglione,5024,Asti,AT,Piemonte,1,14040
+Castell'Alfero,5025,Asti,AT,Piemonte,1,14033
+Castellero,5026,Asti,AT,Piemonte,1,14013
+Castelletto Molina,5027,Asti,AT,Piemonte,1,14040
+Castello di Annone,5028,Asti,AT,Piemonte,1,14034
+Castelnuovo Belbo,5029,Asti,AT,Piemonte,1,14043
+Castelnuovo Calcea,5030,Asti,AT,Piemonte,1,14040
+Castelnuovo Don Bosco,5031,Asti,AT,Piemonte,1,14022
+Castel Rocchero,5032,Asti,AT,Piemonte,1,14044
+Cellarengo,5033,Asti,AT,Piemonte,1,14010
+Celle Enomondo,5034,Asti,AT,Piemonte,1,14010
+Cerreto d'Asti,5035,Asti,AT,Piemonte,1,14020
+Cerro Tanaro,5036,Asti,AT,Piemonte,1,14030
+Cessole,5037,Asti,AT,Piemonte,1,14050
+Chiusano d'Asti,5038,Asti,AT,Piemonte,1,14025
+Cinaglio,5039,Asti,AT,Piemonte,1,14020
+Cisterna d'Asti,5040,Asti,AT,Piemonte,1,14010
+Coazzolo,5041,Asti,AT,Piemonte,1,14054
+Cocconato,5042,Asti,AT,Piemonte,1,14023
+Corsione,5044,Asti,AT,Piemonte,1,14020
+Cortandone,5045,Asti,AT,Piemonte,1,14013
+Cortanze,5046,Asti,AT,Piemonte,1,14020
+Cortazzone,5047,Asti,AT,Piemonte,1,14010
+Cortiglione,5048,Asti,AT,Piemonte,1,14040
+Cossombrato,5049,Asti,AT,Piemonte,1,14020
+Costigliole d'Asti,5050,Asti,AT,Piemonte,1,14055
+Cunico,5051,Asti,AT,Piemonte,1,14026
+Dusino San Michele,5052,Asti,AT,Piemonte,1,14010
+Ferrere,5053,Asti,AT,Piemonte,1,14012
+Fontanile,5054,Asti,AT,Piemonte,1,14044
+Frinco,5055,Asti,AT,Piemonte,1,14030
+Grana Monferrato,5056,Asti,AT,Piemonte,1,14031
+Grazzano Badoglio,5057,Asti,AT,Piemonte,1,14035
+Incisa Scapaccino,5058,Asti,AT,Piemonte,1,14045
+Isola d'Asti,5059,Asti,AT,Piemonte,1,14057
+Loazzolo,5060,Asti,AT,Piemonte,1,14051
+Maranzana,5061,Asti,AT,Piemonte,1,14040
+Maretto,5062,Asti,AT,Piemonte,1,14018
+Moasca,5063,Asti,AT,Piemonte,1,14050
+Mombaldone,5064,Asti,AT,Piemonte,1,14050
+Mombaruzzo,5065,Asti,AT,Piemonte,1,14046
+Mombercelli,5066,Asti,AT,Piemonte,1,14047
+Monale,5067,Asti,AT,Piemonte,1,14013
+Monastero Bormida,5068,Asti,AT,Piemonte,1,14058
+Moncalvo,5069,Asti,AT,Piemonte,1,14036
+Moncucco Torinese,5070,Asti,AT,Piemonte,1,14024
+Mongardino,5071,Asti,AT,Piemonte,1,14040
+Montabone,5072,Asti,AT,Piemonte,1,14040
+Montafia,5073,Asti,AT,Piemonte,1,14014
+Montaldo Scarampi,5074,Asti,AT,Piemonte,1,14048
+Montechiaro d'Asti,5075,Asti,AT,Piemonte,1,14025
+Montegrosso d'Asti,5076,Asti,AT,Piemonte,1,14048
+Montemagno,5077,Asti,AT,Piemonte,1,14030
+Moransengo,5079,Asti,AT,Piemonte,1,14023
+Nizza Monferrato,5080,Asti,AT,Piemonte,1,14049
+Olmo Gentile,5081,Asti,AT,Piemonte,1,14050
+Passerano Marmorito,5082,Asti,AT,Piemonte,1,14020
+Penango,5083,Asti,AT,Piemonte,1,14030
+Piea,5084,Asti,AT,Piemonte,1,14020
+Pino d'Asti,5085,Asti,AT,Piemonte,1,14020
+Piovà Massaia,5086,Asti,AT,Piemonte,1,14026
+Portacomaro,5087,Asti,AT,Piemonte,1,14037
+Quaranti,5088,Asti,AT,Piemonte,1,14040
+Refrancore,5089,Asti,AT,Piemonte,1,14030
+Revigliasco d'Asti,5090,Asti,AT,Piemonte,1,14010
+Roatto,5091,Asti,AT,Piemonte,1,14018
+Robella,5092,Asti,AT,Piemonte,1,14020
+Rocca d'Arazzo,5093,Asti,AT,Piemonte,1,14030
+Roccaverano,5094,Asti,AT,Piemonte,1,14050
+Rocchetta Palafea,5095,Asti,AT,Piemonte,1,14042
+Rocchetta Tanaro,5096,Asti,AT,Piemonte,1,14030
+San Damiano d'Asti,5097,Asti,AT,Piemonte,1,14015
+San Giorgio Scarampi,5098,Asti,AT,Piemonte,1,14059
+San Martino Alfieri,5099,Asti,AT,Piemonte,1,14010
+San Marzano Oliveto,5100,Asti,AT,Piemonte,1,14050
+San Paolo Solbrito,5101,Asti,AT,Piemonte,1,14010
+Scurzolengo,5103,Asti,AT,Piemonte,1,14030
+Serole,5104,Asti,AT,Piemonte,1,14050
+Sessame,5105,Asti,AT,Piemonte,1,14058
+Settime,5106,Asti,AT,Piemonte,1,14020
+Soglio,5107,Asti,AT,Piemonte,1,14020
+Tigliole,5108,Asti,AT,Piemonte,1,14016
+Tonco,5109,Asti,AT,Piemonte,1,14039
+Tonengo,5110,Asti,AT,Piemonte,1,14023
+Vaglio Serra,5111,Asti,AT,Piemonte,1,14049
+Valfenera,5112,Asti,AT,Piemonte,1,14017
+Vesime,5113,Asti,AT,Piemonte,1,14059
+Viale,5114,Asti,AT,Piemonte,1,14010
+Viarigi,5115,Asti,AT,Piemonte,1,14030
+Vigliano d'Asti,5116,Asti,AT,Piemonte,1,14040
+Villafranca d'Asti,5117,Asti,AT,Piemonte,1,14018
+Villanova d'Asti,5118,Asti,AT,Piemonte,1,14019
+Villa San Secondo,5119,Asti,AT,Piemonte,1,14020
+Vinchio,5120,Asti,AT,Piemonte,1,14040
+Montiglio Monferrato,5121,Asti,AT,Piemonte,1,14026
+Acqui Terme,6001,Alessandria,AL,Piemonte,1,15011
+Albera Ligure,6002,Alessandria,AL,Piemonte,1,15060
+Alessandria,6003,Alessandria,AL,Piemonte,1,15121
+Alfiano Natta,6004,Alessandria,AL,Piemonte,1,15021
+Alice Bel Colle,6005,Alessandria,AL,Piemonte,1,15010
+Altavilla Monferrato,6007,Alessandria,AL,Piemonte,1,15041
+Alzano Scrivia,6008,Alessandria,AL,Piemonte,1,15050
+Arquata Scrivia,6009,Alessandria,AL,Piemonte,1,15061
+Avolasca,6010,Alessandria,AL,Piemonte,1,15050
+Balzola,6011,Alessandria,AL,Piemonte,1,15031
+Basaluzzo,6012,Alessandria,AL,Piemonte,1,15060
+Bassignana,6013,Alessandria,AL,Piemonte,1,15042
+Belforte Monferrato,6014,Alessandria,AL,Piemonte,1,15070
+Bergamasco,6015,Alessandria,AL,Piemonte,1,15022
+Berzano di Tortona,6016,Alessandria,AL,Piemonte,1,15050
+Bistagno,6017,Alessandria,AL,Piemonte,1,15012
+Borghetto di Borbera,6018,Alessandria,AL,Piemonte,1,15060
+Borgoratto Alessandrino,6019,Alessandria,AL,Piemonte,1,15013
+Borgo San Martino,6020,Alessandria,AL,Piemonte,1,15032
+Bosco Marengo,6021,Alessandria,AL,Piemonte,1,15062
+Bosio,6022,Alessandria,AL,Piemonte,1,15060
+Bozzole,6023,Alessandria,AL,Piemonte,1,15040
+Brignano-Frascata,6024,Alessandria,AL,Piemonte,1,15050
+Cabella Ligure,6025,Alessandria,AL,Piemonte,1,15060
+Camagna Monferrato,6026,Alessandria,AL,Piemonte,1,15030
+Camino,6027,Alessandria,AL,Piemonte,1,15020
+Cantalupo Ligure,6028,Alessandria,AL,Piemonte,1,15060
+Capriata d'Orba,6029,Alessandria,AL,Piemonte,1,15060
+Carbonara Scrivia,6030,Alessandria,AL,Piemonte,1,15050
+Carentino,6031,Alessandria,AL,Piemonte,1,15026
+Carezzano,6032,Alessandria,AL,Piemonte,1,15051
+Carpeneto,6033,Alessandria,AL,Piemonte,1,15071
+Carrega Ligure,6034,Alessandria,AL,Piemonte,1,15060
+Carrosio,6035,Alessandria,AL,Piemonte,1,15060
+Cartosio,6036,Alessandria,AL,Piemonte,1,15015
+Casal Cermelli,6037,Alessandria,AL,Piemonte,1,15072
+Casaleggio Boiro,6038,Alessandria,AL,Piemonte,1,15070
+Casale Monferrato,6039,Alessandria,AL,Piemonte,1,15033
+Casalnoceto,6040,Alessandria,AL,Piemonte,1,15052
+Casasco,6041,Alessandria,AL,Piemonte,1,15050
+Cassine,6043,Alessandria,AL,Piemonte,1,15016
+Cassinelle,6044,Alessandria,AL,Piemonte,1,15070
+Castellania Coppi,6045,Alessandria,AL,Piemonte,1,15051
+Castellar Guidobono,6046,Alessandria,AL,Piemonte,1,15050
+Castellazzo Bormida,6047,Alessandria,AL,Piemonte,1,15073
+Castelletto d'Erro,6048,Alessandria,AL,Piemonte,1,15010
+Castelletto d'Orba,6049,Alessandria,AL,Piemonte,1,15060
+Castelletto Merli,6050,Alessandria,AL,Piemonte,1,15020
+Castelletto Monferrato,6051,Alessandria,AL,Piemonte,1,15040
+Castelnuovo Bormida,6052,Alessandria,AL,Piemonte,1,15017
+Castelnuovo Scrivia,6053,Alessandria,AL,Piemonte,1,15053
+Castelspina,6054,Alessandria,AL,Piemonte,1,15070
+Cavatore,6055,Alessandria,AL,Piemonte,1,15010
+Cella Monte,6056,Alessandria,AL,Piemonte,1,15034
+Cereseto,6057,Alessandria,AL,Piemonte,1,15020
+Cerreto Grue,6058,Alessandria,AL,Piemonte,1,15050
+Cerrina Monferrato,6059,Alessandria,AL,Piemonte,1,15020
+Coniolo,6060,Alessandria,AL,Piemonte,1,15030
+Conzano,6061,Alessandria,AL,Piemonte,1,15030
+Costa Vescovato,6062,Alessandria,AL,Piemonte,1,15050
+Cremolino,6063,Alessandria,AL,Piemonte,1,15010
+Denice,6065,Alessandria,AL,Piemonte,1,15010
+Dernice,6066,Alessandria,AL,Piemonte,1,15056
+Fabbrica Curone,6067,Alessandria,AL,Piemonte,1,15054
+Felizzano,6068,Alessandria,AL,Piemonte,1,15023
+Fraconalto,6069,Alessandria,AL,Piemonte,1,15060
+Francavilla Bisio,6070,Alessandria,AL,Piemonte,1,15060
+Frascaro,6071,Alessandria,AL,Piemonte,1,15010
+Frassinello Monferrato,6072,Alessandria,AL,Piemonte,1,15035
+Frassineto Po,6073,Alessandria,AL,Piemonte,1,15040
+Fresonara,6074,Alessandria,AL,Piemonte,1,15064
+Frugarolo,6075,Alessandria,AL,Piemonte,1,15065
+Fubine Monferrato,6076,Alessandria,AL,Piemonte,1,15043
+Gabiano,6077,Alessandria,AL,Piemonte,1,15020
+Gamalero,6078,Alessandria,AL,Piemonte,1,15010
+Garbagna,6079,Alessandria,AL,Piemonte,1,15050
+Gavi,6081,Alessandria,AL,Piemonte,1,15066
+Giarole,6082,Alessandria,AL,Piemonte,1,15036
+Gremiasco,6083,Alessandria,AL,Piemonte,1,15056
+Grognardo,6084,Alessandria,AL,Piemonte,1,15010
+Grondona,6085,Alessandria,AL,Piemonte,1,15060
+Guazzora,6086,Alessandria,AL,Piemonte,1,15050
+Isola Sant'Antonio,6087,Alessandria,AL,Piemonte,1,15050
+Lerma,6088,Alessandria,AL,Piemonte,1,15070
+Malvicino,6090,Alessandria,AL,Piemonte,1,15015
+Masio,6091,Alessandria,AL,Piemonte,1,15024
+Melazzo,6092,Alessandria,AL,Piemonte,1,15010
+Merana,6093,Alessandria,AL,Piemonte,1,15010
+Mirabello Monferrato,6094,Alessandria,AL,Piemonte,1,15040
+Molare,6095,Alessandria,AL,Piemonte,1,15074
+Molino dei Torti,6096,Alessandria,AL,Piemonte,1,15050
+Mombello Monferrato,6097,Alessandria,AL,Piemonte,1,15020
+Momperone,6098,Alessandria,AL,Piemonte,1,15050
+Moncestino,6099,Alessandria,AL,Piemonte,1,15020
+Mongiardino Ligure,6100,Alessandria,AL,Piemonte,1,15060
+Monleale,6101,Alessandria,AL,Piemonte,1,15059
+Montacuto,6102,Alessandria,AL,Piemonte,1,15050
+Montaldeo,6103,Alessandria,AL,Piemonte,1,15060
+Montaldo Bormida,6104,Alessandria,AL,Piemonte,1,15010
+Montecastello,6105,Alessandria,AL,Piemonte,1,15040
+Montechiaro d'Acqui,6106,Alessandria,AL,Piemonte,1,15010
+Montegioco,6107,Alessandria,AL,Piemonte,1,15050
+Montemarzino,6108,Alessandria,AL,Piemonte,1,15050
+Morano sul Po,6109,Alessandria,AL,Piemonte,1,15025
+Morbello,6110,Alessandria,AL,Piemonte,1,15010
+Mornese,6111,Alessandria,AL,Piemonte,1,15075
+Morsasco,6112,Alessandria,AL,Piemonte,1,15010
+Murisengo,6113,Alessandria,AL,Piemonte,1,15020
+Novi Ligure,6114,Alessandria,AL,Piemonte,1,15067
+Occimiano,6115,Alessandria,AL,Piemonte,1,15040
+Odalengo Grande,6116,Alessandria,AL,Piemonte,1,15020
+Odalengo Piccolo,6117,Alessandria,AL,Piemonte,1,15020
+Olivola,6118,Alessandria,AL,Piemonte,1,15030
+Orsara Bormida,6119,Alessandria,AL,Piemonte,1,15010
+Ottiglio,6120,Alessandria,AL,Piemonte,1,15038
+Ovada,6121,Alessandria,AL,Piemonte,1,15076
+Oviglio,6122,Alessandria,AL,Piemonte,1,15026
+Ozzano Monferrato,6123,Alessandria,AL,Piemonte,1,15039
+Paderna,6124,Alessandria,AL,Piemonte,1,15050
+Pareto,6125,Alessandria,AL,Piemonte,1,15010
+Parodi Ligure,6126,Alessandria,AL,Piemonte,1,15060
+Pasturana,6127,Alessandria,AL,Piemonte,1,15060
+Pecetto di Valenza,6128,Alessandria,AL,Piemonte,1,15040
+Pietra Marazzi,6129,Alessandria,AL,Piemonte,1,15040
+Pomaro Monferrato,6131,Alessandria,AL,Piemonte,1,15040
+Pontecurone,6132,Alessandria,AL,Piemonte,1,15055
+Pontestura,6133,Alessandria,AL,Piemonte,1,15027
+Ponti,6134,Alessandria,AL,Piemonte,1,15010
+Ponzano Monferrato,6135,Alessandria,AL,Piemonte,1,15020
+Ponzone,6136,Alessandria,AL,Piemonte,1,15010
+Pozzol Groppo,6137,Alessandria,AL,Piemonte,1,15050
+Pozzolo Formigaro,6138,Alessandria,AL,Piemonte,1,15068
+Prasco,6139,Alessandria,AL,Piemonte,1,15010
+Predosa,6140,Alessandria,AL,Piemonte,1,15077
+Quargnento,6141,Alessandria,AL,Piemonte,1,15044
+Quattordio,6142,Alessandria,AL,Piemonte,1,15028
+Ricaldone,6143,Alessandria,AL,Piemonte,1,15010
+Rivalta Bormida,6144,Alessandria,AL,Piemonte,1,15010
+Rivarone,6145,Alessandria,AL,Piemonte,1,15040
+Roccaforte Ligure,6146,Alessandria,AL,Piemonte,1,15060
+Rocca Grimalda,6147,Alessandria,AL,Piemonte,1,15078
+Rocchetta Ligure,6148,Alessandria,AL,Piemonte,1,15060
+Rosignano Monferrato,6149,Alessandria,AL,Piemonte,1,15030
+Sala Monferrato,6150,Alessandria,AL,Piemonte,1,15030
+Sale,6151,Alessandria,AL,Piemonte,1,15045
+San Cristoforo,6152,Alessandria,AL,Piemonte,1,15060
+San Giorgio Monferrato,6153,Alessandria,AL,Piemonte,1,15020
+San Salvatore Monferrato,6154,Alessandria,AL,Piemonte,1,15046
+San Sebastiano Curone,6155,Alessandria,AL,Piemonte,1,15056
+Sant'Agata Fossili,6156,Alessandria,AL,Piemonte,1,15050
+Sardigliano,6157,Alessandria,AL,Piemonte,1,15060
+Sarezzano,6158,Alessandria,AL,Piemonte,1,15050
+Serralunga di Crea,6159,Alessandria,AL,Piemonte,1,15020
+Serravalle Scrivia,6160,Alessandria,AL,Piemonte,1,15069
+Sezzadio,6161,Alessandria,AL,Piemonte,1,15079
+Silvano d'Orba,6162,Alessandria,AL,Piemonte,1,15060
+Solero,6163,Alessandria,AL,Piemonte,1,15029
+Solonghello,6164,Alessandria,AL,Piemonte,1,15020
+Spigno Monferrato,6165,Alessandria,AL,Piemonte,1,15018
+Spineto Scrivia,6166,Alessandria,AL,Piemonte,1,15050
+Stazzano,6167,Alessandria,AL,Piemonte,1,15060
+Strevi,6168,Alessandria,AL,Piemonte,1,15019
+Tagliolo Monferrato,6169,Alessandria,AL,Piemonte,1,15070
+Tassarolo,6170,Alessandria,AL,Piemonte,1,15060
+Terruggia,6171,Alessandria,AL,Piemonte,1,15030
+Terzo,6172,Alessandria,AL,Piemonte,1,15010
+Ticineto,6173,Alessandria,AL,Piemonte,1,15040
+Tortona,6174,Alessandria,AL,Piemonte,1,15057
+Treville,6175,Alessandria,AL,Piemonte,1,15030
+Trisobbio,6176,Alessandria,AL,Piemonte,1,15070
+Valenza,6177,Alessandria,AL,Piemonte,1,15048
+Valmacca,6178,Alessandria,AL,Piemonte,1,15040
+Vignale Monferrato,6179,Alessandria,AL,Piemonte,1,15049
+Vignole Borbera,6180,Alessandria,AL,Piemonte,1,15060
+Viguzzolo,6181,Alessandria,AL,Piemonte,1,15058
+Villadeati,6182,Alessandria,AL,Piemonte,1,15020
+Villalvernia,6183,Alessandria,AL,Piemonte,1,15050
+Villamiroglio,6184,Alessandria,AL,Piemonte,1,15020
+Villanova Monferrato,6185,Alessandria,AL,Piemonte,1,15030
+Villaromagnano,6186,Alessandria,AL,Piemonte,1,15050
+Visone,6187,Alessandria,AL,Piemonte,1,15010
+Volpedo,6188,Alessandria,AL,Piemonte,1,15059
+Volpeglino,6189,Alessandria,AL,Piemonte,1,15050
+Voltaggio,6190,Alessandria,AL,Piemonte,1,15060
+Cassano Spinola,6191,Alessandria,AL,Piemonte,1,15063
+Alluvioni Piovera,6192,Alessandria,AL,Piemonte,1,15047
+Lu e Cuccaro Monferrato,6193,Alessandria,AL,Piemonte,1,15040
+Ailoche,96001,Biella,BI,Piemonte,1,13861
+Andorno Micca,96002,Biella,BI,Piemonte,1,13811
+Benna,96003,Biella,BI,Piemonte,1,13871
+Biella,96004,Biella,BI,Piemonte,1,13900
+Bioglio,96005,Biella,BI,Piemonte,1,13841
+Borriana,96006,Biella,BI,Piemonte,1,13872
+Brusnengo,96007,Biella,BI,Piemonte,1,13862
+Callabiana,96008,Biella,BI,Piemonte,1,13821
+Camandona,96009,Biella,BI,Piemonte,1,13821
+Camburzano,96010,Biella,BI,Piemonte,1,13891
+Candelo,96012,Biella,BI,Piemonte,1,13878
+Caprile,96013,Biella,BI,Piemonte,1,13864
+Casapinta,96014,Biella,BI,Piemonte,1,13866
+Castelletto Cervo,96015,Biella,BI,Piemonte,1,13851
+Cavaglià,96016,Biella,BI,Piemonte,1,13881
+Cerrione,96018,Biella,BI,Piemonte,1,13882
+Coggiola,96019,Biella,BI,Piemonte,1,13863
+Cossato,96020,Biella,BI,Piemonte,1,13836
+Crevacuore,96021,Biella,BI,Piemonte,1,13864
+Curino,96023,Biella,BI,Piemonte,1,13865
+Donato,96024,Biella,BI,Piemonte,1,13893
+Dorzano,96025,Biella,BI,Piemonte,1,13881
+Gaglianico,96026,Biella,BI,Piemonte,1,13894
+Gifflenga,96027,Biella,BI,Piemonte,1,13874
+Graglia,96028,Biella,BI,Piemonte,1,13895
+Magnano,96030,Biella,BI,Piemonte,1,13887
+Massazza,96031,Biella,BI,Piemonte,1,13873
+Masserano,96032,Biella,BI,Piemonte,1,13866
+Mezzana Mortigliengo,96033,Biella,BI,Piemonte,1,13831
+Miagliano,96034,Biella,BI,Piemonte,1,13816
+Mongrando,96035,Biella,BI,Piemonte,1,13888
+Mottalciata,96037,Biella,BI,Piemonte,1,13874
+Muzzano,96038,Biella,BI,Piemonte,1,13895
+Netro,96039,Biella,BI,Piemonte,1,13896
+Occhieppo Inferiore,96040,Biella,BI,Piemonte,1,13897
+Occhieppo Superiore,96041,Biella,BI,Piemonte,1,13898
+Pettinengo,96042,Biella,BI,Piemonte,1,13843
+Piatto,96043,Biella,BI,Piemonte,1,13844
+Piedicavallo,96044,Biella,BI,Piemonte,1,13812
+Pollone,96046,Biella,BI,Piemonte,1,13814
+Ponderano,96047,Biella,BI,Piemonte,1,13875
+Portula,96048,Biella,BI,Piemonte,1,13833
+Pralungo,96049,Biella,BI,Piemonte,1,13899
+Pray,96050,Biella,BI,Piemonte,1,13867
+Ronco Biellese,96053,Biella,BI,Piemonte,1,13845
+Roppolo,96054,Biella,BI,Piemonte,1,13883
+Rosazza,96055,Biella,BI,Piemonte,1,13815
+Sagliano Micca,96056,Biella,BI,Piemonte,1,13816
+Sala Biellese,96057,Biella,BI,Piemonte,1,13884
+Salussola,96058,Biella,BI,Piemonte,1,13885
+Sandigliano,96059,Biella,BI,Piemonte,1,13876
+Sordevolo,96063,Biella,BI,Piemonte,1,13817
+Sostegno,96064,Biella,BI,Piemonte,1,13868
+Strona,96065,Biella,BI,Piemonte,1,13823
+Tavigliano,96066,Biella,BI,Piemonte,1,13811
+Ternengo,96067,Biella,BI,Piemonte,1,13844
+Tollegno,96068,Biella,BI,Piemonte,1,13818
+Torrazzo,96069,Biella,BI,Piemonte,1,13884
+Valdengo,96071,Biella,BI,Piemonte,1,13855
+Vallanzengo,96072,Biella,BI,Piemonte,1,13847
+Valle San Nicolao,96074,Biella,BI,Piemonte,1,13847
+Veglio,96075,Biella,BI,Piemonte,1,13824
+Verrone,96076,Biella,BI,Piemonte,1,13871
+Vigliano Biellese,96077,Biella,BI,Piemonte,1,13856
+Villa del Bosco,96078,Biella,BI,Piemonte,1,13868
+Villanova Biellese,96079,Biella,BI,Piemonte,1,13877
+Viverone,96080,Biella,BI,Piemonte,1,13886
+Zimone,96081,Biella,BI,Piemonte,1,13887
+Zubiena,96082,Biella,BI,Piemonte,1,13888
+Zumaglia,96083,Biella,BI,Piemonte,1,13848
+Lessona,96085,Biella,BI,Piemonte,1,13853
+Campiglia Cervo,96086,Biella,BI,Piemonte,1,13812
+Quaregna Cerreto,96087,Biella,BI,Piemonte,1,13854
+Valdilana,96088,Biella,BI,Piemonte,1,13835
+Antrona Schieranco,103001,Verbano-Cusio-Ossola,VB,Piemonte,1,28841
+Anzola d'Ossola,103002,Verbano-Cusio-Ossola,VB,Piemonte,1,28877
+Arizzano,103003,Verbano-Cusio-Ossola,VB,Piemonte,1,28811
+Arola,103004,Verbano-Cusio-Ossola,VB,Piemonte,1,28899
+Aurano,103005,Verbano-Cusio-Ossola,VB,Piemonte,1,28812
+Baceno,103006,Verbano-Cusio-Ossola,VB,Piemonte,1,28861
+Bannio Anzino,103007,Verbano-Cusio-Ossola,VB,Piemonte,1,28871
+Baveno,103008,Verbano-Cusio-Ossola,VB,Piemonte,1,28831
+Bee,103009,Verbano-Cusio-Ossola,VB,Piemonte,1,28813
+Belgirate,103010,Verbano-Cusio-Ossola,VB,Piemonte,1,28832
+Beura-Cardezza,103011,Verbano-Cusio-Ossola,VB,Piemonte,1,28851
+Bognanco,103012,Verbano-Cusio-Ossola,VB,Piemonte,1,28842
+Brovello-Carpugnino,103013,Verbano-Cusio-Ossola,VB,Piemonte,1,28833
+Calasca-Castiglione,103014,Verbano-Cusio-Ossola,VB,Piemonte,1,28873
+Cambiasca,103015,Verbano-Cusio-Ossola,VB,Piemonte,1,28814
+Cannero Riviera,103016,Verbano-Cusio-Ossola,VB,Piemonte,1,28821
+Cannobio,103017,Verbano-Cusio-Ossola,VB,Piemonte,1,28822
+Caprezzo,103018,Verbano-Cusio-Ossola,VB,Piemonte,1,28815
+Casale Corte Cerro,103019,Verbano-Cusio-Ossola,VB,Piemonte,1,28881
+Ceppo Morelli,103021,Verbano-Cusio-Ossola,VB,Piemonte,1,28875
+Cesara,103022,Verbano-Cusio-Ossola,VB,Piemonte,1,28891
+Cossogno,103023,Verbano-Cusio-Ossola,VB,Piemonte,1,28801
+Craveggia,103024,Verbano-Cusio-Ossola,VB,Piemonte,1,28852
+Crevoladossola,103025,Verbano-Cusio-Ossola,VB,Piemonte,1,28865
+Crodo,103026,Verbano-Cusio-Ossola,VB,Piemonte,1,28862
+Domodossola,103028,Verbano-Cusio-Ossola,VB,Piemonte,1,28845
+Druogno,103029,Verbano-Cusio-Ossola,VB,Piemonte,1,28853
+Formazza,103031,Verbano-Cusio-Ossola,VB,Piemonte,1,28863
+Germagno,103032,Verbano-Cusio-Ossola,VB,Piemonte,1,28887
+Ghiffa,103033,Verbano-Cusio-Ossola,VB,Piemonte,1,28823
+Gignese,103034,Verbano-Cusio-Ossola,VB,Piemonte,1,28836
+Gravellona Toce,103035,Verbano-Cusio-Ossola,VB,Piemonte,1,28883
+Gurro,103036,Verbano-Cusio-Ossola,VB,Piemonte,1,28828
+Intragna,103037,Verbano-Cusio-Ossola,VB,Piemonte,1,28816
+Loreglia,103038,Verbano-Cusio-Ossola,VB,Piemonte,1,28893
+Macugnaga,103039,Verbano-Cusio-Ossola,VB,Piemonte,1,28876
+Madonna del Sasso,103040,Verbano-Cusio-Ossola,VB,Piemonte,1,28894
+Malesco,103041,Verbano-Cusio-Ossola,VB,Piemonte,1,28854
+Masera,103042,Verbano-Cusio-Ossola,VB,Piemonte,1,28855
+Massiola,103043,Verbano-Cusio-Ossola,VB,Piemonte,1,28895
+Mergozzo,103044,Verbano-Cusio-Ossola,VB,Piemonte,1,28802
+Miazzina,103045,Verbano-Cusio-Ossola,VB,Piemonte,1,28817
+Montecrestese,103046,Verbano-Cusio-Ossola,VB,Piemonte,1,28864
+Montescheno,103047,Verbano-Cusio-Ossola,VB,Piemonte,1,28843
+Nonio,103048,Verbano-Cusio-Ossola,VB,Piemonte,1,28891
+Oggebbio,103049,Verbano-Cusio-Ossola,VB,Piemonte,1,28824
+Omegna,103050,Verbano-Cusio-Ossola,VB,Piemonte,1,28887
+Ornavasso,103051,Verbano-Cusio-Ossola,VB,Piemonte,1,28877
+Pallanzeno,103052,Verbano-Cusio-Ossola,VB,Piemonte,1,28884
+Piedimulera,103053,Verbano-Cusio-Ossola,VB,Piemonte,1,28885
+Pieve Vergonte,103054,Verbano-Cusio-Ossola,VB,Piemonte,1,28886
+Premeno,103055,Verbano-Cusio-Ossola,VB,Piemonte,1,28818
+Premia,103056,Verbano-Cusio-Ossola,VB,Piemonte,1,28866
+Premosello-Chiovenda,103057,Verbano-Cusio-Ossola,VB,Piemonte,1,28803
+Quarna Sopra,103058,Verbano-Cusio-Ossola,VB,Piemonte,1,28898
+Quarna Sotto,103059,Verbano-Cusio-Ossola,VB,Piemonte,1,28896
+Re,103060,Verbano-Cusio-Ossola,VB,Piemonte,1,28856
+San Bernardino Verbano,103061,Verbano-Cusio-Ossola,VB,Piemonte,1,28804
+Santa Maria Maggiore,103062,Verbano-Cusio-Ossola,VB,Piemonte,1,28857
+Stresa,103064,Verbano-Cusio-Ossola,VB,Piemonte,1,28838
+Toceno,103065,Verbano-Cusio-Ossola,VB,Piemonte,1,28858
+Trarego Viggiona,103066,Verbano-Cusio-Ossola,VB,Piemonte,1,28826
+Trasquera,103067,Verbano-Cusio-Ossola,VB,Piemonte,1,28868
+Trontano,103068,Verbano-Cusio-Ossola,VB,Piemonte,1,28859
+Valstrona,103069,Verbano-Cusio-Ossola,VB,Piemonte,1,28897
+Vanzone con San Carlo,103070,Verbano-Cusio-Ossola,VB,Piemonte,1,28879
+Varzo,103071,Verbano-Cusio-Ossola,VB,Piemonte,1,28868
+Verbania,103072,Verbano-Cusio-Ossola,VB,Piemonte,1,28922
+Vignone,103074,Verbano-Cusio-Ossola,VB,Piemonte,1,28819
+Villadossola,103075,Verbano-Cusio-Ossola,VB,Piemonte,1,28844
+Villette,103076,Verbano-Cusio-Ossola,VB,Piemonte,1,28856
+Vogogna,103077,Verbano-Cusio-Ossola,VB,Piemonte,1,28805
+Borgomezzavalle,103078,Verbano-Cusio-Ossola,VB,Piemonte,1,28841
+Valle Cannobina,103079,Verbano-Cusio-Ossola,VB,Piemonte,1,28825
+Allein,7001,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Antey-Saint-André,7002,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Aosta,7003,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11100
+Arnad,7004,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Arvier,7005,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11011
+Avise,7006,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Ayas,7007,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Aymavilles,7008,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Bard,7009,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Bionaz,7010,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Brissogne,7011,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Brusson,7012,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11022
+Challand-Saint-Anselme,7013,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Challand-Saint-Victor,7014,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Chambave,7015,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11023
+Chamois,7016,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Champdepraz,7017,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Champorcher,7018,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Charvensod,7019,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Châtillon,7020,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11024
+Cogne,7021,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11012
+Courmayeur,7022,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11013
+Donnas,7023,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Doues,7024,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Emarèse,7025,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Etroubles,7026,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11014
+Fénis,7027,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Fontainemore,7028,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Gaby,7029,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Gignod,7030,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Gressan,7031,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Gressoney-La-Trinité,7032,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Gressoney-Saint-Jean,7033,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11025
+Hône,7034,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Introd,7035,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Issime,7036,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Issogne,7037,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Jovençan,7038,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+La Magdeleine,7039,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+La Salle,7040,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11015
+La Thuile,7041,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11016
+Lillianes,7042,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Montjovet,7043,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Morgex,7044,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11017
+Nus,7045,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Ollomont,7046,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Oyace,7047,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Perloz,7048,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Pollein,7049,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Pontboset,7050,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Pontey,7051,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11024
+Pont-Saint-Martin,7052,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11026
+Pré-Saint-Didier,7053,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Quart,7054,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Rhêmes-Notre-Dame,7055,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Rhêmes-Saint-Georges,7056,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Roisan,7057,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Saint-Christophe,7058,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Saint-Denis,7059,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11023
+Saint-Marcel,7060,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Saint-Nicolas,7061,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Saint-Oyen,7062,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11014
+Saint-Pierre,7063,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Saint-Rhémy-en-Bosses,7064,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Saint-Vincent,7065,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11027
+Sarre,7066,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Torgnon,7067,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Valgrisenche,7068,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Valpelline,7069,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Valsavarenche,7070,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11010
+Valtournenche,7071,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11028
+Verrayes,7072,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11020
+Verrès,7073,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11029
+Villeneuve,7074,Valle d'Aosta/Vallée d'Aoste,AO,Valle d'Aosta/Vallée d'Aoste,2,11018
+Agra,12001,Varese,VA,Lombardia,3,21010
+Albizzate,12002,Varese,VA,Lombardia,3,21041
+Angera,12003,Varese,VA,Lombardia,3,21021
+Arcisate,12004,Varese,VA,Lombardia,3,21051
+Arsago Seprio,12005,Varese,VA,Lombardia,3,21010
+Azzate,12006,Varese,VA,Lombardia,3,21022
+Azzio,12007,Varese,VA,Lombardia,3,21030
+Barasso,12008,Varese,VA,Lombardia,3,21020
+Bardello,12009,Varese,VA,Lombardia,3,21020
+Bedero Valcuvia,12010,Varese,VA,Lombardia,3,21039
+Besano,12011,Varese,VA,Lombardia,3,21050
+Besnate,12012,Varese,VA,Lombardia,3,21010
+Besozzo,12013,Varese,VA,Lombardia,3,21023
+Biandronno,12014,Varese,VA,Lombardia,3,21024
+Bisuschio,12015,Varese,VA,Lombardia,3,21050
+Bodio Lomnago,12016,Varese,VA,Lombardia,3,21020
+Brebbia,12017,Varese,VA,Lombardia,3,21020
+Bregano,12018,Varese,VA,Lombardia,3,21020
+Brenta,12019,Varese,VA,Lombardia,3,21030
+Brezzo di Bedero,12020,Varese,VA,Lombardia,3,21010
+Brinzio,12021,Varese,VA,Lombardia,3,21030
+Brissago-Valtravaglia,12022,Varese,VA,Lombardia,3,21030
+Brunello,12023,Varese,VA,Lombardia,3,21020
+Brusimpiano,12024,Varese,VA,Lombardia,3,21050
+Buguggiate,12025,Varese,VA,Lombardia,3,21020
+Busto Arsizio,12026,Varese,VA,Lombardia,3,21052
+Cadegliano-Viconago,12027,Varese,VA,Lombardia,3,21031
+Cairate,12029,Varese,VA,Lombardia,3,21050
+Cantello,12030,Varese,VA,Lombardia,3,21050
+Caravate,12031,Varese,VA,Lombardia,3,21032
+Cardano al Campo,12032,Varese,VA,Lombardia,3,21010
+Carnago,12033,Varese,VA,Lombardia,3,21040
+Caronno Pertusella,12034,Varese,VA,Lombardia,3,21042
+Caronno Varesino,12035,Varese,VA,Lombardia,3,21040
+Casale Litta,12036,Varese,VA,Lombardia,3,21020
+Casalzuigno,12037,Varese,VA,Lombardia,3,21030
+Casciago,12038,Varese,VA,Lombardia,3,21020
+Casorate Sempione,12039,Varese,VA,Lombardia,3,21011
+Cassano Magnago,12040,Varese,VA,Lombardia,3,21012
+Cassano Valcuvia,12041,Varese,VA,Lombardia,3,21030
+Castellanza,12042,Varese,VA,Lombardia,3,21053
+Castello Cabiaglio,12043,Varese,VA,Lombardia,3,21030
+Castelseprio,12044,Varese,VA,Lombardia,3,21050
+Castelveccana,12045,Varese,VA,Lombardia,3,21010
+Castiglione Olona,12046,Varese,VA,Lombardia,3,21043
+Castronno,12047,Varese,VA,Lombardia,3,21040
+Cavaria con Premezzo,12048,Varese,VA,Lombardia,3,21044
+Cazzago Brabbia,12049,Varese,VA,Lombardia,3,21020
+Cislago,12050,Varese,VA,Lombardia,3,21040
+Cittiglio,12051,Varese,VA,Lombardia,3,21033
+Clivio,12052,Varese,VA,Lombardia,3,21050
+Cocquio-Trevisago,12053,Varese,VA,Lombardia,3,21034
+Comabbio,12054,Varese,VA,Lombardia,3,21020
+Comerio,12055,Varese,VA,Lombardia,3,21025
+Cremenaga,12056,Varese,VA,Lombardia,3,21030
+Crosio della Valle,12057,Varese,VA,Lombardia,3,21020
+Cuasso al Monte,12058,Varese,VA,Lombardia,3,21050
+Cugliate-Fabiasco,12059,Varese,VA,Lombardia,3,21030
+Cunardo,12060,Varese,VA,Lombardia,3,21035
+Curiglia con Monteviasco,12061,Varese,VA,Lombardia,3,21010
+Cuveglio,12062,Varese,VA,Lombardia,3,21030
+Cuvio,12063,Varese,VA,Lombardia,3,21030
+Daverio,12064,Varese,VA,Lombardia,3,21020
+Dumenza,12065,Varese,VA,Lombardia,3,21010
+Duno,12066,Varese,VA,Lombardia,3,21030
+Fagnano Olona,12067,Varese,VA,Lombardia,3,21054
+Ferno,12068,Varese,VA,Lombardia,3,21010
+Ferrera di Varese,12069,Varese,VA,Lombardia,3,21030
+Gallarate,12070,Varese,VA,Lombardia,3,21013
+Galliate Lombardo,12071,Varese,VA,Lombardia,3,21020
+Gavirate,12072,Varese,VA,Lombardia,3,21026
+Gazzada Schianno,12073,Varese,VA,Lombardia,3,21045
+Gemonio,12074,Varese,VA,Lombardia,3,21036
+Gerenzano,12075,Varese,VA,Lombardia,3,21040
+Germignaga,12076,Varese,VA,Lombardia,3,21010
+Golasecca,12077,Varese,VA,Lombardia,3,21010
+Gorla Maggiore,12078,Varese,VA,Lombardia,3,21050
+Gorla Minore,12079,Varese,VA,Lombardia,3,21055
+Gornate Olona,12080,Varese,VA,Lombardia,3,21040
+Grantola,12081,Varese,VA,Lombardia,3,21030
+Inarzo,12082,Varese,VA,Lombardia,3,21020
+Induno Olona,12083,Varese,VA,Lombardia,3,21056
+Ispra,12084,Varese,VA,Lombardia,3,21027
+Jerago con Orago,12085,Varese,VA,Lombardia,3,21040
+Lavena Ponte Tresa,12086,Varese,VA,Lombardia,3,21037
+Laveno-Mombello,12087,Varese,VA,Lombardia,3,21014
+Leggiuno,12088,Varese,VA,Lombardia,3,21038
+Lonate Ceppino,12089,Varese,VA,Lombardia,3,21050
+Lonate Pozzolo,12090,Varese,VA,Lombardia,3,21015
+Lozza,12091,Varese,VA,Lombardia,3,21040
+Luino,12092,Varese,VA,Lombardia,3,21016
+Luvinate,12093,Varese,VA,Lombardia,3,21020
+Malgesso,12095,Varese,VA,Lombardia,3,21020
+Malnate,12096,Varese,VA,Lombardia,3,21046
+Marchirolo,12097,Varese,VA,Lombardia,3,21030
+Marnate,12098,Varese,VA,Lombardia,3,21050
+Marzio,12099,Varese,VA,Lombardia,3,21030
+Masciago Primo,12100,Varese,VA,Lombardia,3,21030
+Mercallo,12101,Varese,VA,Lombardia,3,21020
+Mesenzana,12102,Varese,VA,Lombardia,3,21030
+Montegrino Valtravaglia,12103,Varese,VA,Lombardia,3,21010
+Monvalle,12104,Varese,VA,Lombardia,3,21020
+Morazzone,12105,Varese,VA,Lombardia,3,21040
+Mornago,12106,Varese,VA,Lombardia,3,21020
+Oggiona con Santo Stefano,12107,Varese,VA,Lombardia,3,21040
+Olgiate Olona,12108,Varese,VA,Lombardia,3,21057
+Origgio,12109,Varese,VA,Lombardia,3,21040
+Orino,12110,Varese,VA,Lombardia,3,21030
+Porto Ceresio,12113,Varese,VA,Lombardia,3,21050
+Porto Valtravaglia,12114,Varese,VA,Lombardia,3,21010
+Rancio Valcuvia,12115,Varese,VA,Lombardia,3,21030
+Ranco,12116,Varese,VA,Lombardia,3,21020
+Saltrio,12117,Varese,VA,Lombardia,3,21050
+Samarate,12118,Varese,VA,Lombardia,3,21017
+Saronno,12119,Varese,VA,Lombardia,3,21047
+Sesto Calende,12120,Varese,VA,Lombardia,3,21018
+Solbiate Arno,12121,Varese,VA,Lombardia,3,21048
+Solbiate Olona,12122,Varese,VA,Lombardia,3,21058
+Somma Lombardo,12123,Varese,VA,Lombardia,3,21019
+Sumirago,12124,Varese,VA,Lombardia,3,21040
+Taino,12125,Varese,VA,Lombardia,3,21020
+Ternate,12126,Varese,VA,Lombardia,3,21020
+Tradate,12127,Varese,VA,Lombardia,3,21049
+Travedona-Monate,12128,Varese,VA,Lombardia,3,21028
+Tronzano Lago Maggiore,12129,Varese,VA,Lombardia,3,21010
+Uboldo,12130,Varese,VA,Lombardia,3,21040
+Valganna,12131,Varese,VA,Lombardia,3,21039
+Varano Borghi,12132,Varese,VA,Lombardia,3,21020
+Varese,12133,Varese,VA,Lombardia,3,21100
+Vedano Olona,12134,Varese,VA,Lombardia,3,21040
+Venegono Inferiore,12136,Varese,VA,Lombardia,3,21040
+Venegono Superiore,12137,Varese,VA,Lombardia,3,21040
+Vergiate,12138,Varese,VA,Lombardia,3,21029
+Viggiù,12139,Varese,VA,Lombardia,3,21059
+Vizzola Ticino,12140,Varese,VA,Lombardia,3,21010
+Sangiano,12141,Varese,VA,Lombardia,3,21038
+Maccagno con Pino e Veddasca,12142,Varese,VA,Lombardia,3,21061
+Cadrezzate con Osmate,12143,Varese,VA,Lombardia,3,21062
+Albavilla,13003,Como,CO,Lombardia,3,22031
+Albese con Cassano,13004,Como,CO,Lombardia,3,22032
+Albiolo,13005,Como,CO,Lombardia,3,22070
+Alserio,13006,Como,CO,Lombardia,3,22040
+Alzate Brianza,13007,Como,CO,Lombardia,3,22040
+Anzano del Parco,13009,Como,CO,Lombardia,3,22040
+Appiano Gentile,13010,Como,CO,Lombardia,3,22070
+Argegno,13011,Como,CO,Lombardia,3,22010
+Arosio,13012,Como,CO,Lombardia,3,22060
+Asso,13013,Como,CO,Lombardia,3,22033
+Barni,13015,Como,CO,Lombardia,3,22030
+Bene Lario,13021,Como,CO,Lombardia,3,22010
+Beregazzo con Figliaro,13022,Como,CO,Lombardia,3,22070
+Binago,13023,Como,CO,Lombardia,3,22070
+Bizzarone,13024,Como,CO,Lombardia,3,22020
+Blessagno,13025,Como,CO,Lombardia,3,22028
+Blevio,13026,Como,CO,Lombardia,3,22020
+Bregnano,13028,Como,CO,Lombardia,3,22070
+Brenna,13029,Como,CO,Lombardia,3,22040
+Brienno,13030,Como,CO,Lombardia,3,22010
+Brunate,13032,Como,CO,Lombardia,3,22034
+Bulgarograsso,13034,Como,CO,Lombardia,3,22070
+Cabiate,13035,Como,CO,Lombardia,3,22060
+Cadorago,13036,Como,CO,Lombardia,3,22071
+Caglio,13037,Como,CO,Lombardia,3,22030
+Campione d'Italia,13040,Como,CO,Lombardia,3,22060
+Cantù,13041,Como,CO,Lombardia,3,22063
+Canzo,13042,Como,CO,Lombardia,3,22035
+Capiago Intimiano,13043,Como,CO,Lombardia,3,22070
+Carate Urio,13044,Como,CO,Lombardia,3,22010
+Carbonate,13045,Como,CO,Lombardia,3,22070
+Carimate,13046,Como,CO,Lombardia,3,22060
+Carlazzo,13047,Como,CO,Lombardia,3,22010
+Carugo,13048,Como,CO,Lombardia,3,22060
+Caslino d'Erba,13052,Como,CO,Lombardia,3,22030
+Casnate con Bernate,13053,Como,CO,Lombardia,3,22070
+Cassina Rizzardi,13055,Como,CO,Lombardia,3,22070
+Castelmarte,13058,Como,CO,Lombardia,3,22030
+Castelnuovo Bozzente,13059,Como,CO,Lombardia,3,22070
+Cavargna,13062,Como,CO,Lombardia,3,22010
+Cerano d'Intelvi,13063,Como,CO,Lombardia,3,22020
+Cermenate,13064,Como,CO,Lombardia,3,22072
+Cernobbio,13065,Como,CO,Lombardia,3,22012
+Cirimido,13068,Como,CO,Lombardia,3,22070
+Claino con Osteno,13071,Como,CO,Lombardia,3,22010
+Colonno,13074,Como,CO,Lombardia,3,22010
+Como,13075,Como,CO,Lombardia,3,22100
+Corrido,13077,Como,CO,Lombardia,3,22010
+Cremia,13083,Como,CO,Lombardia,3,22010
+Cucciago,13084,Como,CO,Lombardia,3,22060
+Cusino,13085,Como,CO,Lombardia,3,22010
+Dizzasco,13087,Como,CO,Lombardia,3,22020
+Domaso,13089,Como,CO,Lombardia,3,22013
+Dongo,13090,Como,CO,Lombardia,3,22014
+Dosso del Liro,13092,Como,CO,Lombardia,3,22010
+Erba,13095,Como,CO,Lombardia,3,22036
+Eupilio,13097,Como,CO,Lombardia,3,22030
+Faggeto Lario,13098,Como,CO,Lombardia,3,22020
+Faloppio,13099,Como,CO,Lombardia,3,22020
+Fenegrò,13100,Como,CO,Lombardia,3,22070
+Figino Serenza,13101,Como,CO,Lombardia,3,22060
+Fino Mornasco,13102,Como,CO,Lombardia,3,22073
+Garzeno,13106,Como,CO,Lombardia,3,22010
+Gera Lario,13107,Como,CO,Lombardia,3,22010
+Grandate,13110,Como,CO,Lombardia,3,22070
+Grandola ed Uniti,13111,Como,CO,Lombardia,3,22010
+Griante,13113,Como,CO,Lombardia,3,22011
+Guanzate,13114,Como,CO,Lombardia,3,22070
+Inverigo,13118,Como,CO,Lombardia,3,22044
+Laglio,13119,Como,CO,Lombardia,3,22010
+Laino,13120,Como,CO,Lombardia,3,22020
+Lambrugo,13121,Como,CO,Lombardia,3,22045
+Lasnigo,13123,Como,CO,Lombardia,3,22030
+Lezzeno,13126,Como,CO,Lombardia,3,22025
+Limido Comasco,13128,Como,CO,Lombardia,3,22070
+Lipomo,13129,Como,CO,Lombardia,3,22030
+Livo,13130,Como,CO,Lombardia,3,22010
+Locate Varesino,13131,Como,CO,Lombardia,3,22070
+Lomazzo,13133,Como,CO,Lombardia,3,22074
+Longone al Segrino,13134,Como,CO,Lombardia,3,22030
+Luisago,13135,Como,CO,Lombardia,3,22070
+Lurago d'Erba,13136,Como,CO,Lombardia,3,22040
+Lurago Marinone,13137,Como,CO,Lombardia,3,22070
+Lurate Caccivio,13138,Como,CO,Lombardia,3,22075
+Magreglio,13139,Como,CO,Lombardia,3,22030
+Mariano Comense,13143,Como,CO,Lombardia,3,22066
+Maslianico,13144,Como,CO,Lombardia,3,22026
+Menaggio,13145,Como,CO,Lombardia,3,22017
+Merone,13147,Como,CO,Lombardia,3,22046
+Moltrasio,13152,Como,CO,Lombardia,3,22010
+Monguzzo,13153,Como,CO,Lombardia,3,22040
+Montano Lucino,13154,Como,CO,Lombardia,3,22070
+Montemezzo,13155,Como,CO,Lombardia,3,22010
+Montorfano,13157,Como,CO,Lombardia,3,22030
+Mozzate,13159,Como,CO,Lombardia,3,22076
+Musso,13160,Como,CO,Lombardia,3,22010
+Nesso,13161,Como,CO,Lombardia,3,22020
+Novedrate,13163,Como,CO,Lombardia,3,22060
+Olgiate Comasco,13165,Como,CO,Lombardia,3,22077
+Oltrona di San Mamette,13169,Como,CO,Lombardia,3,22070
+Orsenigo,13170,Como,CO,Lombardia,3,22030
+Peglio,13178,Como,CO,Lombardia,3,22010
+Pianello del Lario,13183,Como,CO,Lombardia,3,22010
+Pigra,13184,Como,CO,Lombardia,3,22020
+Plesio,13185,Como,CO,Lombardia,3,22010
+Pognana Lario,13186,Como,CO,Lombardia,3,22020
+Ponna,13187,Como,CO,Lombardia,3,22020
+Ponte Lambro,13188,Como,CO,Lombardia,3,22037
+Porlezza,13189,Como,CO,Lombardia,3,22018
+Proserpio,13192,Como,CO,Lombardia,3,22030
+Pusiano,13193,Como,CO,Lombardia,3,22030
+Rezzago,13195,Como,CO,Lombardia,3,22030
+Rodero,13197,Como,CO,Lombardia,3,22070
+Ronago,13199,Como,CO,Lombardia,3,22027
+Rovellasca,13201,Como,CO,Lombardia,3,22069
+Rovello Porro,13202,Como,CO,Lombardia,3,22070
+Sala Comacina,13203,Como,CO,Lombardia,3,22010
+San Bartolomeo Val Cavargna,13204,Como,CO,Lombardia,3,22010
+San Fermo della Battaglia,13206,Como,CO,Lombardia,3,22020
+San Nazzaro Val Cavargna,13207,Como,CO,Lombardia,3,22010
+Schignano,13211,Como,CO,Lombardia,3,22020
+Senna Comasco,13212,Como,CO,Lombardia,3,22070
+Sorico,13216,Como,CO,Lombardia,3,22010
+Sormano,13217,Como,CO,Lombardia,3,22030
+Stazzona,13218,Como,CO,Lombardia,3,22010
+Tavernerio,13222,Como,CO,Lombardia,3,22038
+Torno,13223,Como,CO,Lombardia,3,22020
+Trezzone,13226,Como,CO,Lombardia,3,22010
+Turate,13227,Como,CO,Lombardia,3,22078
+Uggiate-Trevano,13228,Como,CO,Lombardia,3,22029
+Valbrona,13229,Como,CO,Lombardia,3,22039
+Valmorea,13232,Como,CO,Lombardia,3,22070
+Val Rezzo,13233,Como,CO,Lombardia,3,22010
+Valsolda,13234,Como,CO,Lombardia,3,22010
+Veleso,13236,Como,CO,Lombardia,3,22020
+Veniano,13238,Como,CO,Lombardia,3,22070
+Vercana,13239,Como,CO,Lombardia,3,22013
+Vertemate con Minoprio,13242,Como,CO,Lombardia,3,22070
+Villa Guardia,13245,Como,CO,Lombardia,3,22079
+Zelbio,13246,Como,CO,Lombardia,3,22020
+San Siro,13248,Como,CO,Lombardia,3,22010
+Gravedona ed Uniti,13249,Como,CO,Lombardia,3,22015
+Bellagio,13250,Como,CO,Lombardia,3,22021
+Colverde,13251,Como,CO,Lombardia,3,22041
+Tremezzina,13252,Como,CO,Lombardia,3,22016
+Alta Valle Intelvi,13253,Como,CO,Lombardia,3,22014
+Centro Valle Intelvi,13254,Como,CO,Lombardia,3,22028
+Solbiate con Cagno,13255,Como,CO,Lombardia,3,22070
+Albaredo per San Marco,14001,Sondrio,SO,Lombardia,3,23010
+Albosaggia,14002,Sondrio,SO,Lombardia,3,23010
+Andalo Valtellino,14003,Sondrio,SO,Lombardia,3,23014
+Aprica,14004,Sondrio,SO,Lombardia,3,23031
+Ardenno,14005,Sondrio,SO,Lombardia,3,23011
+Bema,14006,Sondrio,SO,Lombardia,3,23010
+Berbenno di Valtellina,14007,Sondrio,SO,Lombardia,3,23010
+Bianzone,14008,Sondrio,SO,Lombardia,3,23030
+Bormio,14009,Sondrio,SO,Lombardia,3,23032
+Buglio in Monte,14010,Sondrio,SO,Lombardia,3,23010
+Caiolo,14011,Sondrio,SO,Lombardia,3,23010
+Campodolcino,14012,Sondrio,SO,Lombardia,3,23021
+Caspoggio,14013,Sondrio,SO,Lombardia,3,23020
+Castello dell'Acqua,14014,Sondrio,SO,Lombardia,3,23030
+Castione Andevenno,14015,Sondrio,SO,Lombardia,3,23012
+Cedrasco,14016,Sondrio,SO,Lombardia,3,23010
+Cercino,14017,Sondrio,SO,Lombardia,3,23016
+Chiavenna,14018,Sondrio,SO,Lombardia,3,23022
+Chiesa in Valmalenco,14019,Sondrio,SO,Lombardia,3,23023
+Chiuro,14020,Sondrio,SO,Lombardia,3,23030
+Cino,14021,Sondrio,SO,Lombardia,3,23010
+Civo,14022,Sondrio,SO,Lombardia,3,23010
+Colorina,14023,Sondrio,SO,Lombardia,3,23010
+Cosio Valtellino,14024,Sondrio,SO,Lombardia,3,23013
+Dazio,14025,Sondrio,SO,Lombardia,3,23010
+Delebio,14026,Sondrio,SO,Lombardia,3,23014
+Dubino,14027,Sondrio,SO,Lombardia,3,23015
+Faedo Valtellino,14028,Sondrio,SO,Lombardia,3,23020
+Forcola,14029,Sondrio,SO,Lombardia,3,23010
+Fusine,14030,Sondrio,SO,Lombardia,3,23010
+Gerola Alta,14031,Sondrio,SO,Lombardia,3,23010
+Gordona,14032,Sondrio,SO,Lombardia,3,23020
+Grosio,14033,Sondrio,SO,Lombardia,3,23033
+Grosotto,14034,Sondrio,SO,Lombardia,3,23034
+Madesimo,14035,Sondrio,SO,Lombardia,3,23024
+Lanzada,14036,Sondrio,SO,Lombardia,3,23020
+Livigno,14037,Sondrio,SO,Lombardia,3,23041
+Lovero,14038,Sondrio,SO,Lombardia,3,23030
+Mantello,14039,Sondrio,SO,Lombardia,3,23016
+Mazzo di Valtellina,14040,Sondrio,SO,Lombardia,3,23030
+Mello,14041,Sondrio,SO,Lombardia,3,23010
+Mese,14043,Sondrio,SO,Lombardia,3,23020
+Montagna in Valtellina,14044,Sondrio,SO,Lombardia,3,23020
+Morbegno,14045,Sondrio,SO,Lombardia,3,23017
+Novate Mezzola,14046,Sondrio,SO,Lombardia,3,23025
+Pedesina,14047,Sondrio,SO,Lombardia,3,23010
+Piantedo,14048,Sondrio,SO,Lombardia,3,23010
+Piateda,14049,Sondrio,SO,Lombardia,3,23020
+Piuro,14050,Sondrio,SO,Lombardia,3,23020
+Poggiridenti,14051,Sondrio,SO,Lombardia,3,23020
+Ponte in Valtellina,14052,Sondrio,SO,Lombardia,3,23026
+Postalesio,14053,Sondrio,SO,Lombardia,3,23010
+Prata Camportaccio,14054,Sondrio,SO,Lombardia,3,23020
+Rasura,14055,Sondrio,SO,Lombardia,3,23010
+Rogolo,14056,Sondrio,SO,Lombardia,3,23010
+Samolaco,14057,Sondrio,SO,Lombardia,3,23027
+San Giacomo Filippo,14058,Sondrio,SO,Lombardia,3,23020
+Sernio,14059,Sondrio,SO,Lombardia,3,23030
+Sondalo,14060,Sondrio,SO,Lombardia,3,23035
+Sondrio,14061,Sondrio,SO,Lombardia,3,23100
+Spriana,14062,Sondrio,SO,Lombardia,3,23020
+Talamona,14063,Sondrio,SO,Lombardia,3,23018
+Tartano,14064,Sondrio,SO,Lombardia,3,23010
+Teglio,14065,Sondrio,SO,Lombardia,3,23036
+Tirano,14066,Sondrio,SO,Lombardia,3,23037
+Torre di Santa Maria,14067,Sondrio,SO,Lombardia,3,23020
+Tovo di Sant'Agata,14068,Sondrio,SO,Lombardia,3,23030
+Traona,14069,Sondrio,SO,Lombardia,3,23019
+Tresivio,14070,Sondrio,SO,Lombardia,3,23020
+Valdidentro,14071,Sondrio,SO,Lombardia,3,23038
+Valdisotto,14072,Sondrio,SO,Lombardia,3,23030
+Valfurva,14073,Sondrio,SO,Lombardia,3,23030
+Val Masino,14074,Sondrio,SO,Lombardia,3,23010
+Verceia,14075,Sondrio,SO,Lombardia,3,23020
+Vervio,14076,Sondrio,SO,Lombardia,3,23030
+Villa di Chiavenna,14077,Sondrio,SO,Lombardia,3,23029
+Villa di Tirano,14078,Sondrio,SO,Lombardia,3,23030
+Abbiategrasso,15002,Milano,MI,Lombardia,3,20081
+Albairate,15005,Milano,MI,Lombardia,3,20080
+Arconate,15007,Milano,MI,Lombardia,3,20020
+Arese,15009,Milano,MI,Lombardia,3,20020
+Arluno,15010,Milano,MI,Lombardia,3,20010
+Assago,15011,Milano,MI,Lombardia,3,20090
+Bareggio,15012,Milano,MI,Lombardia,3,20010
+Basiano,15014,Milano,MI,Lombardia,3,20060
+Basiglio,15015,Milano,MI,Lombardia,3,20080
+Bellinzago Lombardo,15016,Milano,MI,Lombardia,3,20060
+Bernate Ticino,15019,Milano,MI,Lombardia,3,20010
+Besate,15022,Milano,MI,Lombardia,3,20080
+Binasco,15024,Milano,MI,Lombardia,3,20082
+Boffalora sopra Ticino,15026,Milano,MI,Lombardia,3,20010
+Bollate,15027,Milano,MI,Lombardia,3,20021
+Bresso,15032,Milano,MI,Lombardia,3,20091
+Bubbiano,15035,Milano,MI,Lombardia,3,20080
+Buccinasco,15036,Milano,MI,Lombardia,3,20090
+Buscate,15038,Milano,MI,Lombardia,3,20010
+Bussero,15040,Milano,MI,Lombardia,3,20060
+Busto Garolfo,15041,Milano,MI,Lombardia,3,20020
+Calvignasco,15042,Milano,MI,Lombardia,3,20080
+Cambiago,15044,Milano,MI,Lombardia,3,20040
+Canegrate,15046,Milano,MI,Lombardia,3,20010
+Carpiano,15050,Milano,MI,Lombardia,3,20080
+Carugate,15051,Milano,MI,Lombardia,3,20061
+Casarile,15055,Milano,MI,Lombardia,3,20080
+Casorezzo,15058,Milano,MI,Lombardia,3,20010
+Cassano d'Adda,15059,Milano,MI,Lombardia,3,20062
+Cassina de' Pecchi,15060,Milano,MI,Lombardia,3,20060
+Cassinetta di Lugagnano,15061,Milano,MI,Lombardia,3,20081
+Castano Primo,15062,Milano,MI,Lombardia,3,20022
+Cernusco sul Naviglio,15070,Milano,MI,Lombardia,3,20063
+Cerro al Lambro,15071,Milano,MI,Lombardia,3,20070
+Cerro Maggiore,15072,Milano,MI,Lombardia,3,20023
+Cesano Boscone,15074,Milano,MI,Lombardia,3,20090
+Cesate,15076,Milano,MI,Lombardia,3,20020
+Cinisello Balsamo,15077,Milano,MI,Lombardia,3,20092
+Cisliano,15078,Milano,MI,Lombardia,3,20080
+Cologno Monzese,15081,Milano,MI,Lombardia,3,20093
+Colturano,15082,Milano,MI,Lombardia,3,20060
+Corbetta,15085,Milano,MI,Lombardia,3,20011
+Cormano,15086,Milano,MI,Lombardia,3,20032
+Cornaredo,15087,Milano,MI,Lombardia,3,20010
+Corsico,15093,Milano,MI,Lombardia,3,20094
+Cuggiono,15096,Milano,MI,Lombardia,3,20012
+Cusago,15097,Milano,MI,Lombardia,3,20090
+Cusano Milanino,15098,Milano,MI,Lombardia,3,20095
+Dairago,15099,Milano,MI,Lombardia,3,20020
+Dresano,15101,Milano,MI,Lombardia,3,20070
+Gaggiano,15103,Milano,MI,Lombardia,3,20083
+Garbagnate Milanese,15105,Milano,MI,Lombardia,3,20024
+Gessate,15106,Milano,MI,Lombardia,3,20060
+Gorgonzola,15108,Milano,MI,Lombardia,3,20064
+Grezzago,15110,Milano,MI,Lombardia,3,20056
+Gudo Visconti,15112,Milano,MI,Lombardia,3,20088
+Inveruno,15113,Milano,MI,Lombardia,3,20010
+Inzago,15114,Milano,MI,Lombardia,3,20065
+Lacchiarella,15115,Milano,MI,Lombardia,3,20084
+Lainate,15116,Milano,MI,Lombardia,3,20020
+Legnano,15118,Milano,MI,Lombardia,3,20025
+Liscate,15122,Milano,MI,Lombardia,3,20050
+Locate di Triulzi,15125,Milano,MI,Lombardia,3,20085
+Magenta,15130,Milano,MI,Lombardia,3,20013
+Magnago,15131,Milano,MI,Lombardia,3,20020
+Marcallo con Casone,15134,Milano,MI,Lombardia,3,20010
+Masate,15136,Milano,MI,Lombardia,3,20060
+Mediglia,15139,Milano,MI,Lombardia,3,20060
+Melegnano,15140,Milano,MI,Lombardia,3,20077
+Melzo,15142,Milano,MI,Lombardia,3,20066
+Mesero,15144,Milano,MI,Lombardia,3,20010
+Milano,15146,Milano,MI,Lombardia,3,20121
+Morimondo,15150,Milano,MI,Lombardia,3,20081
+Motta Visconti,15151,Milano,MI,Lombardia,3,20086
+Nerviano,15154,Milano,MI,Lombardia,3,20014
+Nosate,15155,Milano,MI,Lombardia,3,20020
+Novate Milanese,15157,Milano,MI,Lombardia,3,20026
+Noviglio,15158,Milano,MI,Lombardia,3,20082
+Opera,15159,Milano,MI,Lombardia,3,20073
+Ossona,15164,Milano,MI,Lombardia,3,20010
+Ozzero,15165,Milano,MI,Lombardia,3,20080
+Paderno Dugnano,15166,Milano,MI,Lombardia,3,20037
+Pantigliate,15167,Milano,MI,Lombardia,3,20090
+Parabiago,15168,Milano,MI,Lombardia,3,20015
+Paullo,15169,Milano,MI,Lombardia,3,20067
+Pero,15170,Milano,MI,Lombardia,3,20016
+Peschiera Borromeo,15171,Milano,MI,Lombardia,3,20068
+Pessano con Bornago,15172,Milano,MI,Lombardia,3,20060
+Pieve Emanuele,15173,Milano,MI,Lombardia,3,20090
+Pioltello,15175,Milano,MI,Lombardia,3,20096
+Pogliano Milanese,15176,Milano,MI,Lombardia,3,20010
+Pozzo d'Adda,15177,Milano,MI,Lombardia,3,20060
+Pozzuolo Martesana,15178,Milano,MI,Lombardia,3,20060
+Pregnana Milanese,15179,Milano,MI,Lombardia,3,20010
+Rescaldina,15181,Milano,MI,Lombardia,3,20027
+Rho,15182,Milano,MI,Lombardia,3,20017
+Robecchetto con Induno,15183,Milano,MI,Lombardia,3,20020
+Robecco sul Naviglio,15184,Milano,MI,Lombardia,3,20087
+Rodano,15185,Milano,MI,Lombardia,3,20053
+Rosate,15188,Milano,MI,Lombardia,3,20088
+Rozzano,15189,Milano,MI,Lombardia,3,20089
+San Colombano al Lambro,15191,Milano,MI,Lombardia,3,20078
+San Donato Milanese,15192,Milano,MI,Lombardia,3,20097
+San Giorgio su Legnano,15194,Milano,MI,Lombardia,3,20010
+San Giuliano Milanese,15195,Milano,MI,Lombardia,3,20098
+Santo Stefano Ticino,15200,Milano,MI,Lombardia,3,20010
+San Vittore Olona,15201,Milano,MI,Lombardia,3,20028
+San Zenone al Lambro,15202,Milano,MI,Lombardia,3,20070
+Sedriano,15204,Milano,MI,Lombardia,3,20018
+Segrate,15205,Milano,MI,Lombardia,3,20090
+Senago,15206,Milano,MI,Lombardia,3,20030
+Sesto San Giovanni,15209,Milano,MI,Lombardia,3,20099
+Settala,15210,Milano,MI,Lombardia,3,20090
+Settimo Milanese,15211,Milano,MI,Lombardia,3,20019
+Solaro,15213,Milano,MI,Lombardia,3,20020
+Trezzano Rosa,15219,Milano,MI,Lombardia,3,20060
+Trezzano sul Naviglio,15220,Milano,MI,Lombardia,3,20090
+Trezzo sull'Adda,15221,Milano,MI,Lombardia,3,20056
+Tribiano,15222,Milano,MI,Lombardia,3,20067
+Truccazzano,15224,Milano,MI,Lombardia,3,20060
+Turbigo,15226,Milano,MI,Lombardia,3,20029
+Vanzago,15229,Milano,MI,Lombardia,3,20010
+Vaprio d'Adda,15230,Milano,MI,Lombardia,3,20069
+Vernate,15236,Milano,MI,Lombardia,3,20080
+Vignate,15237,Milano,MI,Lombardia,3,20060
+Vimodrone,15242,Milano,MI,Lombardia,3,20055
+Vittuone,15243,Milano,MI,Lombardia,3,20010
+Vizzolo Predabissi,15244,Milano,MI,Lombardia,3,20070
+Zibido San Giacomo,15247,Milano,MI,Lombardia,3,20080
+Villa Cortese,15248,Milano,MI,Lombardia,3,20035
+Vanzaghello,15249,Milano,MI,Lombardia,3,20020
+Baranzate,15250,Milano,MI,Lombardia,3,20021
+Vermezzo con Zelo,15251,Milano,MI,Lombardia,3,20080
+Adrara San Martino,16001,Bergamo,BG,Lombardia,3,24060
+Adrara San Rocco,16002,Bergamo,BG,Lombardia,3,24060
+Albano Sant'Alessandro,16003,Bergamo,BG,Lombardia,3,24061
+Albino,16004,Bergamo,BG,Lombardia,3,24021
+Almè,16005,Bergamo,BG,Lombardia,3,24011
+Almenno San Bartolomeo,16006,Bergamo,BG,Lombardia,3,24030
+Almenno San Salvatore,16007,Bergamo,BG,Lombardia,3,24031
+Alzano Lombardo,16008,Bergamo,BG,Lombardia,3,24022
+Ambivere,16009,Bergamo,BG,Lombardia,3,24030
+Antegnate,16010,Bergamo,BG,Lombardia,3,24051
+Arcene,16011,Bergamo,BG,Lombardia,3,24040
+Ardesio,16012,Bergamo,BG,Lombardia,3,24020
+Arzago d'Adda,16013,Bergamo,BG,Lombardia,3,24040
+Averara,16014,Bergamo,BG,Lombardia,3,24010
+Aviatico,16015,Bergamo,BG,Lombardia,3,24020
+Azzano San Paolo,16016,Bergamo,BG,Lombardia,3,24052
+Azzone,16017,Bergamo,BG,Lombardia,3,24020
+Bagnatica,16018,Bergamo,BG,Lombardia,3,24060
+Barbata,16019,Bergamo,BG,Lombardia,3,24040
+Bariano,16020,Bergamo,BG,Lombardia,3,24050
+Barzana,16021,Bergamo,BG,Lombardia,3,24030
+Bedulita,16022,Bergamo,BG,Lombardia,3,24030
+Berbenno,16023,Bergamo,BG,Lombardia,3,24030
+Bergamo,16024,Bergamo,BG,Lombardia,3,24122
+Berzo San Fermo,16025,Bergamo,BG,Lombardia,3,24060
+Bianzano,16026,Bergamo,BG,Lombardia,3,24060
+Blello,16027,Bergamo,BG,Lombardia,3,24010
+Bolgare,16028,Bergamo,BG,Lombardia,3,24060
+Boltiere,16029,Bergamo,BG,Lombardia,3,24040
+Bonate Sopra,16030,Bergamo,BG,Lombardia,3,24040
+Bonate Sotto,16031,Bergamo,BG,Lombardia,3,24040
+Borgo di Terzo,16032,Bergamo,BG,Lombardia,3,24060
+Bossico,16033,Bergamo,BG,Lombardia,3,24060
+Bottanuco,16034,Bergamo,BG,Lombardia,3,24040
+Bracca,16035,Bergamo,BG,Lombardia,3,24010
+Branzi,16036,Bergamo,BG,Lombardia,3,24010
+Brembate,16037,Bergamo,BG,Lombardia,3,24041
+Brembate di Sopra,16038,Bergamo,BG,Lombardia,3,24030
+Brignano Gera d'Adda,16040,Bergamo,BG,Lombardia,3,24053
+Brumano,16041,Bergamo,BG,Lombardia,3,24037
+Brusaporto,16042,Bergamo,BG,Lombardia,3,24060
+Calcinate,16043,Bergamo,BG,Lombardia,3,24050
+Calcio,16044,Bergamo,BG,Lombardia,3,24054
+Calusco d'Adda,16046,Bergamo,BG,Lombardia,3,24033
+Calvenzano,16047,Bergamo,BG,Lombardia,3,24040
+Camerata Cornello,16048,Bergamo,BG,Lombardia,3,24010
+Canonica d'Adda,16049,Bergamo,BG,Lombardia,3,24040
+Capizzone,16050,Bergamo,BG,Lombardia,3,24030
+Capriate San Gervasio,16051,Bergamo,BG,Lombardia,3,24042
+Caprino Bergamasco,16052,Bergamo,BG,Lombardia,3,24030
+Caravaggio,16053,Bergamo,BG,Lombardia,3,24043
+Carobbio degli Angeli,16055,Bergamo,BG,Lombardia,3,24060
+Carona,16056,Bergamo,BG,Lombardia,3,24010
+Carvico,16057,Bergamo,BG,Lombardia,3,24030
+Casazza,16058,Bergamo,BG,Lombardia,3,24060
+Casirate d'Adda,16059,Bergamo,BG,Lombardia,3,24040
+Casnigo,16060,Bergamo,BG,Lombardia,3,24020
+Cassiglio,16061,Bergamo,BG,Lombardia,3,24010
+Castelli Calepio,16062,Bergamo,BG,Lombardia,3,24060
+Castel Rozzone,16063,Bergamo,BG,Lombardia,3,24040
+Castione della Presolana,16064,Bergamo,BG,Lombardia,3,24020
+Castro,16065,Bergamo,BG,Lombardia,3,24063
+Cavernago,16066,Bergamo,BG,Lombardia,3,24050
+Cazzano Sant'Andrea,16067,Bergamo,BG,Lombardia,3,24026
+Cenate Sopra,16068,Bergamo,BG,Lombardia,3,24060
+Cenate Sotto,16069,Bergamo,BG,Lombardia,3,24069
+Cene,16070,Bergamo,BG,Lombardia,3,24020
+Cerete,16071,Bergamo,BG,Lombardia,3,24020
+Chignolo d'Isola,16072,Bergamo,BG,Lombardia,3,24040
+Chiuduno,16073,Bergamo,BG,Lombardia,3,24060
+Cisano Bergamasco,16074,Bergamo,BG,Lombardia,3,24034
+Ciserano,16075,Bergamo,BG,Lombardia,3,24040
+Cividate al Piano,16076,Bergamo,BG,Lombardia,3,24050
+Clusone,16077,Bergamo,BG,Lombardia,3,24023
+Colere,16078,Bergamo,BG,Lombardia,3,24020
+Cologno al Serio,16079,Bergamo,BG,Lombardia,3,24055
+Colzate,16080,Bergamo,BG,Lombardia,3,24020
+Comun Nuovo,16081,Bergamo,BG,Lombardia,3,24040
+Corna Imagna,16082,Bergamo,BG,Lombardia,3,24030
+Cortenuova,16083,Bergamo,BG,Lombardia,3,24050
+Costa di Mezzate,16084,Bergamo,BG,Lombardia,3,24060
+Costa Valle Imagna,16085,Bergamo,BG,Lombardia,3,24030
+Costa Volpino,16086,Bergamo,BG,Lombardia,3,24062
+Covo,16087,Bergamo,BG,Lombardia,3,24050
+Credaro,16088,Bergamo,BG,Lombardia,3,24060
+Curno,16089,Bergamo,BG,Lombardia,3,24035
+Cusio,16090,Bergamo,BG,Lombardia,3,24010
+Dalmine,16091,Bergamo,BG,Lombardia,3,24044
+Dossena,16092,Bergamo,BG,Lombardia,3,24010
+Endine Gaiano,16093,Bergamo,BG,Lombardia,3,24060
+Entratico,16094,Bergamo,BG,Lombardia,3,24060
+Fara Gera d'Adda,16096,Bergamo,BG,Lombardia,3,24045
+Fara Olivana con Sola,16097,Bergamo,BG,Lombardia,3,24058
+Filago,16098,Bergamo,BG,Lombardia,3,24040
+Fino del Monte,16099,Bergamo,BG,Lombardia,3,24020
+Fiorano al Serio,16100,Bergamo,BG,Lombardia,3,24020
+Fontanella,16101,Bergamo,BG,Lombardia,3,24056
+Fonteno,16102,Bergamo,BG,Lombardia,3,24060
+Foppolo,16103,Bergamo,BG,Lombardia,3,24010
+Foresto Sparso,16104,Bergamo,BG,Lombardia,3,24060
+Fornovo San Giovanni,16105,Bergamo,BG,Lombardia,3,24040
+Fuipiano Valle Imagna,16106,Bergamo,BG,Lombardia,3,24030
+Gandellino,16107,Bergamo,BG,Lombardia,3,24020
+Gandino,16108,Bergamo,BG,Lombardia,3,24024
+Gandosso,16109,Bergamo,BG,Lombardia,3,24060
+Gaverina Terme,16110,Bergamo,BG,Lombardia,3,24060
+Gazzaniga,16111,Bergamo,BG,Lombardia,3,24025
+Ghisalba,16113,Bergamo,BG,Lombardia,3,24050
+Gorlago,16114,Bergamo,BG,Lombardia,3,24060
+Gorle,16115,Bergamo,BG,Lombardia,3,24020
+Gorno,16116,Bergamo,BG,Lombardia,3,24020
+Grassobbio,16117,Bergamo,BG,Lombardia,3,24050
+Gromo,16118,Bergamo,BG,Lombardia,3,24020
+Grone,16119,Bergamo,BG,Lombardia,3,24060
+Grumello del Monte,16120,Bergamo,BG,Lombardia,3,24064
+Isola di Fondra,16121,Bergamo,BG,Lombardia,3,24010
+Isso,16122,Bergamo,BG,Lombardia,3,24040
+Lallio,16123,Bergamo,BG,Lombardia,3,24040
+Leffe,16124,Bergamo,BG,Lombardia,3,24026
+Lenna,16125,Bergamo,BG,Lombardia,3,24010
+Levate,16126,Bergamo,BG,Lombardia,3,24040
+Locatello,16127,Bergamo,BG,Lombardia,3,24030
+Lovere,16128,Bergamo,BG,Lombardia,3,24065
+Lurano,16129,Bergamo,BG,Lombardia,3,24050
+Luzzana,16130,Bergamo,BG,Lombardia,3,24069
+Madone,16131,Bergamo,BG,Lombardia,3,24040
+Mapello,16132,Bergamo,BG,Lombardia,3,24030
+Martinengo,16133,Bergamo,BG,Lombardia,3,24057
+Mezzoldo,16134,Bergamo,BG,Lombardia,3,24010
+Misano di Gera d'Adda,16135,Bergamo,BG,Lombardia,3,24040
+Moio de' Calvi,16136,Bergamo,BG,Lombardia,3,24010
+Monasterolo del Castello,16137,Bergamo,BG,Lombardia,3,24060
+Montello,16139,Bergamo,BG,Lombardia,3,24060
+Morengo,16140,Bergamo,BG,Lombardia,3,24050
+Mornico al Serio,16141,Bergamo,BG,Lombardia,3,24050
+Mozzanica,16142,Bergamo,BG,Lombardia,3,24050
+Mozzo,16143,Bergamo,BG,Lombardia,3,24030
+Nembro,16144,Bergamo,BG,Lombardia,3,24027
+Olmo al Brembo,16145,Bergamo,BG,Lombardia,3,24010
+Oltre il Colle,16146,Bergamo,BG,Lombardia,3,24013
+Oltressenda Alta,16147,Bergamo,BG,Lombardia,3,24020
+Oneta,16148,Bergamo,BG,Lombardia,3,24020
+Onore,16149,Bergamo,BG,Lombardia,3,24020
+Orio al Serio,16150,Bergamo,BG,Lombardia,3,24050
+Ornica,16151,Bergamo,BG,Lombardia,3,24010
+Osio Sopra,16152,Bergamo,BG,Lombardia,3,24040
+Osio Sotto,16153,Bergamo,BG,Lombardia,3,24046
+Pagazzano,16154,Bergamo,BG,Lombardia,3,24040
+Paladina,16155,Bergamo,BG,Lombardia,3,24030
+Palazzago,16156,Bergamo,BG,Lombardia,3,24030
+Palosco,16157,Bergamo,BG,Lombardia,3,24050
+Parre,16158,Bergamo,BG,Lombardia,3,24020
+Parzanica,16159,Bergamo,BG,Lombardia,3,24060
+Pedrengo,16160,Bergamo,BG,Lombardia,3,24066
+Peia,16161,Bergamo,BG,Lombardia,3,24020
+Pianico,16162,Bergamo,BG,Lombardia,3,24060
+Piario,16163,Bergamo,BG,Lombardia,3,24020
+Piazza Brembana,16164,Bergamo,BG,Lombardia,3,24014
+Piazzatorre,16165,Bergamo,BG,Lombardia,3,24010
+Piazzolo,16166,Bergamo,BG,Lombardia,3,24010
+Pognano,16167,Bergamo,BG,Lombardia,3,24040
+Ponte Nossa,16168,Bergamo,BG,Lombardia,3,24028
+Ponteranica,16169,Bergamo,BG,Lombardia,3,24010
+Ponte San Pietro,16170,Bergamo,BG,Lombardia,3,24036
+Pontida,16171,Bergamo,BG,Lombardia,3,24030
+Pontirolo Nuovo,16172,Bergamo,BG,Lombardia,3,24040
+Pradalunga,16173,Bergamo,BG,Lombardia,3,24020
+Predore,16174,Bergamo,BG,Lombardia,3,24060
+Premolo,16175,Bergamo,BG,Lombardia,3,24020
+Presezzo,16176,Bergamo,BG,Lombardia,3,24030
+Pumenengo,16177,Bergamo,BG,Lombardia,3,24050
+Ranica,16178,Bergamo,BG,Lombardia,3,24020
+Ranzanico,16179,Bergamo,BG,Lombardia,3,24060
+Riva di Solto,16180,Bergamo,BG,Lombardia,3,24060
+Rogno,16182,Bergamo,BG,Lombardia,3,24060
+Romano di Lombardia,16183,Bergamo,BG,Lombardia,3,24058
+Roncobello,16184,Bergamo,BG,Lombardia,3,24010
+Roncola,16185,Bergamo,BG,Lombardia,3,24030
+Rota d'Imagna,16186,Bergamo,BG,Lombardia,3,24037
+Rovetta,16187,Bergamo,BG,Lombardia,3,24020
+San Giovanni Bianco,16188,Bergamo,BG,Lombardia,3,24015
+San Paolo d'Argon,16189,Bergamo,BG,Lombardia,3,24060
+San Pellegrino Terme,16190,Bergamo,BG,Lombardia,3,24016
+Santa Brigida,16191,Bergamo,BG,Lombardia,3,24010
+Sarnico,16193,Bergamo,BG,Lombardia,3,24067
+Scanzorosciate,16194,Bergamo,BG,Lombardia,3,24020
+Schilpario,16195,Bergamo,BG,Lombardia,3,24020
+Sedrina,16196,Bergamo,BG,Lombardia,3,24010
+Selvino,16197,Bergamo,BG,Lombardia,3,24020
+Seriate,16198,Bergamo,BG,Lombardia,3,24068
+Serina,16199,Bergamo,BG,Lombardia,3,24017
+Solto Collina,16200,Bergamo,BG,Lombardia,3,24060
+Songavazzo,16201,Bergamo,BG,Lombardia,3,24020
+Sorisole,16202,Bergamo,BG,Lombardia,3,24010
+Sotto il Monte Giovanni XXIII,16203,Bergamo,BG,Lombardia,3,24039
+Sovere,16204,Bergamo,BG,Lombardia,3,24060
+Spinone al Lago,16205,Bergamo,BG,Lombardia,3,24060
+Spirano,16206,Bergamo,BG,Lombardia,3,24050
+Stezzano,16207,Bergamo,BG,Lombardia,3,24040
+Strozza,16208,Bergamo,BG,Lombardia,3,24030
+Suisio,16209,Bergamo,BG,Lombardia,3,24040
+Taleggio,16210,Bergamo,BG,Lombardia,3,24010
+Tavernola Bergamasca,16211,Bergamo,BG,Lombardia,3,24060
+Telgate,16212,Bergamo,BG,Lombardia,3,24060
+Terno d'Isola,16213,Bergamo,BG,Lombardia,3,24030
+Torre Boldone,16214,Bergamo,BG,Lombardia,3,24020
+Torre de' Busi,16215,Bergamo,BG,Lombardia,3,23806
+Torre de' Roveri,16216,Bergamo,BG,Lombardia,3,24060
+Torre Pallavicina,16217,Bergamo,BG,Lombardia,3,24050
+Trescore Balneario,16218,Bergamo,BG,Lombardia,3,24069
+Treviglio,16219,Bergamo,BG,Lombardia,3,24047
+Treviolo,16220,Bergamo,BG,Lombardia,3,24048
+Ubiale Clanezzo,16221,Bergamo,BG,Lombardia,3,24010
+Urgnano,16222,Bergamo,BG,Lombardia,3,24059
+Valbondione,16223,Bergamo,BG,Lombardia,3,24020
+Valbrembo,16224,Bergamo,BG,Lombardia,3,24030
+Valgoglio,16225,Bergamo,BG,Lombardia,3,24020
+Valleve,16226,Bergamo,BG,Lombardia,3,24010
+Valnegra,16227,Bergamo,BG,Lombardia,3,24010
+Valtorta,16229,Bergamo,BG,Lombardia,3,24010
+Vedeseta,16230,Bergamo,BG,Lombardia,3,24010
+Verdellino,16232,Bergamo,BG,Lombardia,3,24040
+Verdello,16233,Bergamo,BG,Lombardia,3,24049
+Vertova,16234,Bergamo,BG,Lombardia,3,24029
+Viadanica,16235,Bergamo,BG,Lombardia,3,24060
+Vigano San Martino,16236,Bergamo,BG,Lombardia,3,24060
+Vigolo,16237,Bergamo,BG,Lombardia,3,24060
+Villa d'Adda,16238,Bergamo,BG,Lombardia,3,24030
+Villa d'Almè,16239,Bergamo,BG,Lombardia,3,24018
+Villa di Serio,16240,Bergamo,BG,Lombardia,3,24020
+Villa d'Ogna,16241,Bergamo,BG,Lombardia,3,24020
+Villongo,16242,Bergamo,BG,Lombardia,3,24060
+Vilminore di Scalve,16243,Bergamo,BG,Lombardia,3,24020
+Zandobbio,16244,Bergamo,BG,Lombardia,3,24060
+Zanica,16245,Bergamo,BG,Lombardia,3,24050
+Zogno,16246,Bergamo,BG,Lombardia,3,24019
+Costa Serina,16247,Bergamo,BG,Lombardia,3,24010
+Algua,16248,Bergamo,BG,Lombardia,3,24010
+Cornalba,16249,Bergamo,BG,Lombardia,3,24017
+Medolago,16250,Bergamo,BG,Lombardia,3,24030
+Solza,16251,Bergamo,BG,Lombardia,3,24030
+Sant'Omobono Terme,16252,Bergamo,BG,Lombardia,3,24038
+Val Brembilla,16253,Bergamo,BG,Lombardia,3,24012
+Acquafredda,17001,Brescia,BS,Lombardia,3,25010
+Adro,17002,Brescia,BS,Lombardia,3,25030
+Agnosine,17003,Brescia,BS,Lombardia,3,25071
+Alfianello,17004,Brescia,BS,Lombardia,3,25020
+Anfo,17005,Brescia,BS,Lombardia,3,25070
+Angolo Terme,17006,Brescia,BS,Lombardia,3,25040
+Artogne,17007,Brescia,BS,Lombardia,3,25040
+Azzano Mella,17008,Brescia,BS,Lombardia,3,25020
+Bagnolo Mella,17009,Brescia,BS,Lombardia,3,25021
+Bagolino,17010,Brescia,BS,Lombardia,3,25072
+Barbariga,17011,Brescia,BS,Lombardia,3,25030
+Barghe,17012,Brescia,BS,Lombardia,3,25070
+Bassano Bresciano,17013,Brescia,BS,Lombardia,3,25020
+Bedizzole,17014,Brescia,BS,Lombardia,3,25081
+Berlingo,17015,Brescia,BS,Lombardia,3,25030
+Berzo Demo,17016,Brescia,BS,Lombardia,3,25040
+Berzo Inferiore,17017,Brescia,BS,Lombardia,3,25040
+Bienno,17018,Brescia,BS,Lombardia,3,25040
+Bione,17019,Brescia,BS,Lombardia,3,25070
+Borgo San Giacomo,17020,Brescia,BS,Lombardia,3,25022
+Borgosatollo,17021,Brescia,BS,Lombardia,3,25010
+Borno,17022,Brescia,BS,Lombardia,3,25042
+Botticino,17023,Brescia,BS,Lombardia,3,25082
+Bovegno,17024,Brescia,BS,Lombardia,3,25061
+Bovezzo,17025,Brescia,BS,Lombardia,3,25073
+Brandico,17026,Brescia,BS,Lombardia,3,25030
+Braone,17027,Brescia,BS,Lombardia,3,25040
+Breno,17028,Brescia,BS,Lombardia,3,25043
+Brescia,17029,Brescia,BS,Lombardia,3,25121
+Brione,17030,Brescia,BS,Lombardia,3,25060
+Caino,17031,Brescia,BS,Lombardia,3,25070
+Calcinato,17032,Brescia,BS,Lombardia,3,25011
+Calvagese della Riviera,17033,Brescia,BS,Lombardia,3,25080
+Calvisano,17034,Brescia,BS,Lombardia,3,25012
+Capo di Ponte,17035,Brescia,BS,Lombardia,3,25044
+Capovalle,17036,Brescia,BS,Lombardia,3,25070
+Capriano del Colle,17037,Brescia,BS,Lombardia,3,25020
+Capriolo,17038,Brescia,BS,Lombardia,3,25031
+Carpenedolo,17039,Brescia,BS,Lombardia,3,25013
+Castegnato,17040,Brescia,BS,Lombardia,3,25045
+Castelcovati,17041,Brescia,BS,Lombardia,3,25030
+Castel Mella,17042,Brescia,BS,Lombardia,3,25030
+Castenedolo,17043,Brescia,BS,Lombardia,3,25014
+Casto,17044,Brescia,BS,Lombardia,3,25070
+Castrezzato,17045,Brescia,BS,Lombardia,3,25030
+Cazzago San Martino,17046,Brescia,BS,Lombardia,3,25046
+Cedegolo,17047,Brescia,BS,Lombardia,3,25051
+Cellatica,17048,Brescia,BS,Lombardia,3,25060
+Cerveno,17049,Brescia,BS,Lombardia,3,25040
+Ceto,17050,Brescia,BS,Lombardia,3,25040
+Cevo,17051,Brescia,BS,Lombardia,3,25040
+Chiari,17052,Brescia,BS,Lombardia,3,25032
+Cigole,17053,Brescia,BS,Lombardia,3,25020
+Cimbergo,17054,Brescia,BS,Lombardia,3,25050
+Cividate Camuno,17055,Brescia,BS,Lombardia,3,25040
+Coccaglio,17056,Brescia,BS,Lombardia,3,25030
+Collebeato,17057,Brescia,BS,Lombardia,3,25060
+Collio,17058,Brescia,BS,Lombardia,3,25060
+Cologne,17059,Brescia,BS,Lombardia,3,25033
+Comezzano-Cizzago,17060,Brescia,BS,Lombardia,3,25030
+Concesio,17061,Brescia,BS,Lombardia,3,25062
+Corte Franca,17062,Brescia,BS,Lombardia,3,25040
+Corteno Golgi,17063,Brescia,BS,Lombardia,3,25040
+Corzano,17064,Brescia,BS,Lombardia,3,25030
+Darfo Boario Terme,17065,Brescia,BS,Lombardia,3,25047
+Dello,17066,Brescia,BS,Lombardia,3,25020
+Desenzano del Garda,17067,Brescia,BS,Lombardia,3,25015
+Edolo,17068,Brescia,BS,Lombardia,3,25048
+Erbusco,17069,Brescia,BS,Lombardia,3,25030
+Esine,17070,Brescia,BS,Lombardia,3,25040
+Fiesse,17071,Brescia,BS,Lombardia,3,25020
+Flero,17072,Brescia,BS,Lombardia,3,25020
+Gambara,17073,Brescia,BS,Lombardia,3,25020
+Gardone Riviera,17074,Brescia,BS,Lombardia,3,25083
+Gardone Val Trompia,17075,Brescia,BS,Lombardia,3,25063
+Gargnano,17076,Brescia,BS,Lombardia,3,25084
+Gavardo,17077,Brescia,BS,Lombardia,3,25085
+Ghedi,17078,Brescia,BS,Lombardia,3,25016
+Gianico,17079,Brescia,BS,Lombardia,3,25040
+Gottolengo,17080,Brescia,BS,Lombardia,3,25023
+Gussago,17081,Brescia,BS,Lombardia,3,25064
+Idro,17082,Brescia,BS,Lombardia,3,25074
+Incudine,17083,Brescia,BS,Lombardia,3,25040
+Irma,17084,Brescia,BS,Lombardia,3,25061
+Iseo,17085,Brescia,BS,Lombardia,3,25049
+Isorella,17086,Brescia,BS,Lombardia,3,25010
+Lavenone,17087,Brescia,BS,Lombardia,3,25074
+Leno,17088,Brescia,BS,Lombardia,3,25024
+Limone sul Garda,17089,Brescia,BS,Lombardia,3,25010
+Lodrino,17090,Brescia,BS,Lombardia,3,25060
+Lograto,17091,Brescia,BS,Lombardia,3,25030
+Lonato del Garda,17092,Brescia,BS,Lombardia,3,25017
+Longhena,17093,Brescia,BS,Lombardia,3,25030
+Losine,17094,Brescia,BS,Lombardia,3,25050
+Lozio,17095,Brescia,BS,Lombardia,3,25040
+Lumezzane,17096,Brescia,BS,Lombardia,3,25065
+Maclodio,17097,Brescia,BS,Lombardia,3,25030
+Magasa,17098,Brescia,BS,Lombardia,3,25080
+Mairano,17099,Brescia,BS,Lombardia,3,25030
+Malegno,17100,Brescia,BS,Lombardia,3,25053
+Malonno,17101,Brescia,BS,Lombardia,3,25040
+Manerba del Garda,17102,Brescia,BS,Lombardia,3,25080
+Manerbio,17103,Brescia,BS,Lombardia,3,25025
+Marcheno,17104,Brescia,BS,Lombardia,3,25060
+Marmentino,17105,Brescia,BS,Lombardia,3,25060
+Marone,17106,Brescia,BS,Lombardia,3,25054
+Mazzano,17107,Brescia,BS,Lombardia,3,25080
+Milzano,17108,Brescia,BS,Lombardia,3,25020
+Moniga del Garda,17109,Brescia,BS,Lombardia,3,25080
+Monno,17110,Brescia,BS,Lombardia,3,25040
+Monte Isola,17111,Brescia,BS,Lombardia,3,25050
+Monticelli Brusati,17112,Brescia,BS,Lombardia,3,25040
+Montichiari,17113,Brescia,BS,Lombardia,3,25018
+Montirone,17114,Brescia,BS,Lombardia,3,25010
+Mura,17115,Brescia,BS,Lombardia,3,25070
+Muscoline,17116,Brescia,BS,Lombardia,3,25080
+Nave,17117,Brescia,BS,Lombardia,3,25075
+Niardo,17118,Brescia,BS,Lombardia,3,25050
+Nuvolento,17119,Brescia,BS,Lombardia,3,25080
+Nuvolera,17120,Brescia,BS,Lombardia,3,25080
+Odolo,17121,Brescia,BS,Lombardia,3,25076
+Offlaga,17122,Brescia,BS,Lombardia,3,25020
+Ome,17123,Brescia,BS,Lombardia,3,25050
+Ono San Pietro,17124,Brescia,BS,Lombardia,3,25040
+Orzinuovi,17125,Brescia,BS,Lombardia,3,25034
+Orzivecchi,17126,Brescia,BS,Lombardia,3,25030
+Ospitaletto,17127,Brescia,BS,Lombardia,3,25035
+Ossimo,17128,Brescia,BS,Lombardia,3,25050
+Padenghe sul Garda,17129,Brescia,BS,Lombardia,3,25080
+Paderno Franciacorta,17130,Brescia,BS,Lombardia,3,25050
+Paisco Loveno,17131,Brescia,BS,Lombardia,3,25050
+Paitone,17132,Brescia,BS,Lombardia,3,25080
+Palazzolo sull'Oglio,17133,Brescia,BS,Lombardia,3,25036
+Paratico,17134,Brescia,BS,Lombardia,3,25030
+Paspardo,17135,Brescia,BS,Lombardia,3,25050
+Passirano,17136,Brescia,BS,Lombardia,3,25050
+Pavone del Mella,17137,Brescia,BS,Lombardia,3,25020
+San Paolo,17138,Brescia,BS,Lombardia,3,25020
+Pertica Alta,17139,Brescia,BS,Lombardia,3,25070
+Pertica Bassa,17140,Brescia,BS,Lombardia,3,25078
+Pezzaze,17141,Brescia,BS,Lombardia,3,25060
+Pian Camuno,17142,Brescia,BS,Lombardia,3,25050
+Pisogne,17143,Brescia,BS,Lombardia,3,25055
+Polaveno,17144,Brescia,BS,Lombardia,3,25060
+Polpenazze del Garda,17145,Brescia,BS,Lombardia,3,25080
+Pompiano,17146,Brescia,BS,Lombardia,3,25030
+Poncarale,17147,Brescia,BS,Lombardia,3,25020
+Ponte di Legno,17148,Brescia,BS,Lombardia,3,25056
+Pontevico,17149,Brescia,BS,Lombardia,3,25026
+Pontoglio,17150,Brescia,BS,Lombardia,3,25037
+Pozzolengo,17151,Brescia,BS,Lombardia,3,25010
+Pralboino,17152,Brescia,BS,Lombardia,3,25020
+Preseglie,17153,Brescia,BS,Lombardia,3,25070
+Prevalle,17155,Brescia,BS,Lombardia,3,25080
+Provaglio d'Iseo,17156,Brescia,BS,Lombardia,3,25050
+Provaglio Val Sabbia,17157,Brescia,BS,Lombardia,3,25070
+Puegnago del Garda,17158,Brescia,BS,Lombardia,3,25080
+Quinzano d'Oglio,17159,Brescia,BS,Lombardia,3,25027
+Remedello,17160,Brescia,BS,Lombardia,3,25010
+Rezzato,17161,Brescia,BS,Lombardia,3,25086
+Roccafranca,17162,Brescia,BS,Lombardia,3,25030
+Rodengo Saiano,17163,Brescia,BS,Lombardia,3,25050
+Roè Volciano,17164,Brescia,BS,Lombardia,3,25077
+Roncadelle,17165,Brescia,BS,Lombardia,3,25030
+Rovato,17166,Brescia,BS,Lombardia,3,25038
+Rudiano,17167,Brescia,BS,Lombardia,3,25030
+Sabbio Chiese,17168,Brescia,BS,Lombardia,3,25070
+Sale Marasino,17169,Brescia,BS,Lombardia,3,25057
+Salò,17170,Brescia,BS,Lombardia,3,25087
+San Felice del Benaco,17171,Brescia,BS,Lombardia,3,25010
+San Gervasio Bresciano,17172,Brescia,BS,Lombardia,3,25020
+San Zeno Naviglio,17173,Brescia,BS,Lombardia,3,25010
+Sarezzo,17174,Brescia,BS,Lombardia,3,25068
+Saviore dell'Adamello,17175,Brescia,BS,Lombardia,3,25040
+Sellero,17176,Brescia,BS,Lombardia,3,25050
+Seniga,17177,Brescia,BS,Lombardia,3,25020
+Serle,17178,Brescia,BS,Lombardia,3,25080
+Sirmione,17179,Brescia,BS,Lombardia,3,25019
+Soiano del Lago,17180,Brescia,BS,Lombardia,3,25080
+Sonico,17181,Brescia,BS,Lombardia,3,25048
+Sulzano,17182,Brescia,BS,Lombardia,3,25058
+Tavernole sul Mella,17183,Brescia,BS,Lombardia,3,25060
+Temù,17184,Brescia,BS,Lombardia,3,25050
+Tignale,17185,Brescia,BS,Lombardia,3,25080
+Torbole Casaglia,17186,Brescia,BS,Lombardia,3,25030
+Toscolano-Maderno,17187,Brescia,BS,Lombardia,3,25088
+Travagliato,17188,Brescia,BS,Lombardia,3,25039
+Tremosine sul Garda,17189,Brescia,BS,Lombardia,3,25010
+Trenzano,17190,Brescia,BS,Lombardia,3,25030
+Treviso Bresciano,17191,Brescia,BS,Lombardia,3,25070
+Urago d'Oglio,17192,Brescia,BS,Lombardia,3,25030
+Vallio Terme,17193,Brescia,BS,Lombardia,3,25080
+Valvestino,17194,Brescia,BS,Lombardia,3,25080
+Verolanuova,17195,Brescia,BS,Lombardia,3,25028
+Verolavecchia,17196,Brescia,BS,Lombardia,3,25029
+Vestone,17197,Brescia,BS,Lombardia,3,25078
+Vezza d'Oglio,17198,Brescia,BS,Lombardia,3,25059
+Villa Carcina,17199,Brescia,BS,Lombardia,3,25069
+Villachiara,17200,Brescia,BS,Lombardia,3,25030
+Villanuova sul Clisi,17201,Brescia,BS,Lombardia,3,25089
+Vione,17202,Brescia,BS,Lombardia,3,25050
+Visano,17203,Brescia,BS,Lombardia,3,25010
+Vobarno,17204,Brescia,BS,Lombardia,3,25079
+Zone,17205,Brescia,BS,Lombardia,3,25050
+Piancogno,17206,Brescia,BS,Lombardia,3,25052
+Alagna,18001,Pavia,PV,Lombardia,3,27020
+Albaredo Arnaboldi,18002,Pavia,PV,Lombardia,3,27040
+Albonese,18003,Pavia,PV,Lombardia,3,27020
+Albuzzano,18004,Pavia,PV,Lombardia,3,27010
+Arena Po,18005,Pavia,PV,Lombardia,3,27040
+Badia Pavese,18006,Pavia,PV,Lombardia,3,27010
+Bagnaria,18007,Pavia,PV,Lombardia,3,27050
+Barbianello,18008,Pavia,PV,Lombardia,3,27041
+Bascapè,18009,Pavia,PV,Lombardia,3,27010
+Bastida Pancarana,18011,Pavia,PV,Lombardia,3,27050
+Battuda,18012,Pavia,PV,Lombardia,3,27020
+Belgioioso,18013,Pavia,PV,Lombardia,3,27011
+Bereguardo,18014,Pavia,PV,Lombardia,3,27021
+Borgarello,18015,Pavia,PV,Lombardia,3,27010
+Borgo Priolo,18016,Pavia,PV,Lombardia,3,27040
+Borgoratto Mormorolo,18017,Pavia,PV,Lombardia,3,27040
+Borgo San Siro,18018,Pavia,PV,Lombardia,3,27020
+Bornasco,18019,Pavia,PV,Lombardia,3,27010
+Bosnasco,18020,Pavia,PV,Lombardia,3,27040
+Brallo di Pregola,18021,Pavia,PV,Lombardia,3,27050
+Breme,18022,Pavia,PV,Lombardia,3,27020
+Bressana Bottarone,18023,Pavia,PV,Lombardia,3,27042
+Broni,18024,Pavia,PV,Lombardia,3,27043
+Calvignano,18025,Pavia,PV,Lombardia,3,27040
+Campospinoso,18026,Pavia,PV,Lombardia,3,27040
+Candia Lomellina,18027,Pavia,PV,Lombardia,3,27031
+Canneto Pavese,18029,Pavia,PV,Lombardia,3,27044
+Carbonara al Ticino,18030,Pavia,PV,Lombardia,3,27020
+Casanova Lonati,18031,Pavia,PV,Lombardia,3,27041
+Casatisma,18032,Pavia,PV,Lombardia,3,27040
+Casei Gerola,18033,Pavia,PV,Lombardia,3,27050
+Casorate Primo,18034,Pavia,PV,Lombardia,3,27022
+Cassolnovo,18035,Pavia,PV,Lombardia,3,27023
+Castana,18036,Pavia,PV,Lombardia,3,27040
+Casteggio,18037,Pavia,PV,Lombardia,3,27045
+Castelletto di Branduzzo,18038,Pavia,PV,Lombardia,3,27040
+Castello d'Agogna,18039,Pavia,PV,Lombardia,3,27030
+Castelnovetto,18040,Pavia,PV,Lombardia,3,27030
+Cava Manara,18041,Pavia,PV,Lombardia,3,27051
+Cecima,18042,Pavia,PV,Lombardia,3,27050
+Ceranova,18043,Pavia,PV,Lombardia,3,27010
+Ceretto Lomellina,18044,Pavia,PV,Lombardia,3,27030
+Cergnago,18045,Pavia,PV,Lombardia,3,27020
+Certosa di Pavia,18046,Pavia,PV,Lombardia,3,27012
+Cervesina,18047,Pavia,PV,Lombardia,3,27050
+Chignolo Po,18048,Pavia,PV,Lombardia,3,27013
+Cigognola,18049,Pavia,PV,Lombardia,3,27040
+Cilavegna,18050,Pavia,PV,Lombardia,3,27024
+Codevilla,18051,Pavia,PV,Lombardia,3,27050
+Confienza,18052,Pavia,PV,Lombardia,3,27030
+Copiano,18053,Pavia,PV,Lombardia,3,27010
+Corana,18054,Pavia,PV,Lombardia,3,27050
+Corvino San Quirico,18057,Pavia,PV,Lombardia,3,27050
+Costa de' Nobili,18058,Pavia,PV,Lombardia,3,27010
+Cozzo,18059,Pavia,PV,Lombardia,3,27030
+Cura Carpignano,18060,Pavia,PV,Lombardia,3,27010
+Dorno,18061,Pavia,PV,Lombardia,3,27020
+Ferrera Erbognone,18062,Pavia,PV,Lombardia,3,27032
+Filighera,18063,Pavia,PV,Lombardia,3,27010
+Fortunago,18064,Pavia,PV,Lombardia,3,27040
+Frascarolo,18065,Pavia,PV,Lombardia,3,27030
+Galliavola,18066,Pavia,PV,Lombardia,3,27034
+Gambarana,18067,Pavia,PV,Lombardia,3,27030
+Gambolò,18068,Pavia,PV,Lombardia,3,27025
+Garlasco,18069,Pavia,PV,Lombardia,3,27026
+Gerenzago,18071,Pavia,PV,Lombardia,3,27010
+Giussago,18072,Pavia,PV,Lombardia,3,27010
+Godiasco Salice Terme,18073,Pavia,PV,Lombardia,3,27052
+Golferenzo,18074,Pavia,PV,Lombardia,3,27047
+Gravellona Lomellina,18075,Pavia,PV,Lombardia,3,27020
+Gropello Cairoli,18076,Pavia,PV,Lombardia,3,27027
+Inverno e Monteleone,18077,Pavia,PV,Lombardia,3,27010
+Landriano,18078,Pavia,PV,Lombardia,3,27015
+Langosco,18079,Pavia,PV,Lombardia,3,27030
+Lardirago,18080,Pavia,PV,Lombardia,3,27016
+Linarolo,18081,Pavia,PV,Lombardia,3,27010
+Lirio,18082,Pavia,PV,Lombardia,3,27040
+Lomello,18083,Pavia,PV,Lombardia,3,27034
+Lungavilla,18084,Pavia,PV,Lombardia,3,27053
+Magherno,18085,Pavia,PV,Lombardia,3,27010
+Marcignago,18086,Pavia,PV,Lombardia,3,27020
+Marzano,18087,Pavia,PV,Lombardia,3,27010
+Mede,18088,Pavia,PV,Lombardia,3,27035
+Menconico,18089,Pavia,PV,Lombardia,3,27050
+Mezzana Bigli,18090,Pavia,PV,Lombardia,3,27030
+Mezzana Rabattone,18091,Pavia,PV,Lombardia,3,27030
+Mezzanino,18092,Pavia,PV,Lombardia,3,27040
+Miradolo Terme,18093,Pavia,PV,Lombardia,3,27010
+Montalto Pavese,18094,Pavia,PV,Lombardia,3,27040
+Montebello della Battaglia,18095,Pavia,PV,Lombardia,3,27054
+Montecalvo Versiggia,18096,Pavia,PV,Lombardia,3,27047
+Montescano,18097,Pavia,PV,Lombardia,3,27040
+Montesegale,18098,Pavia,PV,Lombardia,3,27052
+Monticelli Pavese,18099,Pavia,PV,Lombardia,3,27010
+Montù Beccaria,18100,Pavia,PV,Lombardia,3,27040
+Mornico Losana,18101,Pavia,PV,Lombardia,3,27040
+Mortara,18102,Pavia,PV,Lombardia,3,27036
+Nicorvo,18103,Pavia,PV,Lombardia,3,27020
+Olevano di Lomellina,18104,Pavia,PV,Lombardia,3,27020
+Oliva Gessi,18105,Pavia,PV,Lombardia,3,27050
+Ottobiano,18106,Pavia,PV,Lombardia,3,27030
+Palestro,18107,Pavia,PV,Lombardia,3,27030
+Pancarana,18108,Pavia,PV,Lombardia,3,27050
+Parona,18109,Pavia,PV,Lombardia,3,27020
+Pavia,18110,Pavia,PV,Lombardia,3,27100
+Pietra de' Giorgi,18111,Pavia,PV,Lombardia,3,27040
+Pieve Albignola,18112,Pavia,PV,Lombardia,3,27030
+Pieve del Cairo,18113,Pavia,PV,Lombardia,3,27037
+Pieve Porto Morone,18114,Pavia,PV,Lombardia,3,27017
+Pinarolo Po,18115,Pavia,PV,Lombardia,3,27040
+Pizzale,18116,Pavia,PV,Lombardia,3,27050
+Ponte Nizza,18117,Pavia,PV,Lombardia,3,27050
+Portalbera,18118,Pavia,PV,Lombardia,3,27040
+Rea,18119,Pavia,PV,Lombardia,3,27040
+Redavalle,18120,Pavia,PV,Lombardia,3,27050
+Retorbido,18121,Pavia,PV,Lombardia,3,27050
+Rivanazzano Terme,18122,Pavia,PV,Lombardia,3,27055
+Robbio,18123,Pavia,PV,Lombardia,3,27038
+Robecco Pavese,18124,Pavia,PV,Lombardia,3,27042
+Rocca de' Giorgi,18125,Pavia,PV,Lombardia,3,27040
+Rocca Susella,18126,Pavia,PV,Lombardia,3,27052
+Rognano,18127,Pavia,PV,Lombardia,3,27010
+Romagnese,18128,Pavia,PV,Lombardia,3,27050
+Roncaro,18129,Pavia,PV,Lombardia,3,27010
+Rosasco,18130,Pavia,PV,Lombardia,3,27030
+Rovescala,18131,Pavia,PV,Lombardia,3,27040
+San Cipriano Po,18133,Pavia,PV,Lombardia,3,27043
+San Damiano al Colle,18134,Pavia,PV,Lombardia,3,27040
+San Genesio ed Uniti,18135,Pavia,PV,Lombardia,3,27010
+San Giorgio di Lomellina,18136,Pavia,PV,Lombardia,3,27020
+San Martino Siccomario,18137,Pavia,PV,Lombardia,3,27028
+Sannazzaro de' Burgondi,18138,Pavia,PV,Lombardia,3,27039
+Santa Cristina e Bissone,18139,Pavia,PV,Lombardia,3,27010
+Santa Giuletta,18140,Pavia,PV,Lombardia,3,27046
+Sant'Alessio con Vialone,18141,Pavia,PV,Lombardia,3,27016
+Santa Margherita di Staffora,18142,Pavia,PV,Lombardia,3,27050
+Santa Maria della Versa,18143,Pavia,PV,Lombardia,3,27047
+Sant'Angelo Lomellina,18144,Pavia,PV,Lombardia,3,27030
+San Zenone al Po,18145,Pavia,PV,Lombardia,3,27010
+Sartirana Lomellina,18146,Pavia,PV,Lombardia,3,27020
+Scaldasole,18147,Pavia,PV,Lombardia,3,27020
+Semiana,18148,Pavia,PV,Lombardia,3,27020
+Silvano Pietra,18149,Pavia,PV,Lombardia,3,27050
+Siziano,18150,Pavia,PV,Lombardia,3,27010
+Sommo,18151,Pavia,PV,Lombardia,3,27048
+Spessa,18152,Pavia,PV,Lombardia,3,27010
+Stradella,18153,Pavia,PV,Lombardia,3,27049
+Suardi,18154,Pavia,PV,Lombardia,3,27030
+Torrazza Coste,18155,Pavia,PV,Lombardia,3,27050
+Torre Beretti e Castellaro,18156,Pavia,PV,Lombardia,3,27030
+Torre d'Arese,18157,Pavia,PV,Lombardia,3,27010
+Torre de' Negri,18158,Pavia,PV,Lombardia,3,27011
+Torre d'Isola,18159,Pavia,PV,Lombardia,3,27020
+Torrevecchia Pia,18160,Pavia,PV,Lombardia,3,27010
+Torricella Verzate,18161,Pavia,PV,Lombardia,3,27050
+Travacò Siccomario,18162,Pavia,PV,Lombardia,3,27020
+Trivolzio,18163,Pavia,PV,Lombardia,3,27020
+Tromello,18164,Pavia,PV,Lombardia,3,27020
+Trovo,18165,Pavia,PV,Lombardia,3,27020
+Val di Nizza,18166,Pavia,PV,Lombardia,3,27050
+Valeggio,18167,Pavia,PV,Lombardia,3,27020
+Valle Lomellina,18168,Pavia,PV,Lombardia,3,27020
+Valle Salimbene,18169,Pavia,PV,Lombardia,3,27010
+Varzi,18171,Pavia,PV,Lombardia,3,27057
+Velezzo Lomellina,18172,Pavia,PV,Lombardia,3,27020
+Vellezzo Bellini,18173,Pavia,PV,Lombardia,3,27010
+Verretto,18174,Pavia,PV,Lombardia,3,27053
+Verrua Po,18175,Pavia,PV,Lombardia,3,27040
+Vidigulfo,18176,Pavia,PV,Lombardia,3,27018
+Vigevano,18177,Pavia,PV,Lombardia,3,27029
+Villa Biscossi,18178,Pavia,PV,Lombardia,3,27035
+Villanova d'Ardenghi,18179,Pavia,PV,Lombardia,3,27030
+Villanterio,18180,Pavia,PV,Lombardia,3,27019
+Vistarino,18181,Pavia,PV,Lombardia,3,27010
+Voghera,18182,Pavia,PV,Lombardia,3,27058
+Volpara,18183,Pavia,PV,Lombardia,3,27047
+Zavattarello,18184,Pavia,PV,Lombardia,3,27059
+Zeccone,18185,Pavia,PV,Lombardia,3,27010
+Zeme,18186,Pavia,PV,Lombardia,3,27030
+Zenevredo,18187,Pavia,PV,Lombardia,3,27049
+Zerbo,18188,Pavia,PV,Lombardia,3,27017
+Zerbolò,18189,Pavia,PV,Lombardia,3,27020
+Zinasco,18190,Pavia,PV,Lombardia,3,27030
+Cornale e Bastida,18191,Pavia,PV,Lombardia,3,27056
+Corteolona e Genzone,18192,Pavia,PV,Lombardia,3,27014
+Colli Verdi,18193,Pavia,PV,Lombardia,3,27061
+Acquanegra Cremonese,19001,Cremona,CR,Lombardia,3,26020
+Agnadello,19002,Cremona,CR,Lombardia,3,26020
+Annicco,19003,Cremona,CR,Lombardia,3,26021
+Azzanello,19004,Cremona,CR,Lombardia,3,26010
+Bagnolo Cremasco,19005,Cremona,CR,Lombardia,3,26010
+Bonemerse,19006,Cremona,CR,Lombardia,3,26040
+Bordolano,19007,Cremona,CR,Lombardia,3,26020
+Calvatone,19009,Cremona,CR,Lombardia,3,26030
+Camisano,19010,Cremona,CR,Lombardia,3,26010
+Campagnola Cremasca,19011,Cremona,CR,Lombardia,3,26010
+Capergnanica,19012,Cremona,CR,Lombardia,3,26010
+Cappella Cantone,19013,Cremona,CR,Lombardia,3,26020
+Cappella de' Picenardi,19014,Cremona,CR,Lombardia,3,26030
+Capralba,19015,Cremona,CR,Lombardia,3,26010
+Casalbuttano ed Uniti,19016,Cremona,CR,Lombardia,3,26011
+Casale Cremasco-Vidolasco,19017,Cremona,CR,Lombardia,3,26010
+Casaletto Ceredano,19018,Cremona,CR,Lombardia,3,26010
+Casaletto di Sopra,19019,Cremona,CR,Lombardia,3,26014
+Casaletto Vaprio,19020,Cremona,CR,Lombardia,3,26010
+Casalmaggiore,19021,Cremona,CR,Lombardia,3,26041
+Casalmorano,19022,Cremona,CR,Lombardia,3,26020
+Casteldidone,19023,Cremona,CR,Lombardia,3,26030
+Castel Gabbiano,19024,Cremona,CR,Lombardia,3,26010
+Castelleone,19025,Cremona,CR,Lombardia,3,26012
+Castelverde,19026,Cremona,CR,Lombardia,3,26022
+Castelvisconti,19027,Cremona,CR,Lombardia,3,26010
+Cella Dati,19028,Cremona,CR,Lombardia,3,26040
+Chieve,19029,Cremona,CR,Lombardia,3,26010
+Cicognolo,19030,Cremona,CR,Lombardia,3,26030
+Cingia de' Botti,19031,Cremona,CR,Lombardia,3,26042
+Corte de' Cortesi con Cignone,19032,Cremona,CR,Lombardia,3,26020
+Corte de' Frati,19033,Cremona,CR,Lombardia,3,26010
+Credera Rubbiano,19034,Cremona,CR,Lombardia,3,26010
+Crema,19035,Cremona,CR,Lombardia,3,26013
+Cremona,19036,Cremona,CR,Lombardia,3,26100
+Cremosano,19037,Cremona,CR,Lombardia,3,26010
+Crotta d'Adda,19038,Cremona,CR,Lombardia,3,26020
+Cumignano sul Naviglio,19039,Cremona,CR,Lombardia,3,26020
+Derovere,19040,Cremona,CR,Lombardia,3,26040
+Dovera,19041,Cremona,CR,Lombardia,3,26010
+Fiesco,19043,Cremona,CR,Lombardia,3,26010
+Formigara,19044,Cremona,CR,Lombardia,3,26020
+Gabbioneta-Binanuova,19045,Cremona,CR,Lombardia,3,26030
+Gadesco-Pieve Delmona,19046,Cremona,CR,Lombardia,3,26030
+Genivolta,19047,Cremona,CR,Lombardia,3,26020
+Gerre de' Caprioli,19048,Cremona,CR,Lombardia,3,26040
+Gombito,19049,Cremona,CR,Lombardia,3,26020
+Grontardo,19050,Cremona,CR,Lombardia,3,26044
+Grumello Cremonese ed Uniti,19051,Cremona,CR,Lombardia,3,26023
+Gussola,19052,Cremona,CR,Lombardia,3,26040
+Isola Dovarese,19053,Cremona,CR,Lombardia,3,26031
+Izano,19054,Cremona,CR,Lombardia,3,26010
+Madignano,19055,Cremona,CR,Lombardia,3,26020
+Malagnino,19056,Cremona,CR,Lombardia,3,26030
+Martignana di Po,19057,Cremona,CR,Lombardia,3,26040
+Monte Cremasco,19058,Cremona,CR,Lombardia,3,26010
+Montodine,19059,Cremona,CR,Lombardia,3,26010
+Moscazzano,19060,Cremona,CR,Lombardia,3,26010
+Motta Baluffi,19061,Cremona,CR,Lombardia,3,26045
+Offanengo,19062,Cremona,CR,Lombardia,3,26010
+Olmeneta,19063,Cremona,CR,Lombardia,3,26010
+Ostiano,19064,Cremona,CR,Lombardia,3,26032
+Paderno Ponchielli,19065,Cremona,CR,Lombardia,3,26024
+Palazzo Pignano,19066,Cremona,CR,Lombardia,3,26020
+Pandino,19067,Cremona,CR,Lombardia,3,26025
+Persico Dosimo,19068,Cremona,CR,Lombardia,3,26043
+Pescarolo ed Uniti,19069,Cremona,CR,Lombardia,3,26033
+Pessina Cremonese,19070,Cremona,CR,Lombardia,3,26030
+Pianengo,19072,Cremona,CR,Lombardia,3,26010
+Pieranica,19073,Cremona,CR,Lombardia,3,26017
+Pieve d'Olmi,19074,Cremona,CR,Lombardia,3,26040
+Pieve San Giacomo,19075,Cremona,CR,Lombardia,3,26035
+Pizzighettone,19076,Cremona,CR,Lombardia,3,26026
+Pozzaglio ed Uniti,19077,Cremona,CR,Lombardia,3,26010
+Quintano,19078,Cremona,CR,Lombardia,3,26017
+Ricengo,19079,Cremona,CR,Lombardia,3,26010
+Ripalta Arpina,19080,Cremona,CR,Lombardia,3,26010
+Ripalta Cremasca,19081,Cremona,CR,Lombardia,3,26010
+Ripalta Guerina,19082,Cremona,CR,Lombardia,3,26010
+Rivarolo del Re ed Uniti,19083,Cremona,CR,Lombardia,3,26036
+Rivolta d'Adda,19084,Cremona,CR,Lombardia,3,26027
+Robecco d'Oglio,19085,Cremona,CR,Lombardia,3,26010
+Romanengo,19086,Cremona,CR,Lombardia,3,26014
+Salvirola,19087,Cremona,CR,Lombardia,3,26010
+San Bassano,19088,Cremona,CR,Lombardia,3,26020
+San Daniele Po,19089,Cremona,CR,Lombardia,3,26046
+San Giovanni in Croce,19090,Cremona,CR,Lombardia,3,26037
+San Martino del Lago,19091,Cremona,CR,Lombardia,3,26040
+Scandolara Ravara,19092,Cremona,CR,Lombardia,3,26040
+Scandolara Ripa d'Oglio,19093,Cremona,CR,Lombardia,3,26047
+Sergnano,19094,Cremona,CR,Lombardia,3,26010
+Sesto ed Uniti,19095,Cremona,CR,Lombardia,3,26028
+Solarolo Rainerio,19096,Cremona,CR,Lombardia,3,26030
+Soncino,19097,Cremona,CR,Lombardia,3,26029
+Soresina,19098,Cremona,CR,Lombardia,3,26015
+Sospiro,19099,Cremona,CR,Lombardia,3,26048
+Spinadesco,19100,Cremona,CR,Lombardia,3,26020
+Spineda,19101,Cremona,CR,Lombardia,3,26030
+Spino d'Adda,19102,Cremona,CR,Lombardia,3,26016
+Stagno Lombardo,19103,Cremona,CR,Lombardia,3,26049
+Ticengo,19104,Cremona,CR,Lombardia,3,26020
+Torlino Vimercati,19105,Cremona,CR,Lombardia,3,26017
+Tornata,19106,Cremona,CR,Lombardia,3,26030
+Torre de' Picenardi,19107,Cremona,CR,Lombardia,3,26038
+Torricella del Pizzo,19108,Cremona,CR,Lombardia,3,26040
+Trescore Cremasco,19109,Cremona,CR,Lombardia,3,26017
+Trigolo,19110,Cremona,CR,Lombardia,3,26018
+Vaiano Cremasco,19111,Cremona,CR,Lombardia,3,26010
+Vailate,19112,Cremona,CR,Lombardia,3,26019
+Vescovato,19113,Cremona,CR,Lombardia,3,26039
+Volongo,19114,Cremona,CR,Lombardia,3,26030
+Voltido,19115,Cremona,CR,Lombardia,3,26030
+Piadena Drizzona,19116,Cremona,CR,Lombardia,3,26034
+Acquanegra sul Chiese,20001,Mantova,MN,Lombardia,3,46011
+Asola,20002,Mantova,MN,Lombardia,3,46041
+Bagnolo San Vito,20003,Mantova,MN,Lombardia,3,46031
+Bozzolo,20007,Mantova,MN,Lombardia,3,46012
+Canneto sull'Oglio,20008,Mantova,MN,Lombardia,3,46013
+Casalmoro,20010,Mantova,MN,Lombardia,3,46040
+Casaloldo,20011,Mantova,MN,Lombardia,3,46040
+Casalromano,20012,Mantova,MN,Lombardia,3,46040
+Castelbelforte,20013,Mantova,MN,Lombardia,3,46032
+Castel d'Ario,20014,Mantova,MN,Lombardia,3,46033
+Castel Goffredo,20015,Mantova,MN,Lombardia,3,46042
+Castellucchio,20016,Mantova,MN,Lombardia,3,46014
+Castiglione delle Stiviere,20017,Mantova,MN,Lombardia,3,46043
+Cavriana,20018,Mantova,MN,Lombardia,3,46040
+Ceresara,20019,Mantova,MN,Lombardia,3,46040
+Commessaggio,20020,Mantova,MN,Lombardia,3,46010
+Curtatone,20021,Mantova,MN,Lombardia,3,46010
+Dosolo,20022,Mantova,MN,Lombardia,3,46030
+Gazoldo degli Ippoliti,20024,Mantova,MN,Lombardia,3,46040
+Gazzuolo,20025,Mantova,MN,Lombardia,3,46010
+Goito,20026,Mantova,MN,Lombardia,3,46044
+Gonzaga,20027,Mantova,MN,Lombardia,3,46023
+Guidizzolo,20028,Mantova,MN,Lombardia,3,46040
+Magnacavallo,20029,Mantova,MN,Lombardia,3,46020
+Mantova,20030,Mantova,MN,Lombardia,3,46100
+Marcaria,20031,Mantova,MN,Lombardia,3,46010
+Mariana Mantovana,20032,Mantova,MN,Lombardia,3,46010
+Marmirolo,20033,Mantova,MN,Lombardia,3,46045
+Medole,20034,Mantova,MN,Lombardia,3,46046
+Moglia,20035,Mantova,MN,Lombardia,3,46024
+Monzambano,20036,Mantova,MN,Lombardia,3,46040
+Motteggiana,20037,Mantova,MN,Lombardia,3,46020
+Ostiglia,20038,Mantova,MN,Lombardia,3,46035
+Pegognaga,20039,Mantova,MN,Lombardia,3,46020
+Piubega,20041,Mantova,MN,Lombardia,3,46040
+Poggio Rusco,20042,Mantova,MN,Lombardia,3,46025
+Pomponesco,20043,Mantova,MN,Lombardia,3,46030
+Ponti sul Mincio,20044,Mantova,MN,Lombardia,3,46040
+Porto Mantovano,20045,Mantova,MN,Lombardia,3,46047
+Quingentole,20046,Mantova,MN,Lombardia,3,46020
+Quistello,20047,Mantova,MN,Lombardia,3,46026
+Redondesco,20048,Mantova,MN,Lombardia,3,46010
+Rivarolo Mantovano,20050,Mantova,MN,Lombardia,3,46017
+Rodigo,20051,Mantova,MN,Lombardia,3,46040
+Roncoferraro,20052,Mantova,MN,Lombardia,3,46037
+Roverbella,20053,Mantova,MN,Lombardia,3,46048
+Sabbioneta,20054,Mantova,MN,Lombardia,3,46018
+San Benedetto Po,20055,Mantova,MN,Lombardia,3,46027
+San Giacomo delle Segnate,20056,Mantova,MN,Lombardia,3,46020
+San Giorgio Bigarello,20057,Mantova,MN,Lombardia,3,46030
+San Giovanni del Dosso,20058,Mantova,MN,Lombardia,3,46020
+San Martino dall'Argine,20059,Mantova,MN,Lombardia,3,46010
+Schivenoglia,20060,Mantova,MN,Lombardia,3,46020
+Sermide e Felonica,20061,Mantova,MN,Lombardia,3,46028
+Serravalle a Po,20062,Mantova,MN,Lombardia,3,46030
+Solferino,20063,Mantova,MN,Lombardia,3,46040
+Sustinente,20064,Mantova,MN,Lombardia,3,46030
+Suzzara,20065,Mantova,MN,Lombardia,3,46029
+Viadana,20066,Mantova,MN,Lombardia,3,46019
+Villimpenta,20068,Mantova,MN,Lombardia,3,46039
+Volta Mantovana,20070,Mantova,MN,Lombardia,3,46049
+Borgo Virgilio,20071,Mantova,MN,Lombardia,3,46034
+Borgo Mantovano,20072,Mantova,MN,Lombardia,3,46036
+Borgocarbonara,20073,Mantova,MN,Lombardia,3,46020
+Abbadia Lariana,97001,Lecco,LC,Lombardia,3,23821
+Airuno,97002,Lecco,LC,Lombardia,3,23881
+Annone di Brianza,97003,Lecco,LC,Lombardia,3,23841
+Ballabio,97004,Lecco,LC,Lombardia,3,23811
+Barzago,97005,Lecco,LC,Lombardia,3,23890
+Barzanò,97006,Lecco,LC,Lombardia,3,23891
+Barzio,97007,Lecco,LC,Lombardia,3,23816
+Bellano,97008,Lecco,LC,Lombardia,3,23822
+Bosisio Parini,97009,Lecco,LC,Lombardia,3,23842
+Brivio,97010,Lecco,LC,Lombardia,3,23883
+Bulciago,97011,Lecco,LC,Lombardia,3,23892
+Calco,97012,Lecco,LC,Lombardia,3,23885
+Calolziocorte,97013,Lecco,LC,Lombardia,3,23801
+Carenno,97014,Lecco,LC,Lombardia,3,23802
+Casargo,97015,Lecco,LC,Lombardia,3,23831
+Casatenovo,97016,Lecco,LC,Lombardia,3,23880
+Cassago Brianza,97017,Lecco,LC,Lombardia,3,23893
+Cassina Valsassina,97018,Lecco,LC,Lombardia,3,23817
+Castello di Brianza,97019,Lecco,LC,Lombardia,3,23884
+Cernusco Lombardone,97020,Lecco,LC,Lombardia,3,23870
+Cesana Brianza,97021,Lecco,LC,Lombardia,3,23861
+Civate,97022,Lecco,LC,Lombardia,3,23862
+Colico,97023,Lecco,LC,Lombardia,3,23823
+Colle Brianza,97024,Lecco,LC,Lombardia,3,23886
+Cortenova,97025,Lecco,LC,Lombardia,3,23813
+Costa Masnaga,97026,Lecco,LC,Lombardia,3,23845
+Crandola Valsassina,97027,Lecco,LC,Lombardia,3,23832
+Cremella,97028,Lecco,LC,Lombardia,3,23894
+Cremeno,97029,Lecco,LC,Lombardia,3,23814
+Dervio,97030,Lecco,LC,Lombardia,3,23824
+Dolzago,97031,Lecco,LC,Lombardia,3,23843
+Dorio,97032,Lecco,LC,Lombardia,3,23824
+Ello,97033,Lecco,LC,Lombardia,3,23848
+Erve,97034,Lecco,LC,Lombardia,3,23805
+Esino Lario,97035,Lecco,LC,Lombardia,3,23825
+Galbiate,97036,Lecco,LC,Lombardia,3,23851
+Garbagnate Monastero,97037,Lecco,LC,Lombardia,3,23846
+Garlate,97038,Lecco,LC,Lombardia,3,23852
+Imbersago,97039,Lecco,LC,Lombardia,3,23898
+Introbio,97040,Lecco,LC,Lombardia,3,23815
+Lecco,97042,Lecco,LC,Lombardia,3,23900
+Lierna,97043,Lecco,LC,Lombardia,3,23827
+Lomagna,97044,Lecco,LC,Lombardia,3,23871
+Malgrate,97045,Lecco,LC,Lombardia,3,23864
+Mandello del Lario,97046,Lecco,LC,Lombardia,3,23826
+Margno,97047,Lecco,LC,Lombardia,3,23832
+Merate,97048,Lecco,LC,Lombardia,3,23807
+Missaglia,97049,Lecco,LC,Lombardia,3,23873
+Moggio,97050,Lecco,LC,Lombardia,3,23817
+Molteno,97051,Lecco,LC,Lombardia,3,23847
+Monte Marenzo,97052,Lecco,LC,Lombardia,3,23804
+Montevecchia,97053,Lecco,LC,Lombardia,3,23874
+Monticello Brianza,97054,Lecco,LC,Lombardia,3,23876
+Morterone,97055,Lecco,LC,Lombardia,3,23811
+Nibionno,97056,Lecco,LC,Lombardia,3,23895
+Oggiono,97057,Lecco,LC,Lombardia,3,23848
+Olgiate Molgora,97058,Lecco,LC,Lombardia,3,23887
+Olginate,97059,Lecco,LC,Lombardia,3,23854
+Oliveto Lario,97060,Lecco,LC,Lombardia,3,23865
+Osnago,97061,Lecco,LC,Lombardia,3,23875
+Paderno d'Adda,97062,Lecco,LC,Lombardia,3,23877
+Pagnona,97063,Lecco,LC,Lombardia,3,23833
+Parlasco,97064,Lecco,LC,Lombardia,3,23837
+Pasturo,97065,Lecco,LC,Lombardia,3,23818
+Perledo,97067,Lecco,LC,Lombardia,3,23828
+Pescate,97068,Lecco,LC,Lombardia,3,23855
+Premana,97069,Lecco,LC,Lombardia,3,23834
+Primaluna,97070,Lecco,LC,Lombardia,3,23819
+Robbiate,97071,Lecco,LC,Lombardia,3,23899
+Rogeno,97072,Lecco,LC,Lombardia,3,23849
+Santa Maria Hoè,97074,Lecco,LC,Lombardia,3,23889
+Sirone,97075,Lecco,LC,Lombardia,3,23844
+Sirtori,97076,Lecco,LC,Lombardia,3,23896
+Sueglio,97077,Lecco,LC,Lombardia,3,23835
+Suello,97078,Lecco,LC,Lombardia,3,23867
+Taceno,97079,Lecco,LC,Lombardia,3,23837
+Valgreghentino,97082,Lecco,LC,Lombardia,3,23857
+Valmadrera,97083,Lecco,LC,Lombardia,3,23868
+Varenna,97084,Lecco,LC,Lombardia,3,23829
+Vercurago,97086,Lecco,LC,Lombardia,3,23808
+Viganò,97090,Lecco,LC,Lombardia,3,23897
+Verderio,97091,Lecco,LC,Lombardia,3,23879
+La Valletta Brianza,97092,Lecco,LC,Lombardia,3,23888
+Valvarrone,97093,Lecco,LC,Lombardia,3,23836
+Abbadia Cerreto,98001,Lodi,LO,Lombardia,3,26834
+Bertonico,98002,Lodi,LO,Lombardia,3,26821
+Boffalora d'Adda,98003,Lodi,LO,Lombardia,3,26811
+Borghetto Lodigiano,98004,Lodi,LO,Lombardia,3,26812
+Borgo San Giovanni,98005,Lodi,LO,Lombardia,3,26851
+Brembio,98006,Lodi,LO,Lombardia,3,26822
+Casaletto Lodigiano,98008,Lodi,LO,Lombardia,3,26852
+Casalmaiocco,98009,Lodi,LO,Lombardia,3,26831
+Casalpusterlengo,98010,Lodi,LO,Lombardia,3,26841
+Caselle Landi,98011,Lodi,LO,Lombardia,3,26842
+Caselle Lurani,98012,Lodi,LO,Lombardia,3,26853
+Castelnuovo Bocca d'Adda,98013,Lodi,LO,Lombardia,3,26843
+Castiglione d'Adda,98014,Lodi,LO,Lombardia,3,26823
+Castiraga Vidardo,98015,Lodi,LO,Lombardia,3,26866
+Cavenago d'Adda,98017,Lodi,LO,Lombardia,3,26824
+Cervignano d'Adda,98018,Lodi,LO,Lombardia,3,26832
+Codogno,98019,Lodi,LO,Lombardia,3,26845
+Comazzo,98020,Lodi,LO,Lombardia,3,26833
+Cornegliano Laudense,98021,Lodi,LO,Lombardia,3,26854
+Corno Giovine,98022,Lodi,LO,Lombardia,3,26846
+Cornovecchio,98023,Lodi,LO,Lombardia,3,26842
+Corte Palasio,98024,Lodi,LO,Lombardia,3,26834
+Crespiatica,98025,Lodi,LO,Lombardia,3,26835
+Fombio,98026,Lodi,LO,Lombardia,3,26861
+Galgagnano,98027,Lodi,LO,Lombardia,3,26832
+Graffignana,98028,Lodi,LO,Lombardia,3,26813
+Guardamiglio,98029,Lodi,LO,Lombardia,3,26862
+Livraga,98030,Lodi,LO,Lombardia,3,26814
+Lodi,98031,Lodi,LO,Lombardia,3,26900
+Lodi Vecchio,98032,Lodi,LO,Lombardia,3,26855
+Maccastorna,98033,Lodi,LO,Lombardia,3,26843
+Mairago,98034,Lodi,LO,Lombardia,3,26825
+Maleo,98035,Lodi,LO,Lombardia,3,26847
+Marudo,98036,Lodi,LO,Lombardia,3,26866
+Massalengo,98037,Lodi,LO,Lombardia,3,26815
+Meleti,98038,Lodi,LO,Lombardia,3,26843
+Merlino,98039,Lodi,LO,Lombardia,3,26833
+Montanaso Lombardo,98040,Lodi,LO,Lombardia,3,26836
+Mulazzano,98041,Lodi,LO,Lombardia,3,26837
+Orio Litta,98042,Lodi,LO,Lombardia,3,26863
+Ospedaletto Lodigiano,98043,Lodi,LO,Lombardia,3,26864
+Ossago Lodigiano,98044,Lodi,LO,Lombardia,3,26816
+Pieve Fissiraga,98045,Lodi,LO,Lombardia,3,26854
+Salerano sul Lambro,98046,Lodi,LO,Lombardia,3,26857
+San Fiorano,98047,Lodi,LO,Lombardia,3,26848
+San Martino in Strada,98048,Lodi,LO,Lombardia,3,26817
+San Rocco al Porto,98049,Lodi,LO,Lombardia,3,26865
+Sant'Angelo Lodigiano,98050,Lodi,LO,Lombardia,3,26866
+Santo Stefano Lodigiano,98051,Lodi,LO,Lombardia,3,26849
+Secugnago,98052,Lodi,LO,Lombardia,3,26826
+Senna Lodigiana,98053,Lodi,LO,Lombardia,3,26856
+Somaglia,98054,Lodi,LO,Lombardia,3,26867
+Sordio,98055,Lodi,LO,Lombardia,3,26858
+Tavazzano con Villavesco,98056,Lodi,LO,Lombardia,3,26838
+Terranova dei Passerini,98057,Lodi,LO,Lombardia,3,26827
+Turano Lodigiano,98058,Lodi,LO,Lombardia,3,26828
+Valera Fratta,98059,Lodi,LO,Lombardia,3,26859
+Villanova del Sillaro,98060,Lodi,LO,Lombardia,3,26818
+Zelo Buon Persico,98061,Lodi,LO,Lombardia,3,26839
+Castelgerundo,98062,Lodi,LO,Lombardia,3,26844
+Agrate Brianza,108001,Monza e della Brianza,MB,Lombardia,3,20864
+Aicurzio,108002,Monza e della Brianza,MB,Lombardia,3,20886
+Albiate,108003,Monza e della Brianza,MB,Lombardia,3,20847
+Arcore,108004,Monza e della Brianza,MB,Lombardia,3,20862
+Barlassina,108005,Monza e della Brianza,MB,Lombardia,3,20825
+Bellusco,108006,Monza e della Brianza,MB,Lombardia,3,20882
+Bernareggio,108007,Monza e della Brianza,MB,Lombardia,3,20881
+Besana in Brianza,108008,Monza e della Brianza,MB,Lombardia,3,20842
+Biassono,108009,Monza e della Brianza,MB,Lombardia,3,20853
+Bovisio-Masciago,108010,Monza e della Brianza,MB,Lombardia,3,20813
+Briosco,108011,Monza e della Brianza,MB,Lombardia,3,20836
+Brugherio,108012,Monza e della Brianza,MB,Lombardia,3,20861
+Burago di Molgora,108013,Monza e della Brianza,MB,Lombardia,3,20875
+Camparada,108014,Monza e della Brianza,MB,Lombardia,3,20857
+Carate Brianza,108015,Monza e della Brianza,MB,Lombardia,3,20841
+Carnate,108016,Monza e della Brianza,MB,Lombardia,3,20866
+Cavenago di Brianza,108017,Monza e della Brianza,MB,Lombardia,3,20873
+Ceriano Laghetto,108018,Monza e della Brianza,MB,Lombardia,3,20816
+Cesano Maderno,108019,Monza e della Brianza,MB,Lombardia,3,20811
+Cogliate,108020,Monza e della Brianza,MB,Lombardia,3,20815
+Concorezzo,108021,Monza e della Brianza,MB,Lombardia,3,20863
+Correzzana,108022,Monza e della Brianza,MB,Lombardia,3,20856
+Desio,108023,Monza e della Brianza,MB,Lombardia,3,20832
+Giussano,108024,Monza e della Brianza,MB,Lombardia,3,20833
+Lazzate,108025,Monza e della Brianza,MB,Lombardia,3,20824
+Lesmo,108026,Monza e della Brianza,MB,Lombardia,3,20855
+Limbiate,108027,Monza e della Brianza,MB,Lombardia,3,20812
+Lissone,108028,Monza e della Brianza,MB,Lombardia,3,20851
+Macherio,108029,Monza e della Brianza,MB,Lombardia,3,20846
+Meda,108030,Monza e della Brianza,MB,Lombardia,3,20821
+Mezzago,108031,Monza e della Brianza,MB,Lombardia,3,20883
+Misinto,108032,Monza e della Brianza,MB,Lombardia,3,20826
+Monza,108033,Monza e della Brianza,MB,Lombardia,3,20900
+Muggiò,108034,Monza e della Brianza,MB,Lombardia,3,20835
+Nova Milanese,108035,Monza e della Brianza,MB,Lombardia,3,20834
+Ornago,108036,Monza e della Brianza,MB,Lombardia,3,20876
+Renate,108037,Monza e della Brianza,MB,Lombardia,3,20838
+Ronco Briantino,108038,Monza e della Brianza,MB,Lombardia,3,20885
+Seregno,108039,Monza e della Brianza,MB,Lombardia,3,20831
+Seveso,108040,Monza e della Brianza,MB,Lombardia,3,20822
+Sovico,108041,Monza e della Brianza,MB,Lombardia,3,20845
+Sulbiate,108042,Monza e della Brianza,MB,Lombardia,3,20884
+Triuggio,108043,Monza e della Brianza,MB,Lombardia,3,20844
+Usmate Velate,108044,Monza e della Brianza,MB,Lombardia,3,20865
+Varedo,108045,Monza e della Brianza,MB,Lombardia,3,20814
+Vedano al Lambro,108046,Monza e della Brianza,MB,Lombardia,3,20854
+Veduggio con Colzano,108047,Monza e della Brianza,MB,Lombardia,3,20837
+Verano Brianza,108048,Monza e della Brianza,MB,Lombardia,3,20843
+Villasanta,108049,Monza e della Brianza,MB,Lombardia,3,20852
+Vimercate,108050,Monza e della Brianza,MB,Lombardia,3,20871
+Busnago,108051,Monza e della Brianza,MB,Lombardia,3,20874
+Caponago,108052,Monza e della Brianza,MB,Lombardia,3,20867
+Cornate d'Adda,108053,Monza e della Brianza,MB,Lombardia,3,20872
+Lentate sul Seveso,108054,Monza e della Brianza,MB,Lombardia,3,20823
+Roncello,108055,Monza e della Brianza,MB,Lombardia,3,20877
+Aldino,21001,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Andriano,21002,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Anterivo,21003,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Appiano sulla strada del vino,21004,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39057
+Avelengo,21005,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Badia,21006,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39036
+Barbiano,21007,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Bolzano,21008,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39100
+Braies,21009,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Brennero,21010,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39041
+Bressanone,21011,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39042
+Bronzolo,21012,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39051
+Brunico,21013,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39031
+Caines,21014,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Caldaro sulla strada del vino,21015,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39052
+Campo di Trens,21016,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Campo Tures,21017,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39032
+Castelbello-Ciardes,21018,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39020
+Castelrotto,21019,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Cermes,21020,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Chienes,21021,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Chiusa,21022,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39043
+Cornedo all'Isarco,21023,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39053
+Cortaccia sulla strada del vino,21024,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Cortina sulla strada del vino,21025,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Corvara in Badia,21026,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39033
+Curon Venosta,21027,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39027
+Dobbiaco,21028,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39034
+Egna,21029,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39044
+Falzes,21030,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Fiè allo Sciliar,21031,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39050
+Fortezza,21032,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39045
+Funes,21033,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Gais,21034,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Gargazzone,21035,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Glorenza,21036,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39020
+Laces,21037,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39021
+Lagundo,21038,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39022
+Laion,21039,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Laives,21040,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39055
+Lana,21041,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39011
+Lasa,21042,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39023
+Lauregno,21043,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Luson,21044,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Magrè sulla strada del vino,21045,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Malles Venosta,21046,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39024
+Marebbe,21047,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Marlengo,21048,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39020
+Martello,21049,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39020
+Meltina,21050,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Merano,21051,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39012
+Monguelfo-Tesido,21052,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39035
+Montagna,21053,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Moso in Passiria,21054,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39013
+Nalles,21055,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Naturno,21056,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39025
+Naz-Sciaves,21057,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Nova Levante,21058,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39056
+Nova Ponente,21059,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39050
+Ora,21060,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Ortisei,21061,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39046
+Parcines,21062,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39020
+Perca,21063,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Plaus,21064,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39025
+Ponte Gardena,21065,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Postal,21066,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39014
+Prato allo Stelvio,21067,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39026
+Predoi,21068,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Proves,21069,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Racines,21070,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Rasun-Anterselva,21071,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Renon,21072,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39054
+Rifiano,21073,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Rio di Pusteria,21074,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39037
+Rodengo,21075,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39037
+Salorno sulla strada del vino,21076,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+San Candido,21077,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39038
+San Genesio Atesino,21079,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39050
+San Leonardo in Passiria,21080,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39015
+San Lorenzo di Sebato,21081,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+San Martino in Badia,21082,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+San Martino in Passiria,21083,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+San Pancrazio,21084,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Santa Cristina Valgardena,21085,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39047
+Sarentino,21086,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39058
+Scena,21087,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39017
+Selva dei Molini,21088,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Selva di Val Gardena,21089,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39048
+Senales,21091,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39020
+Sesto,21092,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Silandro,21093,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39028
+Sluderno,21094,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39020
+Stelvio,21095,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39029
+Terento,21096,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Terlano,21097,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39018
+Termeno sulla strada del vino,21098,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Tesimo,21099,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Tires,21100,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39050
+Tirolo,21101,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39019
+Trodena nel parco naturale,21102,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Tubre,21103,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39020
+Ultimo,21104,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39016
+Vadena,21105,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39051
+Valdaora,21106,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Val di Vizze,21107,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39049
+Valle Aurina,21108,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Valle di Casies,21109,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Vandoies,21110,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Varna,21111,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Verano,21112,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Villabassa,21113,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39039
+Villandro,21114,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+Vipiteno,21115,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39049
+Velturno,21116,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39040
+La Valle,21117,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39030
+Senale-San Felice,21118,Bolzano/Bozen,BZ,Trentino-Alto Adige/Südtirol,4,39010
+Ala,22001,Trento,TN,Trentino-Alto Adige/Südtirol,4,38061
+Albiano,22002,Trento,TN,Trentino-Alto Adige/Südtirol,4,38041
+Aldeno,22003,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Andalo,22005,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Arco,22006,Trento,TN,Trentino-Alto Adige/Südtirol,4,38062
+Avio,22007,Trento,TN,Trentino-Alto Adige/Südtirol,4,38063
+Baselga di Pinè,22009,Trento,TN,Trentino-Alto Adige/Südtirol,4,38042
+Bedollo,22011,Trento,TN,Trentino-Alto Adige/Südtirol,4,38043
+Besenello,22013,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Bieno,22015,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Bleggio Superiore,22017,Trento,TN,Trentino-Alto Adige/Südtirol,4,38071
+Bocenago,22018,Trento,TN,Trentino-Alto Adige/Südtirol,4,38080
+Bondone,22021,Trento,TN,Trentino-Alto Adige/Südtirol,4,38080
+Borgo Valsugana,22022,Trento,TN,Trentino-Alto Adige/Südtirol,4,38051
+Brentonico,22025,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Bresimo,22026,Trento,TN,Trentino-Alto Adige/Südtirol,4,38020
+Caderzone Terme,22029,Trento,TN,Trentino-Alto Adige/Südtirol,4,38080
+Calceranica al Lago,22032,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Caldes,22033,Trento,TN,Trentino-Alto Adige/Südtirol,4,38022
+Caldonazzo,22034,Trento,TN,Trentino-Alto Adige/Südtirol,4,38052
+Calliano,22035,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Campitello di Fassa,22036,Trento,TN,Trentino-Alto Adige/Südtirol,4,38031
+Campodenno,22037,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Canal San Bovo,22038,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Canazei,22039,Trento,TN,Trentino-Alto Adige/Südtirol,4,38032
+Capriana,22040,Trento,TN,Trentino-Alto Adige/Südtirol,4,38030
+Carisolo,22042,Trento,TN,Trentino-Alto Adige/Südtirol,4,38080
+Carzano,22043,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Castel Condino,22045,Trento,TN,Trentino-Alto Adige/Südtirol,4,38082
+Castello-Molina di Fiemme,22047,Trento,TN,Trentino-Alto Adige/Südtirol,4,38030
+Castello Tesino,22048,Trento,TN,Trentino-Alto Adige/Südtirol,4,38053
+Castelnuovo,22049,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Cavalese,22050,Trento,TN,Trentino-Alto Adige/Südtirol,4,38033
+Cavareno,22051,Trento,TN,Trentino-Alto Adige/Südtirol,4,38011
+Cavedago,22052,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Cavedine,22053,Trento,TN,Trentino-Alto Adige/Südtirol,4,38073
+Cavizzana,22054,Trento,TN,Trentino-Alto Adige/Südtirol,4,38022
+Cimone,22058,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Cinte Tesino,22059,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Cis,22060,Trento,TN,Trentino-Alto Adige/Südtirol,4,38020
+Civezzano,22061,Trento,TN,Trentino-Alto Adige/Südtirol,4,38045
+Cles,22062,Trento,TN,Trentino-Alto Adige/Südtirol,4,38023
+Commezzadura,22064,Trento,TN,Trentino-Alto Adige/Südtirol,4,38020
+Croviana,22068,Trento,TN,Trentino-Alto Adige/Südtirol,4,38027
+Dambel,22071,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Denno,22074,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Drena,22078,Trento,TN,Trentino-Alto Adige/Südtirol,4,38074
+Dro,22079,Trento,TN,Trentino-Alto Adige/Südtirol,4,38074
+Fai della Paganella,22081,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Fiavè,22083,Trento,TN,Trentino-Alto Adige/Südtirol,4,38075
+Fierozzo,22085,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Folgaria,22087,Trento,TN,Trentino-Alto Adige/Südtirol,4,38064
+Fornace,22089,Trento,TN,Trentino-Alto Adige/Südtirol,4,38040
+Frassilongo,22090,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Garniga Terme,22091,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Giovo,22092,Trento,TN,Trentino-Alto Adige/Südtirol,4,38030
+Giustino,22093,Trento,TN,Trentino-Alto Adige/Südtirol,4,38086
+Grigno,22095,Trento,TN,Trentino-Alto Adige/Südtirol,4,38055
+Imer,22097,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Isera,22098,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Lavarone,22102,Trento,TN,Trentino-Alto Adige/Südtirol,4,38046
+Lavis,22103,Trento,TN,Trentino-Alto Adige/Südtirol,4,38015
+Levico Terme,22104,Trento,TN,Trentino-Alto Adige/Südtirol,4,38056
+Livo,22106,Trento,TN,Trentino-Alto Adige/Südtirol,4,38020
+Lona-Lases,22108,Trento,TN,Trentino-Alto Adige/Südtirol,4,38040
+Luserna,22109,Trento,TN,Trentino-Alto Adige/Südtirol,4,38040
+Malé,22110,Trento,TN,Trentino-Alto Adige/Südtirol,4,38027
+Massimeno,22112,Trento,TN,Trentino-Alto Adige/Südtirol,4,38086
+Mazzin,22113,Trento,TN,Trentino-Alto Adige/Südtirol,4,38030
+Mezzana,22114,Trento,TN,Trentino-Alto Adige/Südtirol,4,38020
+Mezzano,22115,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Mezzocorona,22116,Trento,TN,Trentino-Alto Adige/Südtirol,4,38016
+Mezzolombardo,22117,Trento,TN,Trentino-Alto Adige/Südtirol,4,38017
+Moena,22118,Trento,TN,Trentino-Alto Adige/Südtirol,4,38035
+Molveno,22120,Trento,TN,Trentino-Alto Adige/Südtirol,4,38018
+Mori,22123,Trento,TN,Trentino-Alto Adige/Südtirol,4,38065
+Nago-Torbole,22124,Trento,TN,Trentino-Alto Adige/Südtirol,4,38069
+Nogaredo,22127,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Nomi,22128,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Novaledo,22129,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Ospedaletto,22130,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Ossana,22131,Trento,TN,Trentino-Alto Adige/Südtirol,4,38026
+Palù del Fersina,22133,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Panchià,22134,Trento,TN,Trentino-Alto Adige/Südtirol,4,38030
+Ronzo-Chienis,22135,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Peio,22136,Trento,TN,Trentino-Alto Adige/Südtirol,4,38024
+Pellizzano,22137,Trento,TN,Trentino-Alto Adige/Südtirol,4,38020
+Pelugo,22138,Trento,TN,Trentino-Alto Adige/Südtirol,4,38079
+Pergine Valsugana,22139,Trento,TN,Trentino-Alto Adige/Südtirol,4,38057
+Pieve Tesino,22142,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Pinzolo,22143,Trento,TN,Trentino-Alto Adige/Südtirol,4,38086
+Pomarolo,22144,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Predazzo,22147,Trento,TN,Trentino-Alto Adige/Südtirol,4,38037
+Rabbi,22150,Trento,TN,Trentino-Alto Adige/Südtirol,4,38020
+Riva del Garda,22153,Trento,TN,Trentino-Alto Adige/Südtirol,4,38066
+Romeno,22155,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Roncegno Terme,22156,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Ronchi Valsugana,22157,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Ronzone,22159,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Roverè della Luna,22160,Trento,TN,Trentino-Alto Adige/Südtirol,4,38030
+Rovereto,22161,Trento,TN,Trentino-Alto Adige/Südtirol,4,38068
+Ruffrè-Mendola,22162,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Rumo,22163,Trento,TN,Trentino-Alto Adige/Südtirol,4,38020
+Sagron Mis,22164,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Samone,22165,Trento,TN,Trentino-Alto Adige/Südtirol,4,38059
+San Michele all'Adige,22167,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Sant'Orsola Terme,22168,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Sanzeno,22169,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Sarnonico,22170,Trento,TN,Trentino-Alto Adige/Südtirol,4,38011
+Scurelle,22171,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Segonzano,22172,Trento,TN,Trentino-Alto Adige/Südtirol,4,38047
+Sfruz,22173,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Soraga di Fassa,22176,Trento,TN,Trentino-Alto Adige/Südtirol,4,38030
+Sover,22177,Trento,TN,Trentino-Alto Adige/Südtirol,4,38048
+Spiazzo,22179,Trento,TN,Trentino-Alto Adige/Südtirol,4,38088
+Spormaggiore,22180,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Sporminore,22181,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Stenico,22182,Trento,TN,Trentino-Alto Adige/Südtirol,4,38070
+Storo,22183,Trento,TN,Trentino-Alto Adige/Südtirol,4,38089
+Strembo,22184,Trento,TN,Trentino-Alto Adige/Südtirol,4,38080
+Telve,22188,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Telve di Sopra,22189,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Tenna,22190,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Tenno,22191,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Terragnolo,22193,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Terzolas,22195,Trento,TN,Trentino-Alto Adige/Südtirol,4,38027
+Tesero,22196,Trento,TN,Trentino-Alto Adige/Südtirol,4,38038
+Tione di Trento,22199,Trento,TN,Trentino-Alto Adige/Südtirol,4,38079
+Ton,22200,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Torcegno,22202,Trento,TN,Trentino-Alto Adige/Südtirol,4,38050
+Trambileno,22203,Trento,TN,Trentino-Alto Adige/Südtirol,4,38068
+Trento,22205,Trento,TN,Trentino-Alto Adige/Südtirol,4,38121
+Valfloriana,22209,Trento,TN,Trentino-Alto Adige/Südtirol,4,38040
+Vallarsa,22210,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Vermiglio,22213,Trento,TN,Trentino-Alto Adige/Südtirol,4,38029
+Vignola-Falesina,22216,Trento,TN,Trentino-Alto Adige/Südtirol,4,38057
+Villa Lagarina,22222,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Volano,22224,Trento,TN,Trentino-Alto Adige/Südtirol,4,38060
+Ziano di Fiemme,22226,Trento,TN,Trentino-Alto Adige/Südtirol,4,38030
+Comano Terme,22228,Trento,TN,Trentino-Alto Adige/Südtirol,4,38077
+Ledro,22229,Trento,TN,Trentino-Alto Adige/Südtirol,4,38067
+Predaia,22230,Trento,TN,Trentino-Alto Adige/Südtirol,4,38012
+San Lorenzo Dorsino,22231,Trento,TN,Trentino-Alto Adige/Südtirol,4,38078
+Valdaone,22232,Trento,TN,Trentino-Alto Adige/Südtirol,4,38080
+Dimaro Folgarida,22233,Trento,TN,Trentino-Alto Adige/Südtirol,4,38025
+Pieve di Bono-Prezzo,22234,Trento,TN,Trentino-Alto Adige/Südtirol,4,38085
+Altavalle,22235,Trento,TN,Trentino-Alto Adige/Südtirol,4,38092
+Altopiano della Vigolana,22236,Trento,TN,Trentino-Alto Adige/Südtirol,4,38049
+Amblar-Don,22237,Trento,TN,Trentino-Alto Adige/Südtirol,4,38011
+Borgo Chiese,22238,Trento,TN,Trentino-Alto Adige/Südtirol,4,38083
+Borgo Lares,22239,Trento,TN,Trentino-Alto Adige/Südtirol,4,38079
+Castel Ivano,22240,Trento,TN,Trentino-Alto Adige/Südtirol,4,38059
+Cembra Lisignago,22241,Trento,TN,Trentino-Alto Adige/Südtirol,4,38034
+Contà,22242,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Madruzzo,22243,Trento,TN,Trentino-Alto Adige/Südtirol,4,38076
+Porte di Rendena,22244,Trento,TN,Trentino-Alto Adige/Südtirol,4,38094
+Primiero San Martino di Castrozza,22245,Trento,TN,Trentino-Alto Adige/Südtirol,4,38054
+Sella Giudicarie,22246,Trento,TN,Trentino-Alto Adige/Südtirol,4,38087
+Tre Ville,22247,Trento,TN,Trentino-Alto Adige/Südtirol,4,38070
+Vallelaghi,22248,Trento,TN,Trentino-Alto Adige/Südtirol,4,38070
+Ville d'Anaunia,22249,Trento,TN,Trentino-Alto Adige/Südtirol,4,38019
+San Giovanni di Fassa,22250,Trento,TN,Trentino-Alto Adige/Südtirol,4,38039
+Terre d'Adige,22251,Trento,TN,Trentino-Alto Adige/Südtirol,4,38010
+Borgo d'Anaunia,22252,Trento,TN,Trentino-Alto Adige/Südtirol,4,38013
+Novella,22253,Trento,TN,Trentino-Alto Adige/Südtirol,4,38028
+Ville di Fiemme,22254,Trento,TN,Trentino-Alto Adige/Südtirol,4,38030
+Affi,23001,Verona,VR,Veneto,5,37010
+Albaredo d'Adige,23002,Verona,VR,Veneto,5,37041
+Angiari,23003,Verona,VR,Veneto,5,37050
+Arcole,23004,Verona,VR,Veneto,5,37040
+Badia Calavena,23005,Verona,VR,Veneto,5,37030
+Bardolino,23006,Verona,VR,Veneto,5,37011
+Belfiore,23007,Verona,VR,Veneto,5,37050
+Bevilacqua,23008,Verona,VR,Veneto,5,37040
+Bonavigo,23009,Verona,VR,Veneto,5,37040
+Boschi Sant'Anna,23010,Verona,VR,Veneto,5,37040
+Bosco Chiesanuova,23011,Verona,VR,Veneto,5,37021
+Bovolone,23012,Verona,VR,Veneto,5,37051
+Brentino Belluno,23013,Verona,VR,Veneto,5,37020
+Brenzone sul Garda,23014,Verona,VR,Veneto,5,37010
+Bussolengo,23015,Verona,VR,Veneto,5,37012
+Buttapietra,23016,Verona,VR,Veneto,5,37060
+Caldiero,23017,Verona,VR,Veneto,5,37042
+Caprino Veronese,23018,Verona,VR,Veneto,5,37013
+Casaleone,23019,Verona,VR,Veneto,5,37052
+Castagnaro,23020,Verona,VR,Veneto,5,37043
+Castel d'Azzano,23021,Verona,VR,Veneto,5,37060
+Castelnuovo del Garda,23022,Verona,VR,Veneto,5,37014
+Cavaion Veronese,23023,Verona,VR,Veneto,5,37010
+Cazzano di Tramigna,23024,Verona,VR,Veneto,5,37030
+Cerea,23025,Verona,VR,Veneto,5,37053
+Cerro Veronese,23026,Verona,VR,Veneto,5,37020
+Cologna Veneta,23027,Verona,VR,Veneto,5,37044
+Colognola ai Colli,23028,Verona,VR,Veneto,5,37030
+Concamarise,23029,Verona,VR,Veneto,5,37050
+Costermano sul Garda,23030,Verona,VR,Veneto,5,37010
+Dolcè,23031,Verona,VR,Veneto,5,37020
+Erbè,23032,Verona,VR,Veneto,5,37060
+Erbezzo,23033,Verona,VR,Veneto,5,37020
+Ferrara di Monte Baldo,23034,Verona,VR,Veneto,5,37020
+Fumane,23035,Verona,VR,Veneto,5,37022
+Garda,23036,Verona,VR,Veneto,5,37016
+Gazzo Veronese,23037,Verona,VR,Veneto,5,37060
+Grezzana,23038,Verona,VR,Veneto,5,37023
+Illasi,23039,Verona,VR,Veneto,5,37031
+Isola della Scala,23040,Verona,VR,Veneto,5,37063
+Isola Rizza,23041,Verona,VR,Veneto,5,37050
+Lavagno,23042,Verona,VR,Veneto,5,37030
+Lazise,23043,Verona,VR,Veneto,5,37017
+Legnago,23044,Verona,VR,Veneto,5,37045
+Malcesine,23045,Verona,VR,Veneto,5,37018
+Marano di Valpolicella,23046,Verona,VR,Veneto,5,37020
+Mezzane di Sotto,23047,Verona,VR,Veneto,5,37030
+Minerbe,23048,Verona,VR,Veneto,5,37046
+Montecchia di Crosara,23049,Verona,VR,Veneto,5,37030
+Monteforte d'Alpone,23050,Verona,VR,Veneto,5,37032
+Mozzecane,23051,Verona,VR,Veneto,5,37060
+Negrar di Valpolicella,23052,Verona,VR,Veneto,5,37024
+Nogara,23053,Verona,VR,Veneto,5,37054
+Nogarole Rocca,23054,Verona,VR,Veneto,5,37060
+Oppeano,23055,Verona,VR,Veneto,5,37050
+Palù,23056,Verona,VR,Veneto,5,37050
+Pastrengo,23057,Verona,VR,Veneto,5,37010
+Pescantina,23058,Verona,VR,Veneto,5,37026
+Peschiera del Garda,23059,Verona,VR,Veneto,5,37019
+Povegliano Veronese,23060,Verona,VR,Veneto,5,37064
+Pressana,23061,Verona,VR,Veneto,5,37040
+Rivoli Veronese,23062,Verona,VR,Veneto,5,37010
+Roncà,23063,Verona,VR,Veneto,5,37030
+Ronco all'Adige,23064,Verona,VR,Veneto,5,37055
+Roverchiara,23065,Verona,VR,Veneto,5,37050
+Roveredo di Guà,23066,Verona,VR,Veneto,5,37040
+Roverè Veronese,23067,Verona,VR,Veneto,5,37028
+Salizzole,23068,Verona,VR,Veneto,5,37056
+San Bonifacio,23069,Verona,VR,Veneto,5,37047
+San Giovanni Ilarione,23070,Verona,VR,Veneto,5,37035
+San Giovanni Lupatoto,23071,Verona,VR,Veneto,5,37057
+Sanguinetto,23072,Verona,VR,Veneto,5,37058
+San Martino Buon Albergo,23073,Verona,VR,Veneto,5,37036
+San Mauro di Saline,23074,Verona,VR,Veneto,5,37030
+San Pietro di Morubio,23075,Verona,VR,Veneto,5,37050
+San Pietro in Cariano,23076,Verona,VR,Veneto,5,37029
+Sant'Ambrogio di Valpolicella,23077,Verona,VR,Veneto,5,37015
+Sant'Anna d'Alfaedo,23078,Verona,VR,Veneto,5,37020
+San Zeno di Montagna,23079,Verona,VR,Veneto,5,37010
+Selva di Progno,23080,Verona,VR,Veneto,5,37030
+Soave,23081,Verona,VR,Veneto,5,37038
+Sommacampagna,23082,Verona,VR,Veneto,5,37066
+Sona,23083,Verona,VR,Veneto,5,37060
+Sorgà,23084,Verona,VR,Veneto,5,37060
+Terrazzo,23085,Verona,VR,Veneto,5,37040
+Torri del Benaco,23086,Verona,VR,Veneto,5,37010
+Tregnago,23087,Verona,VR,Veneto,5,37039
+Trevenzuolo,23088,Verona,VR,Veneto,5,37060
+Valeggio sul Mincio,23089,Verona,VR,Veneto,5,37067
+Velo Veronese,23090,Verona,VR,Veneto,5,37030
+Verona,23091,Verona,VR,Veneto,5,37121
+Veronella,23092,Verona,VR,Veneto,5,37040
+Vestenanova,23093,Verona,VR,Veneto,5,37030
+Vigasio,23094,Verona,VR,Veneto,5,37068
+Villa Bartolomea,23095,Verona,VR,Veneto,5,37049
+Villafranca di Verona,23096,Verona,VR,Veneto,5,37069
+Zevio,23097,Verona,VR,Veneto,5,37059
+Zimella,23098,Verona,VR,Veneto,5,37040
+Agugliaro,24001,Vicenza,VI,Veneto,5,36020
+Albettone,24002,Vicenza,VI,Veneto,5,36020
+Alonte,24003,Vicenza,VI,Veneto,5,36045
+Altavilla Vicentina,24004,Vicenza,VI,Veneto,5,36077
+Altissimo,24005,Vicenza,VI,Veneto,5,36070
+Arcugnano,24006,Vicenza,VI,Veneto,5,36057
+Arsiero,24007,Vicenza,VI,Veneto,5,36011
+Arzignano,24008,Vicenza,VI,Veneto,5,36071
+Asiago,24009,Vicenza,VI,Veneto,5,36012
+Asigliano Veneto,24010,Vicenza,VI,Veneto,5,36020
+Bassano del Grappa,24012,Vicenza,VI,Veneto,5,36061
+Bolzano Vicentino,24013,Vicenza,VI,Veneto,5,36050
+Breganze,24014,Vicenza,VI,Veneto,5,36042
+Brendola,24015,Vicenza,VI,Veneto,5,36040
+Bressanvido,24016,Vicenza,VI,Veneto,5,36050
+Brogliano,24017,Vicenza,VI,Veneto,5,36070
+Caldogno,24018,Vicenza,VI,Veneto,5,36030
+Caltrano,24019,Vicenza,VI,Veneto,5,36030
+Calvene,24020,Vicenza,VI,Veneto,5,36030
+Camisano Vicentino,24021,Vicenza,VI,Veneto,5,36043
+Campiglia dei Berici,24022,Vicenza,VI,Veneto,5,36020
+Carrè,24024,Vicenza,VI,Veneto,5,36010
+Cartigliano,24025,Vicenza,VI,Veneto,5,36050
+Cassola,24026,Vicenza,VI,Veneto,5,36022
+Castegnero,24027,Vicenza,VI,Veneto,5,36020
+Castelgomberto,24028,Vicenza,VI,Veneto,5,36070
+Chiampo,24029,Vicenza,VI,Veneto,5,36072
+Chiuppano,24030,Vicenza,VI,Veneto,5,36010
+Cogollo del Cengio,24032,Vicenza,VI,Veneto,5,36010
+Cornedo Vicentino,24034,Vicenza,VI,Veneto,5,36073
+Costabissara,24035,Vicenza,VI,Veneto,5,36030
+Creazzo,24036,Vicenza,VI,Veneto,5,36051
+Crespadoro,24037,Vicenza,VI,Veneto,5,36070
+Dueville,24038,Vicenza,VI,Veneto,5,36031
+Enego,24039,Vicenza,VI,Veneto,5,36052
+Fara Vicentino,24040,Vicenza,VI,Veneto,5,36030
+Foza,24041,Vicenza,VI,Veneto,5,36010
+Gallio,24042,Vicenza,VI,Veneto,5,36032
+Gambellara,24043,Vicenza,VI,Veneto,5,36053
+Gambugliano,24044,Vicenza,VI,Veneto,5,36050
+Grisignano di Zocco,24046,Vicenza,VI,Veneto,5,36040
+Grumolo delle Abbadesse,24047,Vicenza,VI,Veneto,5,36040
+Isola Vicentina,24048,Vicenza,VI,Veneto,5,36033
+Laghi,24049,Vicenza,VI,Veneto,5,36010
+Lastebasse,24050,Vicenza,VI,Veneto,5,36040
+Longare,24051,Vicenza,VI,Veneto,5,36023
+Lonigo,24052,Vicenza,VI,Veneto,5,36045
+Lugo di Vicenza,24053,Vicenza,VI,Veneto,5,36030
+Malo,24055,Vicenza,VI,Veneto,5,36034
+Marano Vicentino,24056,Vicenza,VI,Veneto,5,36035
+Marostica,24057,Vicenza,VI,Veneto,5,36063
+Montebello Vicentino,24060,Vicenza,VI,Veneto,5,36054
+Montecchio Maggiore,24061,Vicenza,VI,Veneto,5,36075
+Montecchio Precalcino,24062,Vicenza,VI,Veneto,5,36030
+Monte di Malo,24063,Vicenza,VI,Veneto,5,36030
+Montegalda,24064,Vicenza,VI,Veneto,5,36047
+Montegaldella,24065,Vicenza,VI,Veneto,5,36047
+Monteviale,24066,Vicenza,VI,Veneto,5,36050
+Monticello Conte Otto,24067,Vicenza,VI,Veneto,5,36010
+Montorso Vicentino,24068,Vicenza,VI,Veneto,5,36050
+Mussolente,24070,Vicenza,VI,Veneto,5,36065
+Nanto,24071,Vicenza,VI,Veneto,5,36024
+Nogarole Vicentino,24072,Vicenza,VI,Veneto,5,36070
+Nove,24073,Vicenza,VI,Veneto,5,36055
+Noventa Vicentina,24074,Vicenza,VI,Veneto,5,36025
+Orgiano,24075,Vicenza,VI,Veneto,5,36040
+Pedemonte,24076,Vicenza,VI,Veneto,5,36040
+Pianezze,24077,Vicenza,VI,Veneto,5,36060
+Piovene Rocchette,24078,Vicenza,VI,Veneto,5,36013
+Pojana Maggiore,24079,Vicenza,VI,Veneto,5,36026
+Posina,24080,Vicenza,VI,Veneto,5,36010
+Pove del Grappa,24081,Vicenza,VI,Veneto,5,36020
+Pozzoleone,24082,Vicenza,VI,Veneto,5,36050
+Quinto Vicentino,24083,Vicenza,VI,Veneto,5,36050
+Recoaro Terme,24084,Vicenza,VI,Veneto,5,36076
+Roana,24085,Vicenza,VI,Veneto,5,36010
+Romano d'Ezzelino,24086,Vicenza,VI,Veneto,5,36060
+Rosà,24087,Vicenza,VI,Veneto,5,36027
+Rossano Veneto,24088,Vicenza,VI,Veneto,5,36028
+Rotzo,24089,Vicenza,VI,Veneto,5,36010
+Salcedo,24090,Vicenza,VI,Veneto,5,36040
+Sandrigo,24091,Vicenza,VI,Veneto,5,36066
+San Pietro Mussolino,24094,Vicenza,VI,Veneto,5,36070
+Santorso,24095,Vicenza,VI,Veneto,5,36014
+San Vito di Leguzzano,24096,Vicenza,VI,Veneto,5,36030
+Sarcedo,24097,Vicenza,VI,Veneto,5,36030
+Sarego,24098,Vicenza,VI,Veneto,5,36040
+Schiavon,24099,Vicenza,VI,Veneto,5,36060
+Schio,24100,Vicenza,VI,Veneto,5,36015
+Solagna,24101,Vicenza,VI,Veneto,5,36020
+Sossano,24102,Vicenza,VI,Veneto,5,36040
+Sovizzo,24103,Vicenza,VI,Veneto,5,36050
+Tezze sul Brenta,24104,Vicenza,VI,Veneto,5,36056
+Thiene,24105,Vicenza,VI,Veneto,5,36016
+Tonezza del Cimone,24106,Vicenza,VI,Veneto,5,36040
+Torrebelvicino,24107,Vicenza,VI,Veneto,5,36036
+Torri di Quartesolo,24108,Vicenza,VI,Veneto,5,36040
+Trissino,24110,Vicenza,VI,Veneto,5,36070
+Valdagno,24111,Vicenza,VI,Veneto,5,36078
+Valdastico,24112,Vicenza,VI,Veneto,5,36040
+Valli del Pasubio,24113,Vicenza,VI,Veneto,5,36030
+Velo d'Astico,24115,Vicenza,VI,Veneto,5,36010
+Vicenza,24116,Vicenza,VI,Veneto,5,36100
+Villaga,24117,Vicenza,VI,Veneto,5,36021
+Villaverla,24118,Vicenza,VI,Veneto,5,36030
+Zanè,24119,Vicenza,VI,Veneto,5,36010
+Zermeghedo,24120,Vicenza,VI,Veneto,5,36050
+Zovencedo,24121,Vicenza,VI,Veneto,5,36020
+Zugliano,24122,Vicenza,VI,Veneto,5,36030
+Val Liona,24123,Vicenza,VI,Veneto,5,36040
+Barbarano Mossano,24124,Vicenza,VI,Veneto,5,36048
+Valbrenta,24125,Vicenza,VI,Veneto,5,36020
+Colceresa,24126,Vicenza,VI,Veneto,5,36064
+Lusiana Conco,24127,Vicenza,VI,Veneto,5,36046
+Agordo,25001,Belluno,BL,Veneto,5,32021
+Alano di Piave,25002,Belluno,BL,Veneto,5,32031
+Alleghe,25003,Belluno,BL,Veneto,5,32022
+Arsiè,25004,Belluno,BL,Veneto,5,32030
+Auronzo di Cadore,25005,Belluno,BL,Veneto,5,32041
+Belluno,25006,Belluno,BL,Veneto,5,32100
+Borca di Cadore,25007,Belluno,BL,Veneto,5,32040
+Calalzo di Cadore,25008,Belluno,BL,Veneto,5,32042
+Cencenighe Agordino,25010,Belluno,BL,Veneto,5,32020
+Cesiomaggiore,25011,Belluno,BL,Veneto,5,32030
+Chies d'Alpago,25012,Belluno,BL,Veneto,5,32010
+Cibiana di Cadore,25013,Belluno,BL,Veneto,5,32040
+Colle Santa Lucia,25014,Belluno,BL,Veneto,5,32020
+Comelico Superiore,25015,Belluno,BL,Veneto,5,32040
+Cortina d'Ampezzo,25016,Belluno,BL,Veneto,5,32043
+Danta di Cadore,25017,Belluno,BL,Veneto,5,32040
+Domegge di Cadore,25018,Belluno,BL,Veneto,5,32040
+Falcade,25019,Belluno,BL,Veneto,5,32020
+Feltre,25021,Belluno,BL,Veneto,5,32032
+Fonzaso,25022,Belluno,BL,Veneto,5,32030
+Canale d'Agordo,25023,Belluno,BL,Veneto,5,32020
+Gosaldo,25025,Belluno,BL,Veneto,5,32020
+Lamon,25026,Belluno,BL,Veneto,5,32033
+La Valle Agordina,25027,Belluno,BL,Veneto,5,32020
+Limana,25029,Belluno,BL,Veneto,5,32020
+Livinallongo del Col di Lana,25030,Belluno,BL,Veneto,5,32020
+Lorenzago di Cadore,25032,Belluno,BL,Veneto,5,32040
+Lozzo di Cadore,25033,Belluno,BL,Veneto,5,32040
+Ospitale di Cadore,25035,Belluno,BL,Veneto,5,32010
+Pedavena,25036,Belluno,BL,Veneto,5,32034
+Perarolo di Cadore,25037,Belluno,BL,Veneto,5,32010
+Pieve di Cadore,25039,Belluno,BL,Veneto,5,32044
+Ponte nelle Alpi,25040,Belluno,BL,Veneto,5,32014
+Rivamonte Agordino,25043,Belluno,BL,Veneto,5,32020
+Rocca Pietore,25044,Belluno,BL,Veneto,5,32020
+San Gregorio nelle Alpi,25045,Belluno,BL,Veneto,5,32030
+San Nicolò di Comelico,25046,Belluno,BL,Veneto,5,32040
+San Pietro di Cadore,25047,Belluno,BL,Veneto,5,32040
+Santa Giustina,25048,Belluno,BL,Veneto,5,32035
+San Tomaso Agordino,25049,Belluno,BL,Veneto,5,32020
+Santo Stefano di Cadore,25050,Belluno,BL,Veneto,5,32045
+San Vito di Cadore,25051,Belluno,BL,Veneto,5,32046
+Sedico,25053,Belluno,BL,Veneto,5,32036
+Selva di Cadore,25054,Belluno,BL,Veneto,5,32020
+Seren del Grappa,25055,Belluno,BL,Veneto,5,32030
+Sospirolo,25056,Belluno,BL,Veneto,5,32037
+Soverzene,25057,Belluno,BL,Veneto,5,32010
+Sovramonte,25058,Belluno,BL,Veneto,5,32030
+Taibon Agordino,25059,Belluno,BL,Veneto,5,32027
+Tambre,25060,Belluno,BL,Veneto,5,32010
+Vallada Agordina,25062,Belluno,BL,Veneto,5,32020
+Valle di Cadore,25063,Belluno,BL,Veneto,5,32040
+Vigo di Cadore,25065,Belluno,BL,Veneto,5,32040
+Vodo Cadore,25066,Belluno,BL,Veneto,5,32040
+Voltago Agordino,25067,Belluno,BL,Veneto,5,32020
+Zoppè di Cadore,25069,Belluno,BL,Veneto,5,32010
+Quero Vas,25070,Belluno,BL,Veneto,5,32038
+Longarone,25071,Belluno,BL,Veneto,5,32013
+Alpago,25072,Belluno,BL,Veneto,5,32010
+Val di Zoldo,25073,Belluno,BL,Veneto,5,32010
+Borgo Valbelluna,25074,Belluno,BL,Veneto,5,32026
+Altivole,26001,Treviso,TV,Veneto,5,31030
+Arcade,26002,Treviso,TV,Veneto,5,31030
+Asolo,26003,Treviso,TV,Veneto,5,31011
+Borso del Grappa,26004,Treviso,TV,Veneto,5,31030
+Breda di Piave,26005,Treviso,TV,Veneto,5,31030
+Caerano di San Marco,26006,Treviso,TV,Veneto,5,31031
+Cappella Maggiore,26007,Treviso,TV,Veneto,5,31012
+Carbonera,26008,Treviso,TV,Veneto,5,31030
+Casale sul Sile,26009,Treviso,TV,Veneto,5,31032
+Casier,26010,Treviso,TV,Veneto,5,31030
+Castelcucco,26011,Treviso,TV,Veneto,5,31030
+Castelfranco Veneto,26012,Treviso,TV,Veneto,5,31033
+Castello di Godego,26013,Treviso,TV,Veneto,5,31030
+Cavaso del Tomba,26014,Treviso,TV,Veneto,5,31034
+Cessalto,26015,Treviso,TV,Veneto,5,31040
+Chiarano,26016,Treviso,TV,Veneto,5,31040
+Cimadolmo,26017,Treviso,TV,Veneto,5,31010
+Cison di Valmarino,26018,Treviso,TV,Veneto,5,31030
+Codognè,26019,Treviso,TV,Veneto,5,31013
+Colle Umberto,26020,Treviso,TV,Veneto,5,31014
+Conegliano,26021,Treviso,TV,Veneto,5,31015
+Cordignano,26022,Treviso,TV,Veneto,5,31016
+Cornuda,26023,Treviso,TV,Veneto,5,31041
+Crocetta del Montello,26025,Treviso,TV,Veneto,5,31035
+Farra di Soligo,26026,Treviso,TV,Veneto,5,31010
+Follina,26027,Treviso,TV,Veneto,5,31051
+Fontanelle,26028,Treviso,TV,Veneto,5,31043
+Fonte,26029,Treviso,TV,Veneto,5,31010
+Fregona,26030,Treviso,TV,Veneto,5,31010
+Gaiarine,26031,Treviso,TV,Veneto,5,31018
+Giavera del Montello,26032,Treviso,TV,Veneto,5,31040
+Godega di Sant'Urbano,26033,Treviso,TV,Veneto,5,31010
+Gorgo al Monticano,26034,Treviso,TV,Veneto,5,31040
+Istrana,26035,Treviso,TV,Veneto,5,31036
+Loria,26036,Treviso,TV,Veneto,5,31037
+Mansuè,26037,Treviso,TV,Veneto,5,31040
+Mareno di Piave,26038,Treviso,TV,Veneto,5,31010
+Maser,26039,Treviso,TV,Veneto,5,31010
+Maserada sul Piave,26040,Treviso,TV,Veneto,5,31052
+Meduna di Livenza,26041,Treviso,TV,Veneto,5,31040
+Miane,26042,Treviso,TV,Veneto,5,31050
+Mogliano Veneto,26043,Treviso,TV,Veneto,5,31021
+Monastier di Treviso,26044,Treviso,TV,Veneto,5,31050
+Monfumo,26045,Treviso,TV,Veneto,5,31010
+Montebelluna,26046,Treviso,TV,Veneto,5,31044
+Morgano,26047,Treviso,TV,Veneto,5,31050
+Moriago della Battaglia,26048,Treviso,TV,Veneto,5,31010
+Motta di Livenza,26049,Treviso,TV,Veneto,5,31045
+Nervesa della Battaglia,26050,Treviso,TV,Veneto,5,31040
+Oderzo,26051,Treviso,TV,Veneto,5,31046
+Ormelle,26052,Treviso,TV,Veneto,5,31024
+Orsago,26053,Treviso,TV,Veneto,5,31010
+Paese,26055,Treviso,TV,Veneto,5,31038
+Pederobba,26056,Treviso,TV,Veneto,5,31040
+Pieve di Soligo,26057,Treviso,TV,Veneto,5,31053
+Ponte di Piave,26058,Treviso,TV,Veneto,5,31047
+Ponzano Veneto,26059,Treviso,TV,Veneto,5,31050
+Portobuffolè,26060,Treviso,TV,Veneto,5,31040
+Possagno,26061,Treviso,TV,Veneto,5,31054
+Povegliano,26062,Treviso,TV,Veneto,5,31050
+Preganziol,26063,Treviso,TV,Veneto,5,31022
+Quinto di Treviso,26064,Treviso,TV,Veneto,5,31055
+Refrontolo,26065,Treviso,TV,Veneto,5,31020
+Resana,26066,Treviso,TV,Veneto,5,31023
+Revine Lago,26067,Treviso,TV,Veneto,5,31020
+Riese Pio X,26068,Treviso,TV,Veneto,5,31039
+Roncade,26069,Treviso,TV,Veneto,5,31056
+Salgareda,26070,Treviso,TV,Veneto,5,31040
+San Biagio di Callalta,26071,Treviso,TV,Veneto,5,31048
+San Fior,26072,Treviso,TV,Veneto,5,31020
+San Pietro di Feletto,26073,Treviso,TV,Veneto,5,31020
+San Polo di Piave,26074,Treviso,TV,Veneto,5,31020
+Santa Lucia di Piave,26075,Treviso,TV,Veneto,5,31025
+San Vendemiano,26076,Treviso,TV,Veneto,5,31020
+San Zenone degli Ezzelini,26077,Treviso,TV,Veneto,5,31020
+Sarmede,26078,Treviso,TV,Veneto,5,31026
+Segusino,26079,Treviso,TV,Veneto,5,31040
+Sernaglia della Battaglia,26080,Treviso,TV,Veneto,5,31020
+Silea,26081,Treviso,TV,Veneto,5,31057
+Spresiano,26082,Treviso,TV,Veneto,5,31027
+Susegana,26083,Treviso,TV,Veneto,5,31058
+Tarzo,26084,Treviso,TV,Veneto,5,31020
+Trevignano,26085,Treviso,TV,Veneto,5,31040
+Treviso,26086,Treviso,TV,Veneto,5,31100
+Valdobbiadene,26087,Treviso,TV,Veneto,5,31049
+Vazzola,26088,Treviso,TV,Veneto,5,31028
+Vedelago,26089,Treviso,TV,Veneto,5,31050
+Vidor,26090,Treviso,TV,Veneto,5,31020
+Villorba,26091,Treviso,TV,Veneto,5,31020
+Vittorio Veneto,26092,Treviso,TV,Veneto,5,31029
+Volpago del Montello,26093,Treviso,TV,Veneto,5,31040
+Zenson di Piave,26094,Treviso,TV,Veneto,5,31050
+Zero Branco,26095,Treviso,TV,Veneto,5,31059
+Pieve del Grappa,26096,Treviso,TV,Veneto,5,31017
+Annone Veneto,27001,Venezia,VE,Veneto,5,30020
+Campagna Lupia,27002,Venezia,VE,Veneto,5,30010
+Campolongo Maggiore,27003,Venezia,VE,Veneto,5,30010
+Camponogara,27004,Venezia,VE,Veneto,5,30010
+Caorle,27005,Venezia,VE,Veneto,5,30021
+Cavarzere,27006,Venezia,VE,Veneto,5,30014
+Ceggia,27007,Venezia,VE,Veneto,5,30022
+Chioggia,27008,Venezia,VE,Veneto,5,30015
+Cinto Caomaggiore,27009,Venezia,VE,Veneto,5,30020
+Cona,27010,Venezia,VE,Veneto,5,30010
+Concordia Sagittaria,27011,Venezia,VE,Veneto,5,30023
+Dolo,27012,Venezia,VE,Veneto,5,30031
+Eraclea,27013,Venezia,VE,Veneto,5,30020
+Fiesso d'Artico,27014,Venezia,VE,Veneto,5,30032
+Fossalta di Piave,27015,Venezia,VE,Veneto,5,30020
+Fossalta di Portogruaro,27016,Venezia,VE,Veneto,5,30025
+Fossò,27017,Venezia,VE,Veneto,5,30030
+Gruaro,27018,Venezia,VE,Veneto,5,30020
+Jesolo,27019,Venezia,VE,Veneto,5,30016
+Marcon,27020,Venezia,VE,Veneto,5,30020
+Martellago,27021,Venezia,VE,Veneto,5,30030
+Meolo,27022,Venezia,VE,Veneto,5,30020
+Mira,27023,Venezia,VE,Veneto,5,30034
+Mirano,27024,Venezia,VE,Veneto,5,30035
+Musile di Piave,27025,Venezia,VE,Veneto,5,30024
+Noale,27026,Venezia,VE,Veneto,5,30033
+Noventa di Piave,27027,Venezia,VE,Veneto,5,30020
+Pianiga,27028,Venezia,VE,Veneto,5,30030
+Portogruaro,27029,Venezia,VE,Veneto,5,30026
+Pramaggiore,27030,Venezia,VE,Veneto,5,30020
+Quarto d'Altino,27031,Venezia,VE,Veneto,5,30020
+Salzano,27032,Venezia,VE,Veneto,5,30030
+San Donà di Piave,27033,Venezia,VE,Veneto,5,30027
+San Michele al Tagliamento,27034,Venezia,VE,Veneto,5,30028
+Santa Maria di Sala,27035,Venezia,VE,Veneto,5,30036
+San Stino di Livenza,27036,Venezia,VE,Veneto,5,30029
+Scorzè,27037,Venezia,VE,Veneto,5,30037
+Spinea,27038,Venezia,VE,Veneto,5,30038
+Stra,27039,Venezia,VE,Veneto,5,30039
+Teglio Veneto,27040,Venezia,VE,Veneto,5,30025
+Torre di Mosto,27041,Venezia,VE,Veneto,5,30020
+Venezia,27042,Venezia,VE,Veneto,5,30100
+Vigonovo,27043,Venezia,VE,Veneto,5,30030
+Cavallino-Treporti,27044,Venezia,VE,Veneto,5,30013
+Abano Terme,28001,Padova,PD,Veneto,5,35031
+Agna,28002,Padova,PD,Veneto,5,35021
+Albignasego,28003,Padova,PD,Veneto,5,35020
+Anguillara Veneta,28004,Padova,PD,Veneto,5,35022
+Arquà Petrarca,28005,Padova,PD,Veneto,5,35032
+Arre,28006,Padova,PD,Veneto,5,35020
+Arzergrande,28007,Padova,PD,Veneto,5,35020
+Bagnoli di Sopra,28008,Padova,PD,Veneto,5,35023
+Baone,28009,Padova,PD,Veneto,5,35030
+Barbona,28010,Padova,PD,Veneto,5,35040
+Battaglia Terme,28011,Padova,PD,Veneto,5,35041
+Boara Pisani,28012,Padova,PD,Veneto,5,35040
+Borgoricco,28013,Padova,PD,Veneto,5,35010
+Bovolenta,28014,Padova,PD,Veneto,5,35024
+Brugine,28015,Padova,PD,Veneto,5,35020
+Cadoneghe,28016,Padova,PD,Veneto,5,35010
+Campodarsego,28017,Padova,PD,Veneto,5,35011
+Campodoro,28018,Padova,PD,Veneto,5,35010
+Camposampiero,28019,Padova,PD,Veneto,5,35012
+Campo San Martino,28020,Padova,PD,Veneto,5,35010
+Candiana,28021,Padova,PD,Veneto,5,35020
+Carceri,28022,Padova,PD,Veneto,5,35040
+Carmignano di Brenta,28023,Padova,PD,Veneto,5,35010
+Cartura,28026,Padova,PD,Veneto,5,35025
+Casale di Scodosia,28027,Padova,PD,Veneto,5,35040
+Casalserugo,28028,Padova,PD,Veneto,5,35020
+Castelbaldo,28029,Padova,PD,Veneto,5,35040
+Cervarese Santa Croce,28030,Padova,PD,Veneto,5,35030
+Cinto Euganeo,28031,Padova,PD,Veneto,5,35030
+Cittadella,28032,Padova,PD,Veneto,5,35013
+Codevigo,28033,Padova,PD,Veneto,5,35020
+Conselve,28034,Padova,PD,Veneto,5,35026
+Correzzola,28035,Padova,PD,Veneto,5,35020
+Curtarolo,28036,Padova,PD,Veneto,5,35010
+Este,28037,Padova,PD,Veneto,5,35042
+Fontaniva,28038,Padova,PD,Veneto,5,35014
+Galliera Veneta,28039,Padova,PD,Veneto,5,35015
+Galzignano Terme,28040,Padova,PD,Veneto,5,35030
+Gazzo,28041,Padova,PD,Veneto,5,35010
+Grantorto,28042,Padova,PD,Veneto,5,35010
+Granze,28043,Padova,PD,Veneto,5,35040
+Legnaro,28044,Padova,PD,Veneto,5,35020
+Limena,28045,Padova,PD,Veneto,5,35010
+Loreggia,28046,Padova,PD,Veneto,5,35010
+Lozzo Atestino,28047,Padova,PD,Veneto,5,35034
+Maserà di Padova,28048,Padova,PD,Veneto,5,35020
+Masi,28049,Padova,PD,Veneto,5,35040
+Massanzago,28050,Padova,PD,Veneto,5,35010
+Megliadino San Vitale,28052,Padova,PD,Veneto,5,35040
+Merlara,28053,Padova,PD,Veneto,5,35040
+Mestrino,28054,Padova,PD,Veneto,5,35035
+Monselice,28055,Padova,PD,Veneto,5,35043
+Montagnana,28056,Padova,PD,Veneto,5,35044
+Montegrotto Terme,28057,Padova,PD,Veneto,5,35036
+Noventa Padovana,28058,Padova,PD,Veneto,5,35027
+Ospedaletto Euganeo,28059,Padova,PD,Veneto,5,35045
+Padova,28060,Padova,PD,Veneto,5,35122
+Pernumia,28061,Padova,PD,Veneto,5,35020
+Piacenza d'Adige,28062,Padova,PD,Veneto,5,35040
+Piazzola sul Brenta,28063,Padova,PD,Veneto,5,35016
+Piombino Dese,28064,Padova,PD,Veneto,5,35017
+Piove di Sacco,28065,Padova,PD,Veneto,5,35028
+Polverara,28066,Padova,PD,Veneto,5,35020
+Ponso,28067,Padova,PD,Veneto,5,35040
+Pontelongo,28068,Padova,PD,Veneto,5,35029
+Ponte San Nicolò,28069,Padova,PD,Veneto,5,35020
+Pozzonovo,28070,Padova,PD,Veneto,5,35020
+Rovolon,28071,Padova,PD,Veneto,5,35030
+Rubano,28072,Padova,PD,Veneto,5,35030
+Saccolongo,28073,Padova,PD,Veneto,5,35030
+San Giorgio delle Pertiche,28075,Padova,PD,Veneto,5,35010
+San Giorgio in Bosco,28076,Padova,PD,Veneto,5,35010
+San Martino di Lupari,28077,Padova,PD,Veneto,5,35018
+San Pietro in Gu,28078,Padova,PD,Veneto,5,35010
+San Pietro Viminario,28079,Padova,PD,Veneto,5,35020
+Santa Giustina in Colle,28080,Padova,PD,Veneto,5,35010
+Sant'Angelo di Piove di Sacco,28082,Padova,PD,Veneto,5,35020
+Sant'Elena,28083,Padova,PD,Veneto,5,35040
+Sant'Urbano,28084,Padova,PD,Veneto,5,35040
+Saonara,28085,Padova,PD,Veneto,5,35020
+Selvazzano Dentro,28086,Padova,PD,Veneto,5,35030
+Solesino,28087,Padova,PD,Veneto,5,35047
+Stanghella,28088,Padova,PD,Veneto,5,35048
+Teolo,28089,Padova,PD,Veneto,5,35037
+Terrassa Padovana,28090,Padova,PD,Veneto,5,35020
+Tombolo,28091,Padova,PD,Veneto,5,35019
+Torreglia,28092,Padova,PD,Veneto,5,35038
+Trebaseleghe,28093,Padova,PD,Veneto,5,35010
+Tribano,28094,Padova,PD,Veneto,5,35020
+Urbana,28095,Padova,PD,Veneto,5,35040
+Veggiano,28096,Padova,PD,Veneto,5,35030
+Vescovana,28097,Padova,PD,Veneto,5,35040
+Vighizzolo d'Este,28098,Padova,PD,Veneto,5,35040
+Vigodarzere,28099,Padova,PD,Veneto,5,35010
+Vigonza,28100,Padova,PD,Veneto,5,35010
+Villa del Conte,28101,Padova,PD,Veneto,5,35010
+Villa Estense,28102,Padova,PD,Veneto,5,35040
+Villafranca Padovana,28103,Padova,PD,Veneto,5,35010
+Villanova di Camposampiero,28104,Padova,PD,Veneto,5,35010
+Vo',28105,Padova,PD,Veneto,5,35030
+Due Carrare,28106,Padova,PD,Veneto,5,35020
+Borgo Veneto,28107,Padova,PD,Veneto,5,35046
+Adria,29001,Rovigo,RO,Veneto,5,45011
+Ariano nel Polesine,29002,Rovigo,RO,Veneto,5,45012
+Arquà Polesine,29003,Rovigo,RO,Veneto,5,45031
+Badia Polesine,29004,Rovigo,RO,Veneto,5,45021
+Bagnolo di Po,29005,Rovigo,RO,Veneto,5,45022
+Bergantino,29006,Rovigo,RO,Veneto,5,45032
+Bosaro,29007,Rovigo,RO,Veneto,5,45033
+Calto,29008,Rovigo,RO,Veneto,5,45030
+Canaro,29009,Rovigo,RO,Veneto,5,45034
+Canda,29010,Rovigo,RO,Veneto,5,45020
+Castelguglielmo,29011,Rovigo,RO,Veneto,5,45020
+Castelmassa,29012,Rovigo,RO,Veneto,5,45035
+Castelnovo Bariano,29013,Rovigo,RO,Veneto,5,45030
+Ceneselli,29014,Rovigo,RO,Veneto,5,45030
+Ceregnano,29015,Rovigo,RO,Veneto,5,45010
+Corbola,29017,Rovigo,RO,Veneto,5,45015
+Costa di Rovigo,29018,Rovigo,RO,Veneto,5,45023
+Crespino,29019,Rovigo,RO,Veneto,5,45030
+Ficarolo,29021,Rovigo,RO,Veneto,5,45036
+Fiesso Umbertiano,29022,Rovigo,RO,Veneto,5,45024
+Frassinelle Polesine,29023,Rovigo,RO,Veneto,5,45030
+Fratta Polesine,29024,Rovigo,RO,Veneto,5,45025
+Gaiba,29025,Rovigo,RO,Veneto,5,45030
+Gavello,29026,Rovigo,RO,Veneto,5,45010
+Giacciano con Baruchella,29027,Rovigo,RO,Veneto,5,45020
+Guarda Veneta,29028,Rovigo,RO,Veneto,5,45030
+Lendinara,29029,Rovigo,RO,Veneto,5,45026
+Loreo,29030,Rovigo,RO,Veneto,5,45017
+Lusia,29031,Rovigo,RO,Veneto,5,45020
+Melara,29032,Rovigo,RO,Veneto,5,45037
+Occhiobello,29033,Rovigo,RO,Veneto,5,45030
+Papozze,29034,Rovigo,RO,Veneto,5,45010
+Pettorazza Grimani,29035,Rovigo,RO,Veneto,5,45010
+Pincara,29036,Rovigo,RO,Veneto,5,45020
+Polesella,29037,Rovigo,RO,Veneto,5,45038
+Pontecchio Polesine,29038,Rovigo,RO,Veneto,5,45030
+Porto Tolle,29039,Rovigo,RO,Veneto,5,45018
+Rosolina,29040,Rovigo,RO,Veneto,5,45010
+Rovigo,29041,Rovigo,RO,Veneto,5,45100
+Salara,29042,Rovigo,RO,Veneto,5,45030
+San Bellino,29043,Rovigo,RO,Veneto,5,45020
+San Martino di Venezze,29044,Rovigo,RO,Veneto,5,45030
+Stienta,29045,Rovigo,RO,Veneto,5,45039
+Taglio di Po,29046,Rovigo,RO,Veneto,5,45019
+Trecenta,29047,Rovigo,RO,Veneto,5,45027
+Villadose,29048,Rovigo,RO,Veneto,5,45010
+Villamarzana,29049,Rovigo,RO,Veneto,5,45030
+Villanova del Ghebbo,29050,Rovigo,RO,Veneto,5,45020
+Villanova Marchesana,29051,Rovigo,RO,Veneto,5,45030
+Porto Viro,29052,Rovigo,RO,Veneto,5,45014
+Aiello del Friuli,30001,Udine,UD,Friuli-Venezia Giulia,6,33041
+Amaro,30002,Udine,UD,Friuli-Venezia Giulia,6,33020
+Ampezzo,30003,Udine,UD,Friuli-Venezia Giulia,6,33021
+Aquileia,30004,Udine,UD,Friuli-Venezia Giulia,6,33051
+Arta Terme,30005,Udine,UD,Friuli-Venezia Giulia,6,33022
+Artegna,30006,Udine,UD,Friuli-Venezia Giulia,6,33011
+Attimis,30007,Udine,UD,Friuli-Venezia Giulia,6,33040
+Bagnaria Arsa,30008,Udine,UD,Friuli-Venezia Giulia,6,33050
+Basiliano,30009,Udine,UD,Friuli-Venezia Giulia,6,33031
+Bertiolo,30010,Udine,UD,Friuli-Venezia Giulia,6,33032
+Bicinicco,30011,Udine,UD,Friuli-Venezia Giulia,6,33050
+Bordano,30012,Udine,UD,Friuli-Venezia Giulia,6,33010
+Buja,30013,Udine,UD,Friuli-Venezia Giulia,6,33030
+Buttrio,30014,Udine,UD,Friuli-Venezia Giulia,6,33042
+Camino al Tagliamento,30015,Udine,UD,Friuli-Venezia Giulia,6,33030
+Campoformido,30016,Udine,UD,Friuli-Venezia Giulia,6,33030
+Carlino,30018,Udine,UD,Friuli-Venezia Giulia,6,33050
+Cassacco,30019,Udine,UD,Friuli-Venezia Giulia,6,33010
+Castions di Strada,30020,Udine,UD,Friuli-Venezia Giulia,6,33050
+Cavazzo Carnico,30021,Udine,UD,Friuli-Venezia Giulia,6,33020
+Cercivento,30022,Udine,UD,Friuli-Venezia Giulia,6,33020
+Cervignano del Friuli,30023,Udine,UD,Friuli-Venezia Giulia,6,33052
+Chiopris-Viscone,30024,Udine,UD,Friuli-Venezia Giulia,6,33048
+Chiusaforte,30025,Udine,UD,Friuli-Venezia Giulia,6,33010
+Cividale del Friuli,30026,Udine,UD,Friuli-Venezia Giulia,6,33043
+Codroipo,30027,Udine,UD,Friuli-Venezia Giulia,6,33033
+Colloredo di Monte Albano,30028,Udine,UD,Friuli-Venezia Giulia,6,33010
+Comeglians,30029,Udine,UD,Friuli-Venezia Giulia,6,33023
+Corno di Rosazzo,30030,Udine,UD,Friuli-Venezia Giulia,6,33040
+Coseano,30031,Udine,UD,Friuli-Venezia Giulia,6,33030
+Dignano,30032,Udine,UD,Friuli-Venezia Giulia,6,33030
+Dogna,30033,Udine,UD,Friuli-Venezia Giulia,6,33010
+Drenchia,30034,Udine,UD,Friuli-Venezia Giulia,6,33040
+Enemonzo,30035,Udine,UD,Friuli-Venezia Giulia,6,33020
+Faedis,30036,Udine,UD,Friuli-Venezia Giulia,6,33040
+Fagagna,30037,Udine,UD,Friuli-Venezia Giulia,6,33034
+Flaibano,30039,Udine,UD,Friuli-Venezia Giulia,6,33030
+Forni Avoltri,30040,Udine,UD,Friuli-Venezia Giulia,6,33020
+Forni di Sopra,30041,Udine,UD,Friuli-Venezia Giulia,6,33024
+Forni di Sotto,30042,Udine,UD,Friuli-Venezia Giulia,6,33020
+Gemona del Friuli,30043,Udine,UD,Friuli-Venezia Giulia,6,33013
+Gonars,30044,Udine,UD,Friuli-Venezia Giulia,6,33050
+Grimacco,30045,Udine,UD,Friuli-Venezia Giulia,6,33040
+Latisana,30046,Udine,UD,Friuli-Venezia Giulia,6,33053
+Lauco,30047,Udine,UD,Friuli-Venezia Giulia,6,33029
+Lestizza,30048,Udine,UD,Friuli-Venezia Giulia,6,33050
+Lignano Sabbiadoro,30049,Udine,UD,Friuli-Venezia Giulia,6,33054
+Lusevera,30051,Udine,UD,Friuli-Venezia Giulia,6,33010
+Magnano in Riviera,30052,Udine,UD,Friuli-Venezia Giulia,6,33010
+Majano,30053,Udine,UD,Friuli-Venezia Giulia,6,33030
+Malborghetto Valbruna,30054,Udine,UD,Friuli-Venezia Giulia,6,33010
+Manzano,30055,Udine,UD,Friuli-Venezia Giulia,6,33044
+Marano Lagunare,30056,Udine,UD,Friuli-Venezia Giulia,6,33050
+Martignacco,30057,Udine,UD,Friuli-Venezia Giulia,6,33035
+Mereto di Tomba,30058,Udine,UD,Friuli-Venezia Giulia,6,33036
+Moggio Udinese,30059,Udine,UD,Friuli-Venezia Giulia,6,33015
+Moimacco,30060,Udine,UD,Friuli-Venezia Giulia,6,33040
+Montenars,30061,Udine,UD,Friuli-Venezia Giulia,6,33010
+Mortegliano,30062,Udine,UD,Friuli-Venezia Giulia,6,33050
+Moruzzo,30063,Udine,UD,Friuli-Venezia Giulia,6,33030
+Muzzana del Turgnano,30064,Udine,UD,Friuli-Venezia Giulia,6,33055
+Nimis,30065,Udine,UD,Friuli-Venezia Giulia,6,33045
+Osoppo,30066,Udine,UD,Friuli-Venezia Giulia,6,33010
+Ovaro,30067,Udine,UD,Friuli-Venezia Giulia,6,33025
+Pagnacco,30068,Udine,UD,Friuli-Venezia Giulia,6,33010
+Palazzolo dello Stella,30069,Udine,UD,Friuli-Venezia Giulia,6,33056
+Palmanova,30070,Udine,UD,Friuli-Venezia Giulia,6,33057
+Paluzza,30071,Udine,UD,Friuli-Venezia Giulia,6,33026
+Pasian di Prato,30072,Udine,UD,Friuli-Venezia Giulia,6,33037
+Paularo,30073,Udine,UD,Friuli-Venezia Giulia,6,33027
+Pavia di Udine,30074,Udine,UD,Friuli-Venezia Giulia,6,33050
+Pocenia,30075,Udine,UD,Friuli-Venezia Giulia,6,33050
+Pontebba,30076,Udine,UD,Friuli-Venezia Giulia,6,33016
+Porpetto,30077,Udine,UD,Friuli-Venezia Giulia,6,33050
+Povoletto,30078,Udine,UD,Friuli-Venezia Giulia,6,33040
+Pozzuolo del Friuli,30079,Udine,UD,Friuli-Venezia Giulia,6,33050
+Pradamano,30080,Udine,UD,Friuli-Venezia Giulia,6,33040
+Prato Carnico,30081,Udine,UD,Friuli-Venezia Giulia,6,33020
+Precenicco,30082,Udine,UD,Friuli-Venezia Giulia,6,33050
+Premariacco,30083,Udine,UD,Friuli-Venezia Giulia,6,33040
+Preone,30084,Udine,UD,Friuli-Venezia Giulia,6,33020
+Prepotto,30085,Udine,UD,Friuli-Venezia Giulia,6,33040
+Pulfero,30086,Udine,UD,Friuli-Venezia Giulia,6,33046
+Ragogna,30087,Udine,UD,Friuli-Venezia Giulia,6,33030
+Ravascletto,30088,Udine,UD,Friuli-Venezia Giulia,6,33020
+Raveo,30089,Udine,UD,Friuli-Venezia Giulia,6,33029
+Reana del Rojale,30090,Udine,UD,Friuli-Venezia Giulia,6,33010
+Remanzacco,30091,Udine,UD,Friuli-Venezia Giulia,6,33047
+Resia,30092,Udine,UD,Friuli-Venezia Giulia,6,33010
+Resiutta,30093,Udine,UD,Friuli-Venezia Giulia,6,33010
+Rigolato,30094,Udine,UD,Friuli-Venezia Giulia,6,33020
+Rive d'Arcano,30095,Udine,UD,Friuli-Venezia Giulia,6,33030
+Ronchis,30097,Udine,UD,Friuli-Venezia Giulia,6,33050
+Ruda,30098,Udine,UD,Friuli-Venezia Giulia,6,33050
+San Daniele del Friuli,30099,Udine,UD,Friuli-Venezia Giulia,6,33038
+San Giorgio di Nogaro,30100,Udine,UD,Friuli-Venezia Giulia,6,33058
+San Giovanni al Natisone,30101,Udine,UD,Friuli-Venezia Giulia,6,33048
+San Leonardo,30102,Udine,UD,Friuli-Venezia Giulia,6,33040
+San Pietro al Natisone,30103,Udine,UD,Friuli-Venezia Giulia,6,33049
+Santa Maria la Longa,30104,Udine,UD,Friuli-Venezia Giulia,6,33050
+San Vito al Torre,30105,Udine,UD,Friuli-Venezia Giulia,6,33050
+San Vito di Fagagna,30106,Udine,UD,Friuli-Venezia Giulia,6,33030
+Sauris,30107,Udine,UD,Friuli-Venezia Giulia,6,33020
+Savogna,30108,Udine,UD,Friuli-Venezia Giulia,6,33040
+Sedegliano,30109,Udine,UD,Friuli-Venezia Giulia,6,33039
+Socchieve,30110,Udine,UD,Friuli-Venezia Giulia,6,33020
+Stregna,30111,Udine,UD,Friuli-Venezia Giulia,6,33040
+Sutrio,30112,Udine,UD,Friuli-Venezia Giulia,6,33020
+Taipana,30113,Udine,UD,Friuli-Venezia Giulia,6,33040
+Talmassons,30114,Udine,UD,Friuli-Venezia Giulia,6,33030
+Tarcento,30116,Udine,UD,Friuli-Venezia Giulia,6,33017
+Tarvisio,30117,Udine,UD,Friuli-Venezia Giulia,6,33018
+Tavagnacco,30118,Udine,UD,Friuli-Venezia Giulia,6,33010
+Terzo d'Aquileia,30120,Udine,UD,Friuli-Venezia Giulia,6,33050
+Tolmezzo,30121,Udine,UD,Friuli-Venezia Giulia,6,33028
+Torreano,30122,Udine,UD,Friuli-Venezia Giulia,6,33040
+Torviscosa,30123,Udine,UD,Friuli-Venezia Giulia,6,33050
+Trasaghis,30124,Udine,UD,Friuli-Venezia Giulia,6,33010
+Treppo Grande,30126,Udine,UD,Friuli-Venezia Giulia,6,33010
+Tricesimo,30127,Udine,UD,Friuli-Venezia Giulia,6,33019
+Trivignano Udinese,30128,Udine,UD,Friuli-Venezia Giulia,6,33050
+Udine,30129,Udine,UD,Friuli-Venezia Giulia,6,33100
+Varmo,30130,Udine,UD,Friuli-Venezia Giulia,6,33030
+Venzone,30131,Udine,UD,Friuli-Venezia Giulia,6,33010
+Verzegnis,30132,Udine,UD,Friuli-Venezia Giulia,6,33020
+Villa Santina,30133,Udine,UD,Friuli-Venezia Giulia,6,33029
+Visco,30135,Udine,UD,Friuli-Venezia Giulia,6,33040
+Zuglio,30136,Udine,UD,Friuli-Venezia Giulia,6,33020
+Forgaria nel Friuli,30137,Udine,UD,Friuli-Venezia Giulia,6,33030
+Campolongo Tapogliano,30138,Udine,UD,Friuli-Venezia Giulia,6,33040
+Rivignano Teor,30188,Udine,UD,Friuli-Venezia Giulia,6,33061
+Sappada,30189,Udine,UD,Friuli-Venezia Giulia,6,33012
+Fiumicello Villa Vicentina,30190,Udine,UD,Friuli-Venezia Giulia,6,33050
+Treppo Ligosullo,30191,Udine,UD,Friuli-Venezia Giulia,6,33014
+Capriva del Friuli,31001,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Cormons,31002,Gorizia,GO,Friuli-Venezia Giulia,6,34071
+Doberdò del Lago,31003,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Dolegna del Collio,31004,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Farra d'Isonzo,31005,Gorizia,GO,Friuli-Venezia Giulia,6,34072
+Fogliano Redipuglia,31006,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Gorizia,31007,Gorizia,GO,Friuli-Venezia Giulia,6,34170
+Gradisca d'Isonzo,31008,Gorizia,GO,Friuli-Venezia Giulia,6,34072
+Grado,31009,Gorizia,GO,Friuli-Venezia Giulia,6,34073
+Mariano del Friuli,31010,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Medea,31011,Gorizia,GO,Friuli-Venezia Giulia,6,34076
+Monfalcone,31012,Gorizia,GO,Friuli-Venezia Giulia,6,34074
+Moraro,31013,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Mossa,31014,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Romans d'Isonzo,31015,Gorizia,GO,Friuli-Venezia Giulia,6,34076
+Ronchi dei Legionari,31016,Gorizia,GO,Friuli-Venezia Giulia,6,34077
+Sagrado,31017,Gorizia,GO,Friuli-Venezia Giulia,6,34078
+San Canzian d'Isonzo,31018,Gorizia,GO,Friuli-Venezia Giulia,6,34075
+San Floriano del Collio,31019,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+San Lorenzo Isontino,31020,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+San Pier d'Isonzo,31021,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Savogna d'Isonzo,31022,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Staranzano,31023,Gorizia,GO,Friuli-Venezia Giulia,6,34079
+Turriaco,31024,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Villesse,31025,Gorizia,GO,Friuli-Venezia Giulia,6,34070
+Duino Aurisina,32001,Trieste,TS,Friuli-Venezia Giulia,6,34011
+Monrupino,32002,Trieste,TS,Friuli-Venezia Giulia,6,34016
+Muggia,32003,Trieste,TS,Friuli-Venezia Giulia,6,34015
+San Dorligo della Valle,32004,Trieste,TS,Friuli-Venezia Giulia,6,34018
+Sgonico,32005,Trieste,TS,Friuli-Venezia Giulia,6,34010
+Trieste,32006,Trieste,TS,Friuli-Venezia Giulia,6,34121
+Andreis,93001,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Arba,93002,Pordenone,PN,Friuli-Venezia Giulia,6,33090
+Aviano,93004,Pordenone,PN,Friuli-Venezia Giulia,6,33081
+Azzano Decimo,93005,Pordenone,PN,Friuli-Venezia Giulia,6,33082
+Barcis,93006,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Brugnera,93007,Pordenone,PN,Friuli-Venezia Giulia,6,33070
+Budoia,93008,Pordenone,PN,Friuli-Venezia Giulia,6,33070
+Caneva,93009,Pordenone,PN,Friuli-Venezia Giulia,6,33070
+Casarsa della Delizia,93010,Pordenone,PN,Friuli-Venezia Giulia,6,33072
+Castelnovo del Friuli,93011,Pordenone,PN,Friuli-Venezia Giulia,6,33090
+Cavasso Nuovo,93012,Pordenone,PN,Friuli-Venezia Giulia,6,33092
+Chions,93013,Pordenone,PN,Friuli-Venezia Giulia,6,33083
+Cimolais,93014,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Claut,93015,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Clauzetto,93016,Pordenone,PN,Friuli-Venezia Giulia,6,33090
+Cordenons,93017,Pordenone,PN,Friuli-Venezia Giulia,6,33084
+Cordovado,93018,Pordenone,PN,Friuli-Venezia Giulia,6,33075
+Erto e Casso,93019,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Fanna,93020,Pordenone,PN,Friuli-Venezia Giulia,6,33092
+Fiume Veneto,93021,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Fontanafredda,93022,Pordenone,PN,Friuli-Venezia Giulia,6,33074
+Frisanco,93024,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Maniago,93025,Pordenone,PN,Friuli-Venezia Giulia,6,33085
+Meduno,93026,Pordenone,PN,Friuli-Venezia Giulia,6,33092
+Montereale Valcellina,93027,Pordenone,PN,Friuli-Venezia Giulia,6,33086
+Morsano al Tagliamento,93028,Pordenone,PN,Friuli-Venezia Giulia,6,33075
+Pasiano di Pordenone,93029,Pordenone,PN,Friuli-Venezia Giulia,6,33087
+Pinzano al Tagliamento,93030,Pordenone,PN,Friuli-Venezia Giulia,6,33094
+Polcenigo,93031,Pordenone,PN,Friuli-Venezia Giulia,6,33070
+Porcia,93032,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Pordenone,93033,Pordenone,PN,Friuli-Venezia Giulia,6,33170
+Prata di Pordenone,93034,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Pravisdomini,93035,Pordenone,PN,Friuli-Venezia Giulia,6,33076
+Roveredo in Piano,93036,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Sacile,93037,Pordenone,PN,Friuli-Venezia Giulia,6,33077
+San Giorgio della Richinvelda,93038,Pordenone,PN,Friuli-Venezia Giulia,6,33095
+San Martino al Tagliamento,93039,Pordenone,PN,Friuli-Venezia Giulia,6,33098
+San Quirino,93040,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+San Vito al Tagliamento,93041,Pordenone,PN,Friuli-Venezia Giulia,6,33078
+Sequals,93042,Pordenone,PN,Friuli-Venezia Giulia,6,33090
+Sesto al Reghena,93043,Pordenone,PN,Friuli-Venezia Giulia,6,33079
+Spilimbergo,93044,Pordenone,PN,Friuli-Venezia Giulia,6,33097
+Tramonti di Sopra,93045,Pordenone,PN,Friuli-Venezia Giulia,6,33090
+Tramonti di Sotto,93046,Pordenone,PN,Friuli-Venezia Giulia,6,33090
+Travesio,93047,Pordenone,PN,Friuli-Venezia Giulia,6,33090
+Vito d'Asio,93049,Pordenone,PN,Friuli-Venezia Giulia,6,33090
+Vivaro,93050,Pordenone,PN,Friuli-Venezia Giulia,6,33099
+Zoppola,93051,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Vajont,93052,Pordenone,PN,Friuli-Venezia Giulia,6,33080
+Valvasone Arzene,93053,Pordenone,PN,Friuli-Venezia Giulia,6,33098
+Airole,8001,Imperia,IM,Liguria,7,18030
+Apricale,8002,Imperia,IM,Liguria,7,18035
+Aquila d'Arroscia,8003,Imperia,IM,Liguria,7,18020
+Armo,8004,Imperia,IM,Liguria,7,18026
+Aurigo,8005,Imperia,IM,Liguria,7,18020
+Badalucco,8006,Imperia,IM,Liguria,7,18010
+Bajardo,8007,Imperia,IM,Liguria,7,18031
+Bordighera,8008,Imperia,IM,Liguria,7,18012
+Borghetto d'Arroscia,8009,Imperia,IM,Liguria,7,18020
+Borgomaro,8010,Imperia,IM,Liguria,7,18021
+Camporosso,8011,Imperia,IM,Liguria,7,18033
+Caravonica,8012,Imperia,IM,Liguria,7,18020
+Castellaro,8014,Imperia,IM,Liguria,7,18011
+Castel Vittorio,8015,Imperia,IM,Liguria,7,18030
+Ceriana,8016,Imperia,IM,Liguria,7,18034
+Cervo,8017,Imperia,IM,Liguria,7,18010
+Cesio,8018,Imperia,IM,Liguria,7,18022
+Chiusanico,8019,Imperia,IM,Liguria,7,18027
+Chiusavecchia,8020,Imperia,IM,Liguria,7,18027
+Cipressa,8021,Imperia,IM,Liguria,7,18017
+Civezza,8022,Imperia,IM,Liguria,7,18017
+Cosio d'Arroscia,8023,Imperia,IM,Liguria,7,18023
+Costarainera,8024,Imperia,IM,Liguria,7,18017
+Diano Arentino,8025,Imperia,IM,Liguria,7,18013
+Diano Castello,8026,Imperia,IM,Liguria,7,18013
+Diano Marina,8027,Imperia,IM,Liguria,7,18013
+Diano San Pietro,8028,Imperia,IM,Liguria,7,18013
+Dolceacqua,8029,Imperia,IM,Liguria,7,18035
+Dolcedo,8030,Imperia,IM,Liguria,7,18020
+Imperia,8031,Imperia,IM,Liguria,7,18100
+Isolabona,8032,Imperia,IM,Liguria,7,18035
+Lucinasco,8033,Imperia,IM,Liguria,7,18020
+Mendatica,8034,Imperia,IM,Liguria,7,18025
+Molini di Triora,8035,Imperia,IM,Liguria,7,18010
+Montegrosso Pian Latte,8037,Imperia,IM,Liguria,7,18025
+Olivetta San Michele,8038,Imperia,IM,Liguria,7,18030
+Ospedaletti,8039,Imperia,IM,Liguria,7,18014
+Perinaldo,8040,Imperia,IM,Liguria,7,18032
+Pietrabruna,8041,Imperia,IM,Liguria,7,18010
+Pieve di Teco,8042,Imperia,IM,Liguria,7,18026
+Pigna,8043,Imperia,IM,Liguria,7,18037
+Pompeiana,8044,Imperia,IM,Liguria,7,18015
+Pontedassio,8045,Imperia,IM,Liguria,7,18027
+Pornassio,8046,Imperia,IM,Liguria,7,18024
+Prelà,8047,Imperia,IM,Liguria,7,18020
+Ranzo,8048,Imperia,IM,Liguria,7,18020
+Rezzo,8049,Imperia,IM,Liguria,7,18026
+Riva Ligure,8050,Imperia,IM,Liguria,7,18015
+Rocchetta Nervina,8051,Imperia,IM,Liguria,7,18030
+San Bartolomeo al Mare,8052,Imperia,IM,Liguria,7,18016
+San Biagio della Cima,8053,Imperia,IM,Liguria,7,18036
+San Lorenzo al Mare,8054,Imperia,IM,Liguria,7,18017
+Sanremo,8055,Imperia,IM,Liguria,7,18038
+Santo Stefano al Mare,8056,Imperia,IM,Liguria,7,18010
+Seborga,8057,Imperia,IM,Liguria,7,18012
+Soldano,8058,Imperia,IM,Liguria,7,18036
+Taggia,8059,Imperia,IM,Liguria,7,18018
+Terzorio,8060,Imperia,IM,Liguria,7,18010
+Triora,8061,Imperia,IM,Liguria,7,18010
+Vallebona,8062,Imperia,IM,Liguria,7,18012
+Vallecrosia,8063,Imperia,IM,Liguria,7,18019
+Vasia,8064,Imperia,IM,Liguria,7,18020
+Ventimiglia,8065,Imperia,IM,Liguria,7,18039
+Vessalico,8066,Imperia,IM,Liguria,7,18026
+Villa Faraldi,8067,Imperia,IM,Liguria,7,18010
+Montalto Carpasio,8068,Imperia,IM,Liguria,7,18010
+Alassio,9001,Savona,SV,Liguria,7,17021
+Albenga,9002,Savona,SV,Liguria,7,17031
+Albissola Marina,9003,Savona,SV,Liguria,7,17012
+Albisola Superiore,9004,Savona,SV,Liguria,7,17011
+Altare,9005,Savona,SV,Liguria,7,17041
+Andora,9006,Savona,SV,Liguria,7,17051
+Arnasco,9007,Savona,SV,Liguria,7,17032
+Balestrino,9008,Savona,SV,Liguria,7,17020
+Bardineto,9009,Savona,SV,Liguria,7,17057
+Bergeggi,9010,Savona,SV,Liguria,7,17028
+Boissano,9011,Savona,SV,Liguria,7,17054
+Borghetto Santo Spirito,9012,Savona,SV,Liguria,7,17052
+Borgio Verezzi,9013,Savona,SV,Liguria,7,17022
+Bormida,9014,Savona,SV,Liguria,7,17045
+Cairo Montenotte,9015,Savona,SV,Liguria,7,17014
+Calice Ligure,9016,Savona,SV,Liguria,7,17020
+Calizzano,9017,Savona,SV,Liguria,7,17057
+Carcare,9018,Savona,SV,Liguria,7,17043
+Casanova Lerrone,9019,Savona,SV,Liguria,7,17033
+Castelbianco,9020,Savona,SV,Liguria,7,17030
+Castelvecchio di Rocca Barbena,9021,Savona,SV,Liguria,7,17034
+Celle Ligure,9022,Savona,SV,Liguria,7,17015
+Cengio,9023,Savona,SV,Liguria,7,17056
+Ceriale,9024,Savona,SV,Liguria,7,17023
+Cisano sul Neva,9025,Savona,SV,Liguria,7,17035
+Cosseria,9026,Savona,SV,Liguria,7,17017
+Dego,9027,Savona,SV,Liguria,7,17058
+Erli,9028,Savona,SV,Liguria,7,17030
+Finale Ligure,9029,Savona,SV,Liguria,7,17024
+Garlenda,9030,Savona,SV,Liguria,7,17033
+Giustenice,9031,Savona,SV,Liguria,7,17027
+Giusvalla,9032,Savona,SV,Liguria,7,17010
+Laigueglia,9033,Savona,SV,Liguria,7,17053
+Loano,9034,Savona,SV,Liguria,7,17025
+Magliolo,9035,Savona,SV,Liguria,7,17020
+Mallare,9036,Savona,SV,Liguria,7,17045
+Massimino,9037,Savona,SV,Liguria,7,12071
+Millesimo,9038,Savona,SV,Liguria,7,17017
+Mioglia,9039,Savona,SV,Liguria,7,17040
+Murialdo,9040,Savona,SV,Liguria,7,17013
+Nasino,9041,Savona,SV,Liguria,7,17030
+Noli,9042,Savona,SV,Liguria,7,17026
+Onzo,9043,Savona,SV,Liguria,7,17037
+Orco Feglino,9044,Savona,SV,Liguria,7,17024
+Ortovero,9045,Savona,SV,Liguria,7,17037
+Osiglia,9046,Savona,SV,Liguria,7,17010
+Pallare,9047,Savona,SV,Liguria,7,17043
+Piana Crixia,9048,Savona,SV,Liguria,7,17058
+Pietra Ligure,9049,Savona,SV,Liguria,7,17027
+Plodio,9050,Savona,SV,Liguria,7,17043
+Pontinvrea,9051,Savona,SV,Liguria,7,17042
+Quiliano,9052,Savona,SV,Liguria,7,17047
+Rialto,9053,Savona,SV,Liguria,7,17020
+Roccavignale,9054,Savona,SV,Liguria,7,17017
+Sassello,9055,Savona,SV,Liguria,7,17046
+Savona,9056,Savona,SV,Liguria,7,17100
+Spotorno,9057,Savona,SV,Liguria,7,17028
+Stella,9058,Savona,SV,Liguria,7,17044
+Stellanello,9059,Savona,SV,Liguria,7,17020
+Testico,9060,Savona,SV,Liguria,7,17020
+Toirano,9061,Savona,SV,Liguria,7,17055
+Tovo San Giacomo,9062,Savona,SV,Liguria,7,17020
+Urbe,9063,Savona,SV,Liguria,7,17048
+Vado Ligure,9064,Savona,SV,Liguria,7,17047
+Varazze,9065,Savona,SV,Liguria,7,17019
+Vendone,9066,Savona,SV,Liguria,7,17032
+Vezzi Portio,9067,Savona,SV,Liguria,7,17028
+Villanova d'Albenga,9068,Savona,SV,Liguria,7,17038
+Zuccarello,9069,Savona,SV,Liguria,7,17039
+Arenzano,10001,Genova,GE,Liguria,7,16011
+Avegno,10002,Genova,GE,Liguria,7,16030
+Bargagli,10003,Genova,GE,Liguria,7,16021
+Bogliasco,10004,Genova,GE,Liguria,7,16031
+Borzonasca,10005,Genova,GE,Liguria,7,16041
+Busalla,10006,Genova,GE,Liguria,7,16012
+Camogli,10007,Genova,GE,Liguria,7,16032
+Campo Ligure,10008,Genova,GE,Liguria,7,16013
+Campomorone,10009,Genova,GE,Liguria,7,16014
+Carasco,10010,Genova,GE,Liguria,7,16042
+Casarza Ligure,10011,Genova,GE,Liguria,7,16030
+Casella,10012,Genova,GE,Liguria,7,16015
+Castiglione Chiavarese,10013,Genova,GE,Liguria,7,16030
+Ceranesi,10014,Genova,GE,Liguria,7,16014
+Chiavari,10015,Genova,GE,Liguria,7,16043
+Cicagna,10016,Genova,GE,Liguria,7,16044
+Cogoleto,10017,Genova,GE,Liguria,7,16016
+Cogorno,10018,Genova,GE,Liguria,7,16030
+Coreglia Ligure,10019,Genova,GE,Liguria,7,16040
+Crocefieschi,10020,Genova,GE,Liguria,7,16010
+Davagna,10021,Genova,GE,Liguria,7,16022
+Fascia,10022,Genova,GE,Liguria,7,16020
+Favale di Malvaro,10023,Genova,GE,Liguria,7,16040
+Fontanigorda,10024,Genova,GE,Liguria,7,16023
+Genova,10025,Genova,GE,Liguria,7,16124
+Gorreto,10026,Genova,GE,Liguria,7,16020
+Isola del Cantone,10027,Genova,GE,Liguria,7,16017
+Lavagna,10028,Genova,GE,Liguria,7,16033
+Leivi,10029,Genova,GE,Liguria,7,16040
+Lorsica,10030,Genova,GE,Liguria,7,16045
+Lumarzo,10031,Genova,GE,Liguria,7,16024
+Masone,10032,Genova,GE,Liguria,7,16010
+Mele,10033,Genova,GE,Liguria,7,16010
+Mezzanego,10034,Genova,GE,Liguria,7,16046
+Mignanego,10035,Genova,GE,Liguria,7,16018
+Moconesi,10036,Genova,GE,Liguria,7,16047
+Moneglia,10037,Genova,GE,Liguria,7,16030
+Montebruno,10038,Genova,GE,Liguria,7,16025
+Montoggio,10039,Genova,GE,Liguria,7,16026
+Ne,10040,Genova,GE,Liguria,7,16040
+Neirone,10041,Genova,GE,Liguria,7,16040
+Orero,10042,Genova,GE,Liguria,7,16040
+Pieve Ligure,10043,Genova,GE,Liguria,7,16030
+Portofino,10044,Genova,GE,Liguria,7,16034
+Propata,10045,Genova,GE,Liguria,7,16027
+Rapallo,10046,Genova,GE,Liguria,7,16035
+Recco,10047,Genova,GE,Liguria,7,16036
+Rezzoaglio,10048,Genova,GE,Liguria,7,16048
+Ronco Scrivia,10049,Genova,GE,Liguria,7,16019
+Rondanina,10050,Genova,GE,Liguria,7,16025
+Rossiglione,10051,Genova,GE,Liguria,7,16010
+Rovegno,10052,Genova,GE,Liguria,7,16028
+San Colombano Certenoli,10053,Genova,GE,Liguria,7,16040
+Santa Margherita Ligure,10054,Genova,GE,Liguria,7,16038
+Sant'Olcese,10055,Genova,GE,Liguria,7,16010
+Santo Stefano d'Aveto,10056,Genova,GE,Liguria,7,16049
+Savignone,10057,Genova,GE,Liguria,7,16010
+Serra Riccò,10058,Genova,GE,Liguria,7,16010
+Sestri Levante,10059,Genova,GE,Liguria,7,16039
+Sori,10060,Genova,GE,Liguria,7,16030
+Tiglieto,10061,Genova,GE,Liguria,7,16010
+Torriglia,10062,Genova,GE,Liguria,7,16029
+Tribogna,10063,Genova,GE,Liguria,7,16030
+Uscio,10064,Genova,GE,Liguria,7,16030
+Valbrevenna,10065,Genova,GE,Liguria,7,16010
+Vobbia,10066,Genova,GE,Liguria,7,16010
+Zoagli,10067,Genova,GE,Liguria,7,16030
+Ameglia,11001,La Spezia,SP,Liguria,7,19031
+Arcola,11002,La Spezia,SP,Liguria,7,19021
+Beverino,11003,La Spezia,SP,Liguria,7,19020
+Bolano,11004,La Spezia,SP,Liguria,7,19020
+Bonassola,11005,La Spezia,SP,Liguria,7,19011
+Borghetto di Vara,11006,La Spezia,SP,Liguria,7,19020
+Brugnato,11007,La Spezia,SP,Liguria,7,19020
+Calice al Cornoviglio,11008,La Spezia,SP,Liguria,7,19020
+Carro,11009,La Spezia,SP,Liguria,7,19012
+Carrodano,11010,La Spezia,SP,Liguria,7,19020
+Castelnuovo Magra,11011,La Spezia,SP,Liguria,7,19033
+Deiva Marina,11012,La Spezia,SP,Liguria,7,19013
+Follo,11013,La Spezia,SP,Liguria,7,19020
+Framura,11014,La Spezia,SP,Liguria,7,19014
+La Spezia,11015,La Spezia,SP,Liguria,7,19124
+Lerici,11016,La Spezia,SP,Liguria,7,19032
+Levanto,11017,La Spezia,SP,Liguria,7,19015
+Maissana,11018,La Spezia,SP,Liguria,7,19010
+Monterosso al Mare,11019,La Spezia,SP,Liguria,7,19016
+Luni,11020,La Spezia,SP,Liguria,7,19034
+Pignone,11021,La Spezia,SP,Liguria,7,19020
+Portovenere,11022,La Spezia,SP,Liguria,7,19025
+Riccò del Golfo di Spezia,11023,La Spezia,SP,Liguria,7,19020
+Riomaggiore,11024,La Spezia,SP,Liguria,7,19017
+Rocchetta di Vara,11025,La Spezia,SP,Liguria,7,19020
+Santo Stefano di Magra,11026,La Spezia,SP,Liguria,7,19037
+Sarzana,11027,La Spezia,SP,Liguria,7,19038
+Sesta Godano,11028,La Spezia,SP,Liguria,7,19020
+Varese Ligure,11029,La Spezia,SP,Liguria,7,19028
+Vernazza,11030,La Spezia,SP,Liguria,7,19018
+Vezzano Ligure,11031,La Spezia,SP,Liguria,7,19020
+Zignago,11032,La Spezia,SP,Liguria,7,19020
+Agazzano,33001,Piacenza,PC,Emilia-Romagna,8,29010
+Alseno,33002,Piacenza,PC,Emilia-Romagna,8,29010
+Besenzone,33003,Piacenza,PC,Emilia-Romagna,8,29010
+Bettola,33004,Piacenza,PC,Emilia-Romagna,8,29021
+Bobbio,33005,Piacenza,PC,Emilia-Romagna,8,29022
+Borgonovo Val Tidone,33006,Piacenza,PC,Emilia-Romagna,8,29011
+Cadeo,33007,Piacenza,PC,Emilia-Romagna,8,29010
+Calendasco,33008,Piacenza,PC,Emilia-Romagna,8,29010
+Caorso,33010,Piacenza,PC,Emilia-Romagna,8,29012
+Carpaneto Piacentino,33011,Piacenza,PC,Emilia-Romagna,8,29013
+Castell'Arquato,33012,Piacenza,PC,Emilia-Romagna,8,29014
+Castel San Giovanni,33013,Piacenza,PC,Emilia-Romagna,8,29015
+Castelvetro Piacentino,33014,Piacenza,PC,Emilia-Romagna,8,29010
+Cerignale,33015,Piacenza,PC,Emilia-Romagna,8,29020
+Coli,33016,Piacenza,PC,Emilia-Romagna,8,29020
+Corte Brugnatella,33017,Piacenza,PC,Emilia-Romagna,8,29020
+Cortemaggiore,33018,Piacenza,PC,Emilia-Romagna,8,29016
+Farini,33019,Piacenza,PC,Emilia-Romagna,8,29023
+Ferriere,33020,Piacenza,PC,Emilia-Romagna,8,29024
+Fiorenzuola d'Arda,33021,Piacenza,PC,Emilia-Romagna,8,29017
+Gazzola,33022,Piacenza,PC,Emilia-Romagna,8,29010
+Gossolengo,33023,Piacenza,PC,Emilia-Romagna,8,29020
+Gragnano Trebbiense,33024,Piacenza,PC,Emilia-Romagna,8,29010
+Gropparello,33025,Piacenza,PC,Emilia-Romagna,8,29025
+Lugagnano Val d'Arda,33026,Piacenza,PC,Emilia-Romagna,8,29018
+Monticelli d'Ongina,33027,Piacenza,PC,Emilia-Romagna,8,29010
+Morfasso,33028,Piacenza,PC,Emilia-Romagna,8,29020
+Ottone,33030,Piacenza,PC,Emilia-Romagna,8,29026
+Piacenza,33032,Piacenza,PC,Emilia-Romagna,8,29120
+Pianello Val Tidone,33033,Piacenza,PC,Emilia-Romagna,8,29010
+Piozzano,33034,Piacenza,PC,Emilia-Romagna,8,29010
+Podenzano,33035,Piacenza,PC,Emilia-Romagna,8,29027
+Ponte dell'Olio,33036,Piacenza,PC,Emilia-Romagna,8,29028
+Pontenure,33037,Piacenza,PC,Emilia-Romagna,8,29010
+Rivergaro,33038,Piacenza,PC,Emilia-Romagna,8,29029
+Rottofreno,33039,Piacenza,PC,Emilia-Romagna,8,29010
+San Giorgio Piacentino,33040,Piacenza,PC,Emilia-Romagna,8,29019
+San Pietro in Cerro,33041,Piacenza,PC,Emilia-Romagna,8,29010
+Sarmato,33042,Piacenza,PC,Emilia-Romagna,8,29010
+Travo,33043,Piacenza,PC,Emilia-Romagna,8,29020
+Vernasca,33044,Piacenza,PC,Emilia-Romagna,8,29010
+Vigolzone,33045,Piacenza,PC,Emilia-Romagna,8,29020
+Villanova sull'Arda,33046,Piacenza,PC,Emilia-Romagna,8,29010
+Zerba,33047,Piacenza,PC,Emilia-Romagna,8,29020
+Ziano Piacentino,33048,Piacenza,PC,Emilia-Romagna,8,29010
+Alta Val Tidone,33049,Piacenza,PC,Emilia-Romagna,8,29010
+Albareto,34001,Parma,PR,Emilia-Romagna,8,43051
+Bardi,34002,Parma,PR,Emilia-Romagna,8,43032
+Bedonia,34003,Parma,PR,Emilia-Romagna,8,43041
+Berceto,34004,Parma,PR,Emilia-Romagna,8,43042
+Bore,34005,Parma,PR,Emilia-Romagna,8,43030
+Borgo Val di Taro,34006,Parma,PR,Emilia-Romagna,8,43043
+Busseto,34007,Parma,PR,Emilia-Romagna,8,43011
+Calestano,34008,Parma,PR,Emilia-Romagna,8,43030
+Collecchio,34009,Parma,PR,Emilia-Romagna,8,43044
+Colorno,34010,Parma,PR,Emilia-Romagna,8,43052
+Compiano,34011,Parma,PR,Emilia-Romagna,8,43053
+Corniglio,34012,Parma,PR,Emilia-Romagna,8,43021
+Felino,34013,Parma,PR,Emilia-Romagna,8,43035
+Fidenza,34014,Parma,PR,Emilia-Romagna,8,43036
+Fontanellato,34015,Parma,PR,Emilia-Romagna,8,43012
+Fontevivo,34016,Parma,PR,Emilia-Romagna,8,43010
+Fornovo di Taro,34017,Parma,PR,Emilia-Romagna,8,43045
+Langhirano,34018,Parma,PR,Emilia-Romagna,8,43013
+Lesignano de' Bagni,34019,Parma,PR,Emilia-Romagna,8,43037
+Medesano,34020,Parma,PR,Emilia-Romagna,8,43014
+Monchio delle Corti,34022,Parma,PR,Emilia-Romagna,8,43010
+Montechiarugolo,34023,Parma,PR,Emilia-Romagna,8,43022
+Neviano degli Arduini,34024,Parma,PR,Emilia-Romagna,8,43024
+Noceto,34025,Parma,PR,Emilia-Romagna,8,43015
+Palanzano,34026,Parma,PR,Emilia-Romagna,8,43025
+Parma,34027,Parma,PR,Emilia-Romagna,8,43121
+Pellegrino Parmense,34028,Parma,PR,Emilia-Romagna,8,43047
+Roccabianca,34030,Parma,PR,Emilia-Romagna,8,43010
+Sala Baganza,34031,Parma,PR,Emilia-Romagna,8,43038
+Salsomaggiore Terme,34032,Parma,PR,Emilia-Romagna,8,43039
+San Secondo Parmense,34033,Parma,PR,Emilia-Romagna,8,43017
+Solignano,34035,Parma,PR,Emilia-Romagna,8,43040
+Soragna,34036,Parma,PR,Emilia-Romagna,8,43019
+Terenzo,34038,Parma,PR,Emilia-Romagna,8,43040
+Tizzano Val Parma,34039,Parma,PR,Emilia-Romagna,8,43028
+Tornolo,34040,Parma,PR,Emilia-Romagna,8,43059
+Torrile,34041,Parma,PR,Emilia-Romagna,8,43056
+Traversetolo,34042,Parma,PR,Emilia-Romagna,8,43029
+Valmozzola,34044,Parma,PR,Emilia-Romagna,8,43050
+Varano de' Melegari,34045,Parma,PR,Emilia-Romagna,8,43040
+Varsi,34046,Parma,PR,Emilia-Romagna,8,43049
+Sissa Trecasali,34049,Parma,PR,Emilia-Romagna,8,43018
+Polesine Zibello,34050,Parma,PR,Emilia-Romagna,8,43010
+Sorbolo Mezzani,34051,Parma,PR,Emilia-Romagna,8,43058
+Albinea,35001,Reggio nell'Emilia,RE,Emilia-Romagna,8,42020
+Bagnolo in Piano,35002,Reggio nell'Emilia,RE,Emilia-Romagna,8,42011
+Baiso,35003,Reggio nell'Emilia,RE,Emilia-Romagna,8,42031
+Bibbiano,35004,Reggio nell'Emilia,RE,Emilia-Romagna,8,42021
+Boretto,35005,Reggio nell'Emilia,RE,Emilia-Romagna,8,42022
+Brescello,35006,Reggio nell'Emilia,RE,Emilia-Romagna,8,42041
+Cadelbosco di Sopra,35008,Reggio nell'Emilia,RE,Emilia-Romagna,8,42023
+Campagnola Emilia,35009,Reggio nell'Emilia,RE,Emilia-Romagna,8,42012
+Campegine,35010,Reggio nell'Emilia,RE,Emilia-Romagna,8,42040
+Carpineti,35011,Reggio nell'Emilia,RE,Emilia-Romagna,8,42033
+Casalgrande,35012,Reggio nell'Emilia,RE,Emilia-Romagna,8,42013
+Casina,35013,Reggio nell'Emilia,RE,Emilia-Romagna,8,42034
+Castellarano,35014,Reggio nell'Emilia,RE,Emilia-Romagna,8,42014
+Castelnovo di Sotto,35015,Reggio nell'Emilia,RE,Emilia-Romagna,8,42024
+Castelnovo ne' Monti,35016,Reggio nell'Emilia,RE,Emilia-Romagna,8,42035
+Cavriago,35017,Reggio nell'Emilia,RE,Emilia-Romagna,8,42025
+Canossa,35018,Reggio nell'Emilia,RE,Emilia-Romagna,8,42026
+Correggio,35020,Reggio nell'Emilia,RE,Emilia-Romagna,8,42015
+Fabbrico,35021,Reggio nell'Emilia,RE,Emilia-Romagna,8,42042
+Gattatico,35022,Reggio nell'Emilia,RE,Emilia-Romagna,8,42043
+Gualtieri,35023,Reggio nell'Emilia,RE,Emilia-Romagna,8,42044
+Guastalla,35024,Reggio nell'Emilia,RE,Emilia-Romagna,8,42016
+Luzzara,35026,Reggio nell'Emilia,RE,Emilia-Romagna,8,42045
+Montecchio Emilia,35027,Reggio nell'Emilia,RE,Emilia-Romagna,8,42027
+Novellara,35028,Reggio nell'Emilia,RE,Emilia-Romagna,8,42017
+Poviglio,35029,Reggio nell'Emilia,RE,Emilia-Romagna,8,42028
+Quattro Castella,35030,Reggio nell'Emilia,RE,Emilia-Romagna,8,42020
+Reggiolo,35032,Reggio nell'Emilia,RE,Emilia-Romagna,8,42046
+Reggio nell'Emilia,35033,Reggio nell'Emilia,RE,Emilia-Romagna,8,42121
+Rio Saliceto,35034,Reggio nell'Emilia,RE,Emilia-Romagna,8,42010
+Rolo,35035,Reggio nell'Emilia,RE,Emilia-Romagna,8,42047
+Rubiera,35036,Reggio nell'Emilia,RE,Emilia-Romagna,8,42048
+San Martino in Rio,35037,Reggio nell'Emilia,RE,Emilia-Romagna,8,42018
+San Polo d'Enza,35038,Reggio nell'Emilia,RE,Emilia-Romagna,8,42020
+Sant'Ilario d'Enza,35039,Reggio nell'Emilia,RE,Emilia-Romagna,8,42049
+Scandiano,35040,Reggio nell'Emilia,RE,Emilia-Romagna,8,42019
+Toano,35041,Reggio nell'Emilia,RE,Emilia-Romagna,8,42010
+Vetto,35042,Reggio nell'Emilia,RE,Emilia-Romagna,8,42020
+Vezzano sul Crostolo,35043,Reggio nell'Emilia,RE,Emilia-Romagna,8,42030
+Viano,35044,Reggio nell'Emilia,RE,Emilia-Romagna,8,42030
+Villa Minozzo,35045,Reggio nell'Emilia,RE,Emilia-Romagna,8,42030
+Ventasso,35046,Reggio nell'Emilia,RE,Emilia-Romagna,8,42032
+Bastiglia,36001,Modena,MO,Emilia-Romagna,8,41030
+Bomporto,36002,Modena,MO,Emilia-Romagna,8,41030
+Campogalliano,36003,Modena,MO,Emilia-Romagna,8,41011
+Camposanto,36004,Modena,MO,Emilia-Romagna,8,41031
+Carpi,36005,Modena,MO,Emilia-Romagna,8,41012
+Castelfranco Emilia,36006,Modena,MO,Emilia-Romagna,8,41013
+Castelnuovo Rangone,36007,Modena,MO,Emilia-Romagna,8,41051
+Castelvetro di Modena,36008,Modena,MO,Emilia-Romagna,8,41014
+Cavezzo,36009,Modena,MO,Emilia-Romagna,8,41032
+Concordia sulla Secchia,36010,Modena,MO,Emilia-Romagna,8,41033
+Fanano,36011,Modena,MO,Emilia-Romagna,8,41021
+Finale Emilia,36012,Modena,MO,Emilia-Romagna,8,41034
+Fiorano Modenese,36013,Modena,MO,Emilia-Romagna,8,41042
+Fiumalbo,36014,Modena,MO,Emilia-Romagna,8,41022
+Formigine,36015,Modena,MO,Emilia-Romagna,8,41043
+Frassinoro,36016,Modena,MO,Emilia-Romagna,8,41044
+Guiglia,36017,Modena,MO,Emilia-Romagna,8,41052
+Lama Mocogno,36018,Modena,MO,Emilia-Romagna,8,41023
+Maranello,36019,Modena,MO,Emilia-Romagna,8,41053
+Marano sul Panaro,36020,Modena,MO,Emilia-Romagna,8,41054
+Medolla,36021,Modena,MO,Emilia-Romagna,8,41036
+Mirandola,36022,Modena,MO,Emilia-Romagna,8,41037
+Modena,36023,Modena,MO,Emilia-Romagna,8,41121
+Montecreto,36024,Modena,MO,Emilia-Romagna,8,41025
+Montefiorino,36025,Modena,MO,Emilia-Romagna,8,41045
+Montese,36026,Modena,MO,Emilia-Romagna,8,41055
+Nonantola,36027,Modena,MO,Emilia-Romagna,8,41015
+Novi di Modena,36028,Modena,MO,Emilia-Romagna,8,41016
+Palagano,36029,Modena,MO,Emilia-Romagna,8,41046
+Pavullo nel Frignano,36030,Modena,MO,Emilia-Romagna,8,41026
+Pievepelago,36031,Modena,MO,Emilia-Romagna,8,41027
+Polinago,36032,Modena,MO,Emilia-Romagna,8,41040
+Prignano sulla Secchia,36033,Modena,MO,Emilia-Romagna,8,41048
+Ravarino,36034,Modena,MO,Emilia-Romagna,8,41017
+Riolunato,36035,Modena,MO,Emilia-Romagna,8,41020
+San Cesario sul Panaro,36036,Modena,MO,Emilia-Romagna,8,41018
+San Felice sul Panaro,36037,Modena,MO,Emilia-Romagna,8,41038
+San Possidonio,36038,Modena,MO,Emilia-Romagna,8,41039
+San Prospero,36039,Modena,MO,Emilia-Romagna,8,41030
+Sassuolo,36040,Modena,MO,Emilia-Romagna,8,41049
+Savignano sul Panaro,36041,Modena,MO,Emilia-Romagna,8,41056
+Serramazzoni,36042,Modena,MO,Emilia-Romagna,8,41028
+Sestola,36043,Modena,MO,Emilia-Romagna,8,41029
+Soliera,36044,Modena,MO,Emilia-Romagna,8,41019
+Spilamberto,36045,Modena,MO,Emilia-Romagna,8,41057
+Vignola,36046,Modena,MO,Emilia-Romagna,8,41058
+Zocca,36047,Modena,MO,Emilia-Romagna,8,41059
+Anzola dell'Emilia,37001,Bologna,BO,Emilia-Romagna,8,40011
+Argelato,37002,Bologna,BO,Emilia-Romagna,8,40050
+Baricella,37003,Bologna,BO,Emilia-Romagna,8,40052
+Bentivoglio,37005,Bologna,BO,Emilia-Romagna,8,40010
+Bologna,37006,Bologna,BO,Emilia-Romagna,8,40121
+Borgo Tossignano,37007,Bologna,BO,Emilia-Romagna,8,40021
+Budrio,37008,Bologna,BO,Emilia-Romagna,8,40054
+Calderara di Reno,37009,Bologna,BO,Emilia-Romagna,8,40012
+Camugnano,37010,Bologna,BO,Emilia-Romagna,8,40032
+Casalecchio di Reno,37011,Bologna,BO,Emilia-Romagna,8,40033
+Casalfiumanese,37012,Bologna,BO,Emilia-Romagna,8,40020
+Castel d'Aiano,37013,Bologna,BO,Emilia-Romagna,8,40034
+Castel del Rio,37014,Bologna,BO,Emilia-Romagna,8,40022
+Castel di Casio,37015,Bologna,BO,Emilia-Romagna,8,40030
+Castel Guelfo di Bologna,37016,Bologna,BO,Emilia-Romagna,8,40023
+Castello d'Argile,37017,Bologna,BO,Emilia-Romagna,8,40050
+Castel Maggiore,37019,Bologna,BO,Emilia-Romagna,8,40013
+Castel San Pietro Terme,37020,Bologna,BO,Emilia-Romagna,8,40024
+Castenaso,37021,Bologna,BO,Emilia-Romagna,8,40055
+Castiglione dei Pepoli,37022,Bologna,BO,Emilia-Romagna,8,40035
+Crevalcore,37024,Bologna,BO,Emilia-Romagna,8,40014
+Dozza,37025,Bologna,BO,Emilia-Romagna,8,40060
+Fontanelice,37026,Bologna,BO,Emilia-Romagna,8,40025
+Gaggio Montano,37027,Bologna,BO,Emilia-Romagna,8,40041
+Galliera,37028,Bologna,BO,Emilia-Romagna,8,40015
+Granarolo dell'Emilia,37030,Bologna,BO,Emilia-Romagna,8,40057
+Grizzana Morandi,37031,Bologna,BO,Emilia-Romagna,8,40030
+Imola,37032,Bologna,BO,Emilia-Romagna,8,40026
+Lizzano in Belvedere,37033,Bologna,BO,Emilia-Romagna,8,40042
+Loiano,37034,Bologna,BO,Emilia-Romagna,8,40050
+Malalbergo,37035,Bologna,BO,Emilia-Romagna,8,40051
+Marzabotto,37036,Bologna,BO,Emilia-Romagna,8,40043
+Medicina,37037,Bologna,BO,Emilia-Romagna,8,40059
+Minerbio,37038,Bologna,BO,Emilia-Romagna,8,40061
+Molinella,37039,Bologna,BO,Emilia-Romagna,8,40062
+Monghidoro,37040,Bologna,BO,Emilia-Romagna,8,40063
+Monterenzio,37041,Bologna,BO,Emilia-Romagna,8,40050
+Monte San Pietro,37042,Bologna,BO,Emilia-Romagna,8,40050
+Monzuno,37044,Bologna,BO,Emilia-Romagna,8,40036
+Mordano,37045,Bologna,BO,Emilia-Romagna,8,40027
+Ozzano dell'Emilia,37046,Bologna,BO,Emilia-Romagna,8,40064
+Pianoro,37047,Bologna,BO,Emilia-Romagna,8,40065
+Pieve di Cento,37048,Bologna,BO,Emilia-Romagna,8,40066
+Sala Bolognese,37050,Bologna,BO,Emilia-Romagna,8,40010
+San Benedetto Val di Sambro,37051,Bologna,BO,Emilia-Romagna,8,40048
+San Giorgio di Piano,37052,Bologna,BO,Emilia-Romagna,8,40016
+San Giovanni in Persiceto,37053,Bologna,BO,Emilia-Romagna,8,40017
+San Lazzaro di Savena,37054,Bologna,BO,Emilia-Romagna,8,40068
+San Pietro in Casale,37055,Bologna,BO,Emilia-Romagna,8,40018
+Sant'Agata Bolognese,37056,Bologna,BO,Emilia-Romagna,8,40019
+Sasso Marconi,37057,Bologna,BO,Emilia-Romagna,8,40037
+Vergato,37059,Bologna,BO,Emilia-Romagna,8,40038
+Zola Predosa,37060,Bologna,BO,Emilia-Romagna,8,40069
+Valsamoggia,37061,Bologna,BO,Emilia-Romagna,8,40053
+Alto Reno Terme,37062,Bologna,BO,Emilia-Romagna,8,40046
+Argenta,38001,Ferrara,FE,Emilia-Romagna,8,44011
+Bondeno,38003,Ferrara,FE,Emilia-Romagna,8,44012
+Cento,38004,Ferrara,FE,Emilia-Romagna,8,44042
+Codigoro,38005,Ferrara,FE,Emilia-Romagna,8,44021
+Comacchio,38006,Ferrara,FE,Emilia-Romagna,8,44022
+Copparo,38007,Ferrara,FE,Emilia-Romagna,8,44034
+Ferrara,38008,Ferrara,FE,Emilia-Romagna,8,44121
+Jolanda di Savoia,38010,Ferrara,FE,Emilia-Romagna,8,44037
+Lagosanto,38011,Ferrara,FE,Emilia-Romagna,8,44023
+Masi Torello,38012,Ferrara,FE,Emilia-Romagna,8,44020
+Mesola,38014,Ferrara,FE,Emilia-Romagna,8,44026
+Ostellato,38017,Ferrara,FE,Emilia-Romagna,8,44020
+Poggio Renatico,38018,Ferrara,FE,Emilia-Romagna,8,44028
+Portomaggiore,38019,Ferrara,FE,Emilia-Romagna,8,44015
+Vigarano Mainarda,38022,Ferrara,FE,Emilia-Romagna,8,44049
+Voghiera,38023,Ferrara,FE,Emilia-Romagna,8,44019
+Goro,38025,Ferrara,FE,Emilia-Romagna,8,44020
+Fiscaglia,38027,Ferrara,FE,Emilia-Romagna,8,44027
+Terre del Reno,38028,Ferrara,FE,Emilia-Romagna,8,44047
+Riva del Po,38029,Ferrara,FE,Emilia-Romagna,8,44033
+Tresignana,38030,Ferrara,FE,Emilia-Romagna,8,44039
+Alfonsine,39001,Ravenna,RA,Emilia-Romagna,8,48011
+Bagnacavallo,39002,Ravenna,RA,Emilia-Romagna,8,48012
+Bagnara di Romagna,39003,Ravenna,RA,Emilia-Romagna,8,48010
+Brisighella,39004,Ravenna,RA,Emilia-Romagna,8,48013
+Casola Valsenio,39005,Ravenna,RA,Emilia-Romagna,8,48010
+Castel Bolognese,39006,Ravenna,RA,Emilia-Romagna,8,48014
+Cervia,39007,Ravenna,RA,Emilia-Romagna,8,48015
+Conselice,39008,Ravenna,RA,Emilia-Romagna,8,48017
+Cotignola,39009,Ravenna,RA,Emilia-Romagna,8,48010
+Faenza,39010,Ravenna,RA,Emilia-Romagna,8,48018
+Fusignano,39011,Ravenna,RA,Emilia-Romagna,8,48010
+Lugo,39012,Ravenna,RA,Emilia-Romagna,8,48022
+Massa Lombarda,39013,Ravenna,RA,Emilia-Romagna,8,48024
+Ravenna,39014,Ravenna,RA,Emilia-Romagna,8,48121
+Riolo Terme,39015,Ravenna,RA,Emilia-Romagna,8,48025
+Russi,39016,Ravenna,RA,Emilia-Romagna,8,48026
+Sant'Agata sul Santerno,39017,Ravenna,RA,Emilia-Romagna,8,48020
+Solarolo,39018,Ravenna,RA,Emilia-Romagna,8,48027
+Bagno di Romagna,40001,Forlì-Cesena,FC,Emilia-Romagna,8,47021
+Bertinoro,40003,Forlì-Cesena,FC,Emilia-Romagna,8,47032
+Borghi,40004,Forlì-Cesena,FC,Emilia-Romagna,8,47030
+Castrocaro Terme e Terra del Sole,40005,Forlì-Cesena,FC,Emilia-Romagna,8,47011
+Cesena,40007,Forlì-Cesena,FC,Emilia-Romagna,8,47520
+Cesenatico,40008,Forlì-Cesena,FC,Emilia-Romagna,8,47042
+Civitella di Romagna,40009,Forlì-Cesena,FC,Emilia-Romagna,8,47012
+Dovadola,40011,Forlì-Cesena,FC,Emilia-Romagna,8,47013
+Forlì,40012,Forlì-Cesena,FC,Emilia-Romagna,8,47121
+Forlimpopoli,40013,Forlì-Cesena,FC,Emilia-Romagna,8,47034
+Galeata,40014,Forlì-Cesena,FC,Emilia-Romagna,8,47010
+Gambettola,40015,Forlì-Cesena,FC,Emilia-Romagna,8,47035
+Gatteo,40016,Forlì-Cesena,FC,Emilia-Romagna,8,47043
+Longiano,40018,Forlì-Cesena,FC,Emilia-Romagna,8,47020
+Meldola,40019,Forlì-Cesena,FC,Emilia-Romagna,8,47014
+Mercato Saraceno,40020,Forlì-Cesena,FC,Emilia-Romagna,8,47025
+Modigliana,40022,Forlì-Cesena,FC,Emilia-Romagna,8,47015
+Montiano,40028,Forlì-Cesena,FC,Emilia-Romagna,8,47020
+Portico e San Benedetto,40031,Forlì-Cesena,FC,Emilia-Romagna,8,47010
+Predappio,40032,Forlì-Cesena,FC,Emilia-Romagna,8,47016
+Premilcuore,40033,Forlì-Cesena,FC,Emilia-Romagna,8,47010
+Rocca San Casciano,40036,Forlì-Cesena,FC,Emilia-Romagna,8,47017
+Roncofreddo,40037,Forlì-Cesena,FC,Emilia-Romagna,8,47020
+San Mauro Pascoli,40041,Forlì-Cesena,FC,Emilia-Romagna,8,47030
+Santa Sofia,40043,Forlì-Cesena,FC,Emilia-Romagna,8,47018
+Sarsina,40044,Forlì-Cesena,FC,Emilia-Romagna,8,47027
+Savignano sul Rubicone,40045,Forlì-Cesena,FC,Emilia-Romagna,8,47039
+Sogliano al Rubicone,40046,Forlì-Cesena,FC,Emilia-Romagna,8,47030
+Tredozio,40049,Forlì-Cesena,FC,Emilia-Romagna,8,47019
+Verghereto,40050,Forlì-Cesena,FC,Emilia-Romagna,8,47028
+Bellaria-Igea Marina,99001,Rimini,RN,Emilia-Romagna,8,47814
+Cattolica,99002,Rimini,RN,Emilia-Romagna,8,47841
+Coriano,99003,Rimini,RN,Emilia-Romagna,8,47853
+Gemmano,99004,Rimini,RN,Emilia-Romagna,8,47855
+Misano Adriatico,99005,Rimini,RN,Emilia-Romagna,8,47843
+Mondaino,99006,Rimini,RN,Emilia-Romagna,8,47836
+Montefiore Conca,99008,Rimini,RN,Emilia-Romagna,8,47834
+Montegridolfo,99009,Rimini,RN,Emilia-Romagna,8,47837
+Morciano di Romagna,99011,Rimini,RN,Emilia-Romagna,8,47833
+Riccione,99013,Rimini,RN,Emilia-Romagna,8,47838
+Rimini,99014,Rimini,RN,Emilia-Romagna,8,47921
+Saludecio,99015,Rimini,RN,Emilia-Romagna,8,47835
+San Clemente,99016,Rimini,RN,Emilia-Romagna,8,47832
+San Giovanni in Marignano,99017,Rimini,RN,Emilia-Romagna,8,47842
+Santarcangelo di Romagna,99018,Rimini,RN,Emilia-Romagna,8,47822
+Verucchio,99020,Rimini,RN,Emilia-Romagna,8,47826
+Casteldelci,99021,Rimini,RN,Emilia-Romagna,8,47861
+Maiolo,99022,Rimini,RN,Emilia-Romagna,8,47862
+Novafeltria,99023,Rimini,RN,Emilia-Romagna,8,47863
+Pennabilli,99024,Rimini,RN,Emilia-Romagna,8,47864
+San Leo,99025,Rimini,RN,Emilia-Romagna,8,47865
+Sant'Agata Feltria,99026,Rimini,RN,Emilia-Romagna,8,47866
+Talamello,99027,Rimini,RN,Emilia-Romagna,8,47867
+Poggio Torriana,99028,Rimini,RN,Emilia-Romagna,8,47824
+Montescudo-Monte Colombo,99029,Rimini,RN,Emilia-Romagna,8,47854
+Aulla,45001,Massa-Carrara,MS,Toscana,9,54011
+Bagnone,45002,Massa-Carrara,MS,Toscana,9,54021
+Carrara,45003,Massa-Carrara,MS,Toscana,9,54033
+Casola in Lunigiana,45004,Massa-Carrara,MS,Toscana,9,54014
+Comano,45005,Massa-Carrara,MS,Toscana,9,54015
+Filattiera,45006,Massa-Carrara,MS,Toscana,9,54023
+Fivizzano,45007,Massa-Carrara,MS,Toscana,9,54013
+Fosdinovo,45008,Massa-Carrara,MS,Toscana,9,54035
+Licciana Nardi,45009,Massa-Carrara,MS,Toscana,9,54016
+Massa,45010,Massa-Carrara,MS,Toscana,9,54100
+Montignoso,45011,Massa-Carrara,MS,Toscana,9,54038
+Mulazzo,45012,Massa-Carrara,MS,Toscana,9,54026
+Podenzana,45013,Massa-Carrara,MS,Toscana,9,54010
+Pontremoli,45014,Massa-Carrara,MS,Toscana,9,54027
+Tresana,45015,Massa-Carrara,MS,Toscana,9,54012
+Villafranca in Lunigiana,45016,Massa-Carrara,MS,Toscana,9,54028
+Zeri,45017,Massa-Carrara,MS,Toscana,9,54029
+Altopascio,46001,Lucca,LU,Toscana,9,55011
+Bagni di Lucca,46002,Lucca,LU,Toscana,9,55022
+Barga,46003,Lucca,LU,Toscana,9,55051
+Borgo a Mozzano,46004,Lucca,LU,Toscana,9,55023
+Camaiore,46005,Lucca,LU,Toscana,9,55041
+Camporgiano,46006,Lucca,LU,Toscana,9,55031
+Capannori,46007,Lucca,LU,Toscana,9,55012
+Careggine,46008,Lucca,LU,Toscana,9,55030
+Castelnuovo di Garfagnana,46009,Lucca,LU,Toscana,9,55032
+Castiglione di Garfagnana,46010,Lucca,LU,Toscana,9,55033
+Coreglia Antelminelli,46011,Lucca,LU,Toscana,9,55025
+Forte dei Marmi,46013,Lucca,LU,Toscana,9,55042
+Fosciandora,46014,Lucca,LU,Toscana,9,55020
+Gallicano,46015,Lucca,LU,Toscana,9,55027
+Lucca,46017,Lucca,LU,Toscana,9,55100
+Massarosa,46018,Lucca,LU,Toscana,9,55054
+Minucciano,46019,Lucca,LU,Toscana,9,55034
+Molazzana,46020,Lucca,LU,Toscana,9,55020
+Montecarlo,46021,Lucca,LU,Toscana,9,55015
+Pescaglia,46022,Lucca,LU,Toscana,9,55064
+Piazza al Serchio,46023,Lucca,LU,Toscana,9,55035
+Pietrasanta,46024,Lucca,LU,Toscana,9,55045
+Pieve Fosciana,46025,Lucca,LU,Toscana,9,55036
+Porcari,46026,Lucca,LU,Toscana,9,55016
+San Romano in Garfagnana,46027,Lucca,LU,Toscana,9,55038
+Seravezza,46028,Lucca,LU,Toscana,9,55047
+Stazzema,46030,Lucca,LU,Toscana,9,55040
+Vagli Sotto,46031,Lucca,LU,Toscana,9,55030
+Viareggio,46033,Lucca,LU,Toscana,9,55049
+Villa Basilica,46034,Lucca,LU,Toscana,9,55019
+Villa Collemandina,46035,Lucca,LU,Toscana,9,55030
+Fabbriche di Vergemoli,46036,Lucca,LU,Toscana,9,55021
+Sillano Giuncugnano,46037,Lucca,LU,Toscana,9,55030
+Agliana,47002,Pistoia,PT,Toscana,9,51031
+Buggiano,47003,Pistoia,PT,Toscana,9,51011
+Lamporecchio,47005,Pistoia,PT,Toscana,9,51035
+Larciano,47006,Pistoia,PT,Toscana,9,51036
+Marliana,47007,Pistoia,PT,Toscana,9,51010
+Massa e Cozzile,47008,Pistoia,PT,Toscana,9,51010
+Monsummano Terme,47009,Pistoia,PT,Toscana,9,51015
+Montale,47010,Pistoia,PT,Toscana,9,51037
+Montecatini-Terme,47011,Pistoia,PT,Toscana,9,51016
+Pescia,47012,Pistoia,PT,Toscana,9,51017
+Pieve a Nievole,47013,Pistoia,PT,Toscana,9,51018
+Pistoia,47014,Pistoia,PT,Toscana,9,51100
+Ponte Buggianese,47016,Pistoia,PT,Toscana,9,51019
+Quarrata,47017,Pistoia,PT,Toscana,9,51039
+Sambuca Pistoiese,47018,Pistoia,PT,Toscana,9,51020
+Serravalle Pistoiese,47020,Pistoia,PT,Toscana,9,51030
+Uzzano,47021,Pistoia,PT,Toscana,9,51010
+Chiesina Uzzanese,47022,Pistoia,PT,Toscana,9,51013
+Abetone Cutigliano,47023,Pistoia,PT,Toscana,9,51024
+San Marcello Piteglio,47024,Pistoia,PT,Toscana,9,51028
+Bagno a Ripoli,48001,Firenze,FI,Toscana,9,50012
+Barberino di Mugello,48002,Firenze,FI,Toscana,9,50031
+Borgo San Lorenzo,48004,Firenze,FI,Toscana,9,50032
+Calenzano,48005,Firenze,FI,Toscana,9,50041
+Campi Bisenzio,48006,Firenze,FI,Toscana,9,50013
+Capraia e Limite,48008,Firenze,FI,Toscana,9,50050
+Castelfiorentino,48010,Firenze,FI,Toscana,9,50051
+Cerreto Guidi,48011,Firenze,FI,Toscana,9,50050
+Certaldo,48012,Firenze,FI,Toscana,9,50052
+Dicomano,48013,Firenze,FI,Toscana,9,50062
+Empoli,48014,Firenze,FI,Toscana,9,50053
+Fiesole,48015,Firenze,FI,Toscana,9,50014
+Firenze,48017,Firenze,FI,Toscana,9,50122
+Firenzuola,48018,Firenze,FI,Toscana,9,50033
+Fucecchio,48019,Firenze,FI,Toscana,9,50054
+Gambassi Terme,48020,Firenze,FI,Toscana,9,50050
+Greve in Chianti,48021,Firenze,FI,Toscana,9,50022
+Impruneta,48022,Firenze,FI,Toscana,9,50023
+Lastra a Signa,48024,Firenze,FI,Toscana,9,50055
+Londa,48025,Firenze,FI,Toscana,9,50060
+Marradi,48026,Firenze,FI,Toscana,9,50034
+Montaione,48027,Firenze,FI,Toscana,9,50050
+Montelupo Fiorentino,48028,Firenze,FI,Toscana,9,50056
+Montespertoli,48030,Firenze,FI,Toscana,9,50025
+Palazzuolo sul Senio,48031,Firenze,FI,Toscana,9,50035
+Pelago,48032,Firenze,FI,Toscana,9,50060
+Pontassieve,48033,Firenze,FI,Toscana,9,50065
+Reggello,48035,Firenze,FI,Toscana,9,50066
+Rignano sull'Arno,48036,Firenze,FI,Toscana,9,50067
+Rufina,48037,Firenze,FI,Toscana,9,50068
+San Casciano in Val di Pesa,48038,Firenze,FI,Toscana,9,50026
+San Godenzo,48039,Firenze,FI,Toscana,9,50060
+Scandicci,48041,Firenze,FI,Toscana,9,50018
+Sesto Fiorentino,48043,Firenze,FI,Toscana,9,50019
+Signa,48044,Firenze,FI,Toscana,9,50058
+Vaglia,48046,Firenze,FI,Toscana,9,50036
+Vicchio,48049,Firenze,FI,Toscana,9,50039
+Vinci,48050,Firenze,FI,Toscana,9,50059
+Figline e Incisa Valdarno,48052,Firenze,FI,Toscana,9,50063
+Scarperia e San Piero,48053,Firenze,FI,Toscana,9,50038
+Barberino Tavarnelle,48054,Firenze,FI,Toscana,9,50028
+Bibbona,49001,Livorno,LI,Toscana,9,57020
+Campiglia Marittima,49002,Livorno,LI,Toscana,9,57021
+Campo nell'Elba,49003,Livorno,LI,Toscana,9,57034
+Capoliveri,49004,Livorno,LI,Toscana,9,57031
+Capraia Isola,49005,Livorno,LI,Toscana,9,57032
+Castagneto Carducci,49006,Livorno,LI,Toscana,9,57022
+Cecina,49007,Livorno,LI,Toscana,9,57023
+Collesalvetti,49008,Livorno,LI,Toscana,9,57014
+Livorno,49009,Livorno,LI,Toscana,9,57123
+Marciana,49010,Livorno,LI,Toscana,9,57030
+Marciana Marina,49011,Livorno,LI,Toscana,9,57033
+Piombino,49012,Livorno,LI,Toscana,9,57025
+Porto Azzurro,49013,Livorno,LI,Toscana,9,57036
+Portoferraio,49014,Livorno,LI,Toscana,9,57037
+Rosignano Marittimo,49017,Livorno,LI,Toscana,9,57016
+San Vincenzo,49018,Livorno,LI,Toscana,9,57027
+Sassetta,49019,Livorno,LI,Toscana,9,57020
+Suvereto,49020,Livorno,LI,Toscana,9,57028
+Rio,49021,Livorno,LI,Toscana,9,57038
+Bientina,50001,Pisa,PI,Toscana,9,56031
+Buti,50002,Pisa,PI,Toscana,9,56032
+Calci,50003,Pisa,PI,Toscana,9,56011
+Calcinaia,50004,Pisa,PI,Toscana,9,56012
+Capannoli,50005,Pisa,PI,Toscana,9,56033
+Casale Marittimo,50006,Pisa,PI,Toscana,9,56040
+Cascina,50008,Pisa,PI,Toscana,9,56021
+Castelfranco di Sotto,50009,Pisa,PI,Toscana,9,56022
+Castellina Marittima,50010,Pisa,PI,Toscana,9,56040
+Castelnuovo di Val di Cecina,50011,Pisa,PI,Toscana,9,56041
+Chianni,50012,Pisa,PI,Toscana,9,56034
+Fauglia,50014,Pisa,PI,Toscana,9,56043
+Guardistallo,50015,Pisa,PI,Toscana,9,56040
+Lajatico,50016,Pisa,PI,Toscana,9,56030
+Montecatini Val di Cecina,50019,Pisa,PI,Toscana,9,56040
+Montescudaio,50020,Pisa,PI,Toscana,9,56040
+Monteverdi Marittimo,50021,Pisa,PI,Toscana,9,56040
+Montopoli in Val d'Arno,50022,Pisa,PI,Toscana,9,56020
+Orciano Pisano,50023,Pisa,PI,Toscana,9,56040
+Palaia,50024,Pisa,PI,Toscana,9,56036
+Peccioli,50025,Pisa,PI,Toscana,9,56037
+Pisa,50026,Pisa,PI,Toscana,9,56120
+Pomarance,50027,Pisa,PI,Toscana,9,56045
+Ponsacco,50028,Pisa,PI,Toscana,9,56038
+Pontedera,50029,Pisa,PI,Toscana,9,56025
+Riparbella,50030,Pisa,PI,Toscana,9,56046
+San Giuliano Terme,50031,Pisa,PI,Toscana,9,56017
+San Miniato,50032,Pisa,PI,Toscana,9,56028
+Santa Croce sull'Arno,50033,Pisa,PI,Toscana,9,56029
+Santa Luce,50034,Pisa,PI,Toscana,9,56040
+Santa Maria a Monte,50035,Pisa,PI,Toscana,9,56020
+Terricciola,50036,Pisa,PI,Toscana,9,56030
+Vecchiano,50037,Pisa,PI,Toscana,9,56019
+Vicopisano,50038,Pisa,PI,Toscana,9,56010
+Volterra,50039,Pisa,PI,Toscana,9,56048
+Casciana Terme Lari,50040,Pisa,PI,Toscana,9,56035
+Crespina Lorenzana,50041,Pisa,PI,Toscana,9,56042
+Anghiari,51001,Arezzo,AR,Toscana,9,52031
+Arezzo,51002,Arezzo,AR,Toscana,9,52100
+Badia Tedalda,51003,Arezzo,AR,Toscana,9,52032
+Bibbiena,51004,Arezzo,AR,Toscana,9,52011
+Bucine,51005,Arezzo,AR,Toscana,9,52021
+Capolona,51006,Arezzo,AR,Toscana,9,52010
+Caprese Michelangelo,51007,Arezzo,AR,Toscana,9,52033
+Castel Focognano,51008,Arezzo,AR,Toscana,9,52016
+Castel San Niccolò,51010,Arezzo,AR,Toscana,9,52018
+Castiglion Fibocchi,51011,Arezzo,AR,Toscana,9,52029
+Castiglion Fiorentino,51012,Arezzo,AR,Toscana,9,52043
+Cavriglia,51013,Arezzo,AR,Toscana,9,52022
+Chitignano,51014,Arezzo,AR,Toscana,9,52010
+Chiusi della Verna,51015,Arezzo,AR,Toscana,9,52010
+Civitella in Val di Chiana,51016,Arezzo,AR,Toscana,9,52041
+Cortona,51017,Arezzo,AR,Toscana,9,52044
+Foiano della Chiana,51018,Arezzo,AR,Toscana,9,52045
+Loro Ciuffenna,51020,Arezzo,AR,Toscana,9,52024
+Lucignano,51021,Arezzo,AR,Toscana,9,52046
+Marciano della Chiana,51022,Arezzo,AR,Toscana,9,52047
+Montemignaio,51023,Arezzo,AR,Toscana,9,52010
+Monterchi,51024,Arezzo,AR,Toscana,9,52035
+Monte San Savino,51025,Arezzo,AR,Toscana,9,52048
+Montevarchi,51026,Arezzo,AR,Toscana,9,52025
+Ortignano Raggiolo,51027,Arezzo,AR,Toscana,9,52010
+Pieve Santo Stefano,51030,Arezzo,AR,Toscana,9,52036
+Poppi,51031,Arezzo,AR,Toscana,9,52014
+San Giovanni Valdarno,51033,Arezzo,AR,Toscana,9,52027
+Sansepolcro,51034,Arezzo,AR,Toscana,9,52037
+Sestino,51035,Arezzo,AR,Toscana,9,52038
+Subbiano,51037,Arezzo,AR,Toscana,9,52010
+Talla,51038,Arezzo,AR,Toscana,9,52010
+Terranuova Bracciolini,51039,Arezzo,AR,Toscana,9,52028
+Castelfranco Piandiscò,51040,Arezzo,AR,Toscana,9,52026
+Pratovecchio Stia,51041,Arezzo,AR,Toscana,9,52015
+Laterina Pergine Valdarno,51042,Arezzo,AR,Toscana,9,52020
+Abbadia San Salvatore,52001,Siena,SI,Toscana,9,53021
+Asciano,52002,Siena,SI,Toscana,9,53041
+Buonconvento,52003,Siena,SI,Toscana,9,53022
+Casole d'Elsa,52004,Siena,SI,Toscana,9,53031
+Castellina in Chianti,52005,Siena,SI,Toscana,9,53011
+Castelnuovo Berardenga,52006,Siena,SI,Toscana,9,53019
+Castiglione d'Orcia,52007,Siena,SI,Toscana,9,53023
+Cetona,52008,Siena,SI,Toscana,9,53040
+Chianciano Terme,52009,Siena,SI,Toscana,9,53042
+Chiusdino,52010,Siena,SI,Toscana,9,53012
+Chiusi,52011,Siena,SI,Toscana,9,53043
+Colle di Val d'Elsa,52012,Siena,SI,Toscana,9,53034
+Gaiole in Chianti,52013,Siena,SI,Toscana,9,53013
+Montepulciano,52015,Siena,SI,Toscana,9,53045
+Monteriggioni,52016,Siena,SI,Toscana,9,53035
+Monteroni d'Arbia,52017,Siena,SI,Toscana,9,53014
+Monticiano,52018,Siena,SI,Toscana,9,53015
+Murlo,52019,Siena,SI,Toscana,9,53016
+Piancastagnaio,52020,Siena,SI,Toscana,9,53025
+Pienza,52021,Siena,SI,Toscana,9,53026
+Poggibonsi,52022,Siena,SI,Toscana,9,53036
+Radda in Chianti,52023,Siena,SI,Toscana,9,53017
+Radicofani,52024,Siena,SI,Toscana,9,53040
+Radicondoli,52025,Siena,SI,Toscana,9,53030
+Rapolano Terme,52026,Siena,SI,Toscana,9,53040
+San Casciano dei Bagni,52027,Siena,SI,Toscana,9,53040
+San Gimignano,52028,Siena,SI,Toscana,9,53037
+San Quirico d'Orcia,52030,Siena,SI,Toscana,9,53027
+Sarteano,52031,Siena,SI,Toscana,9,53047
+Siena,52032,Siena,SI,Toscana,9,53100
+Sinalunga,52033,Siena,SI,Toscana,9,53048
+Sovicille,52034,Siena,SI,Toscana,9,53018
+Torrita di Siena,52035,Siena,SI,Toscana,9,53049
+Trequanda,52036,Siena,SI,Toscana,9,53020
+Montalcino,52037,Siena,SI,Toscana,9,53024
+Arcidosso,53001,Grosseto,GR,Toscana,9,58031
+Campagnatico,53002,Grosseto,GR,Toscana,9,58042
+Capalbio,53003,Grosseto,GR,Toscana,9,58011
+Castel del Piano,53004,Grosseto,GR,Toscana,9,58033
+Castell'Azzara,53005,Grosseto,GR,Toscana,9,58034
+Castiglione della Pescaia,53006,Grosseto,GR,Toscana,9,58043
+Cinigiano,53007,Grosseto,GR,Toscana,9,58044
+Civitella Paganico,53008,Grosseto,GR,Toscana,9,58045
+Follonica,53009,Grosseto,GR,Toscana,9,58022
+Gavorrano,53010,Grosseto,GR,Toscana,9,58023
+Grosseto,53011,Grosseto,GR,Toscana,9,58100
+Isola del Giglio,53012,Grosseto,GR,Toscana,9,58012
+Magliano in Toscana,53013,Grosseto,GR,Toscana,9,58051
+Manciano,53014,Grosseto,GR,Toscana,9,58014
+Massa Marittima,53015,Grosseto,GR,Toscana,9,58024
+Monte Argentario,53016,Grosseto,GR,Toscana,9,58019
+Montieri,53017,Grosseto,GR,Toscana,9,58026
+Orbetello,53018,Grosseto,GR,Toscana,9,58015
+Pitigliano,53019,Grosseto,GR,Toscana,9,58017
+Roccalbegna,53020,Grosseto,GR,Toscana,9,58053
+Roccastrada,53021,Grosseto,GR,Toscana,9,58036
+Santa Fiora,53022,Grosseto,GR,Toscana,9,58037
+Scansano,53023,Grosseto,GR,Toscana,9,58054
+Scarlino,53024,Grosseto,GR,Toscana,9,58020
+Seggiano,53025,Grosseto,GR,Toscana,9,58038
+Sorano,53026,Grosseto,GR,Toscana,9,58010
+Monterotondo Marittimo,53027,Grosseto,GR,Toscana,9,58025
+Semproniano,53028,Grosseto,GR,Toscana,9,58055
+Cantagallo,100001,Prato,PO,Toscana,9,59025
+Carmignano,100002,Prato,PO,Toscana,9,59015
+Montemurlo,100003,Prato,PO,Toscana,9,59013
+Poggio a Caiano,100004,Prato,PO,Toscana,9,59016
+Prato,100005,Prato,PO,Toscana,9,59100
+Vaiano,100006,Prato,PO,Toscana,9,59021
+Vernio,100007,Prato,PO,Toscana,9,59024
+Assisi,54001,Perugia,PG,Umbria,10,6081
+Bastia Umbra,54002,Perugia,PG,Umbria,10,6083
+Bettona,54003,Perugia,PG,Umbria,10,6084
+Bevagna,54004,Perugia,PG,Umbria,10,6031
+Campello sul Clitunno,54005,Perugia,PG,Umbria,10,6042
+Cannara,54006,Perugia,PG,Umbria,10,6033
+Cascia,54007,Perugia,PG,Umbria,10,6043
+Castel Ritaldi,54008,Perugia,PG,Umbria,10,6044
+Castiglione del Lago,54009,Perugia,PG,Umbria,10,6061
+Cerreto di Spoleto,54010,Perugia,PG,Umbria,10,6041
+Citerna,54011,Perugia,PG,Umbria,10,6010
+Città della Pieve,54012,Perugia,PG,Umbria,10,6062
+Città di Castello,54013,Perugia,PG,Umbria,10,6012
+Collazzone,54014,Perugia,PG,Umbria,10,6050
+Corciano,54015,Perugia,PG,Umbria,10,6073
+Costacciaro,54016,Perugia,PG,Umbria,10,6021
+Deruta,54017,Perugia,PG,Umbria,10,6053
+Foligno,54018,Perugia,PG,Umbria,10,6034
+Fossato di Vico,54019,Perugia,PG,Umbria,10,6022
+Fratta Todina,54020,Perugia,PG,Umbria,10,6054
+Giano dell'Umbria,54021,Perugia,PG,Umbria,10,6030
+Gualdo Cattaneo,54022,Perugia,PG,Umbria,10,6035
+Gualdo Tadino,54023,Perugia,PG,Umbria,10,6023
+Gubbio,54024,Perugia,PG,Umbria,10,6024
+Lisciano Niccone,54025,Perugia,PG,Umbria,10,6060
+Magione,54026,Perugia,PG,Umbria,10,6063
+Marsciano,54027,Perugia,PG,Umbria,10,6055
+Massa Martana,54028,Perugia,PG,Umbria,10,6056
+Monte Castello di Vibio,54029,Perugia,PG,Umbria,10,6057
+Montefalco,54030,Perugia,PG,Umbria,10,6036
+Monteleone di Spoleto,54031,Perugia,PG,Umbria,10,6045
+Monte Santa Maria Tiberina,54032,Perugia,PG,Umbria,10,6010
+Montone,54033,Perugia,PG,Umbria,10,6014
+Nocera Umbra,54034,Perugia,PG,Umbria,10,6025
+Norcia,54035,Perugia,PG,Umbria,10,6046
+Paciano,54036,Perugia,PG,Umbria,10,6060
+Panicale,54037,Perugia,PG,Umbria,10,6064
+Passignano sul Trasimeno,54038,Perugia,PG,Umbria,10,6065
+Perugia,54039,Perugia,PG,Umbria,10,6121
+Piegaro,54040,Perugia,PG,Umbria,10,6066
+Pietralunga,54041,Perugia,PG,Umbria,10,6026
+Poggiodomo,54042,Perugia,PG,Umbria,10,6040
+Preci,54043,Perugia,PG,Umbria,10,6047
+San Giustino,54044,Perugia,PG,Umbria,10,6016
+Sant'Anatolia di Narco,54045,Perugia,PG,Umbria,10,6040
+Scheggia e Pascelupo,54046,Perugia,PG,Umbria,10,6027
+Scheggino,54047,Perugia,PG,Umbria,10,6040
+Sellano,54048,Perugia,PG,Umbria,10,6030
+Sigillo,54049,Perugia,PG,Umbria,10,6028
+Spello,54050,Perugia,PG,Umbria,10,6038
+Spoleto,54051,Perugia,PG,Umbria,10,6049
+Todi,54052,Perugia,PG,Umbria,10,6059
+Torgiano,54053,Perugia,PG,Umbria,10,6089
+Trevi,54054,Perugia,PG,Umbria,10,6039
+Tuoro sul Trasimeno,54055,Perugia,PG,Umbria,10,6069
+Umbertide,54056,Perugia,PG,Umbria,10,6019
+Valfabbrica,54057,Perugia,PG,Umbria,10,6029
+Vallo di Nera,54058,Perugia,PG,Umbria,10,6040
+Valtopina,54059,Perugia,PG,Umbria,10,6030
+Acquasparta,55001,Terni,TR,Umbria,10,5021
+Allerona,55002,Terni,TR,Umbria,10,5011
+Alviano,55003,Terni,TR,Umbria,10,5020
+Amelia,55004,Terni,TR,Umbria,10,5022
+Arrone,55005,Terni,TR,Umbria,10,5031
+Attigliano,55006,Terni,TR,Umbria,10,5012
+Baschi,55007,Terni,TR,Umbria,10,5023
+Calvi dell'Umbria,55008,Terni,TR,Umbria,10,5032
+Castel Giorgio,55009,Terni,TR,Umbria,10,5013
+Castel Viscardo,55010,Terni,TR,Umbria,10,5014
+Fabro,55011,Terni,TR,Umbria,10,5015
+Ferentillo,55012,Terni,TR,Umbria,10,5034
+Ficulle,55013,Terni,TR,Umbria,10,5016
+Giove,55014,Terni,TR,Umbria,10,5024
+Guardea,55015,Terni,TR,Umbria,10,5025
+Lugnano in Teverina,55016,Terni,TR,Umbria,10,5020
+Montecastrilli,55017,Terni,TR,Umbria,10,5026
+Montecchio,55018,Terni,TR,Umbria,10,5020
+Montefranco,55019,Terni,TR,Umbria,10,5030
+Montegabbione,55020,Terni,TR,Umbria,10,5010
+Monteleone d'Orvieto,55021,Terni,TR,Umbria,10,5017
+Narni,55022,Terni,TR,Umbria,10,5035
+Orvieto,55023,Terni,TR,Umbria,10,5018
+Otricoli,55024,Terni,TR,Umbria,10,5030
+Parrano,55025,Terni,TR,Umbria,10,5010
+Penna in Teverina,55026,Terni,TR,Umbria,10,5028
+Polino,55027,Terni,TR,Umbria,10,5030
+Porano,55028,Terni,TR,Umbria,10,5010
+San Gemini,55029,Terni,TR,Umbria,10,5029
+San Venanzo,55030,Terni,TR,Umbria,10,5010
+Stroncone,55031,Terni,TR,Umbria,10,5039
+Terni,55032,Terni,TR,Umbria,10,5100
+Avigliano Umbro,55033,Terni,TR,Umbria,10,5020
+Acqualagna,41001,Pesaro e Urbino,PU,Marche,11,61041
+Apecchio,41002,Pesaro e Urbino,PU,Marche,11,61042
+Belforte all'Isauro,41005,Pesaro e Urbino,PU,Marche,11,61026
+Borgo Pace,41006,Pesaro e Urbino,PU,Marche,11,61040
+Cagli,41007,Pesaro e Urbino,PU,Marche,11,61043
+Cantiano,41008,Pesaro e Urbino,PU,Marche,11,61044
+Carpegna,41009,Pesaro e Urbino,PU,Marche,11,61021
+Cartoceto,41010,Pesaro e Urbino,PU,Marche,11,61030
+Fano,41013,Pesaro e Urbino,PU,Marche,11,61032
+Fermignano,41014,Pesaro e Urbino,PU,Marche,11,61033
+Fossombrone,41015,Pesaro e Urbino,PU,Marche,11,61034
+Fratte Rosa,41016,Pesaro e Urbino,PU,Marche,11,61040
+Frontino,41017,Pesaro e Urbino,PU,Marche,11,61021
+Frontone,41018,Pesaro e Urbino,PU,Marche,11,61040
+Gabicce Mare,41019,Pesaro e Urbino,PU,Marche,11,61011
+Gradara,41020,Pesaro e Urbino,PU,Marche,11,61012
+Isola del Piano,41021,Pesaro e Urbino,PU,Marche,11,61030
+Lunano,41022,Pesaro e Urbino,PU,Marche,11,61026
+Macerata Feltria,41023,Pesaro e Urbino,PU,Marche,11,61023
+Mercatello sul Metauro,41025,Pesaro e Urbino,PU,Marche,11,61040
+Mercatino Conca,41026,Pesaro e Urbino,PU,Marche,11,61013
+Mombaroccio,41027,Pesaro e Urbino,PU,Marche,11,61024
+Mondavio,41028,Pesaro e Urbino,PU,Marche,11,61040
+Mondolfo,41029,Pesaro e Urbino,PU,Marche,11,61037
+Montecalvo in Foglia,41030,Pesaro e Urbino,PU,Marche,11,61020
+Monte Cerignone,41031,Pesaro e Urbino,PU,Marche,11,61010
+Montecopiolo,99030,Rimini,RN,Emilia-Romagna,8,47868
+Montefelcino,41034,Pesaro e Urbino,PU,Marche,11,61030
+Monte Grimano Terme,41035,Pesaro e Urbino,PU,Marche,11,61010
+Montelabbate,41036,Pesaro e Urbino,PU,Marche,11,61025
+Monte Porzio,41038,Pesaro e Urbino,PU,Marche,11,61040
+Peglio,41041,Pesaro e Urbino,PU,Marche,11,61049
+Pergola,41043,Pesaro e Urbino,PU,Marche,11,61045
+Pesaro,41044,Pesaro e Urbino,PU,Marche,11,61024
+Petriano,41045,Pesaro e Urbino,PU,Marche,11,61020
+Piandimeleto,41047,Pesaro e Urbino,PU,Marche,11,61026
+Pietrarubbia,41048,Pesaro e Urbino,PU,Marche,11,61023
+Piobbico,41049,Pesaro e Urbino,PU,Marche,11,61046
+San Costanzo,41051,Pesaro e Urbino,PU,Marche,11,61039
+San Lorenzo in Campo,41054,Pesaro e Urbino,PU,Marche,11,61047
+Sant'Angelo in Vado,41057,Pesaro e Urbino,PU,Marche,11,61048
+Sant'Ippolito,41058,Pesaro e Urbino,PU,Marche,11,61040
+Sassofeltrio,99031,Rimini,RN,Emilia-Romagna,8,47869
+Serra Sant'Abbondio,41061,Pesaro e Urbino,PU,Marche,11,61040
+Tavoleto,41064,Pesaro e Urbino,PU,Marche,11,61020
+Tavullia,41065,Pesaro e Urbino,PU,Marche,11,61010
+Urbania,41066,Pesaro e Urbino,PU,Marche,11,61049
+Urbino,41067,Pesaro e Urbino,PU,Marche,11,61029
+Vallefoglia,41068,Pesaro e Urbino,PU,Marche,11,61022
+Colli al Metauro,41069,Pesaro e Urbino,PU,Marche,11,61030
+Terre Roveresche,41070,Pesaro e Urbino,PU,Marche,11,61038
+Sassocorvaro Auditore,41071,Pesaro e Urbino,PU,Marche,11,61028
+Agugliano,42001,Ancona,AN,Marche,11,60020
+Ancona,42002,Ancona,AN,Marche,11,60100
+Arcevia,42003,Ancona,AN,Marche,11,60011
+Barbara,42004,Ancona,AN,Marche,11,60010
+Belvedere Ostrense,42005,Ancona,AN,Marche,11,60030
+Camerano,42006,Ancona,AN,Marche,11,60021
+Camerata Picena,42007,Ancona,AN,Marche,11,60020
+Castelbellino,42008,Ancona,AN,Marche,11,60030
+Castelfidardo,42010,Ancona,AN,Marche,11,60022
+Castelleone di Suasa,42011,Ancona,AN,Marche,11,60010
+Castelplanio,42012,Ancona,AN,Marche,11,60031
+Cerreto d'Esi,42013,Ancona,AN,Marche,11,60043
+Chiaravalle,42014,Ancona,AN,Marche,11,60033
+Corinaldo,42015,Ancona,AN,Marche,11,60013
+Cupramontana,42016,Ancona,AN,Marche,11,60034
+Fabriano,42017,Ancona,AN,Marche,11,60044
+Falconara Marittima,42018,Ancona,AN,Marche,11,60015
+Filottrano,42019,Ancona,AN,Marche,11,60024
+Genga,42020,Ancona,AN,Marche,11,60040
+Jesi,42021,Ancona,AN,Marche,11,60035
+Loreto,42022,Ancona,AN,Marche,11,60025
+Maiolati Spontini,42023,Ancona,AN,Marche,11,60030
+Mergo,42024,Ancona,AN,Marche,11,60030
+Monsano,42025,Ancona,AN,Marche,11,60030
+Montecarotto,42026,Ancona,AN,Marche,11,60036
+Montemarciano,42027,Ancona,AN,Marche,11,60018
+Monte Roberto,42029,Ancona,AN,Marche,11,60030
+Monte San Vito,42030,Ancona,AN,Marche,11,60037
+Morro d'Alba,42031,Ancona,AN,Marche,11,60030
+Numana,42032,Ancona,AN,Marche,11,60026
+Offagna,42033,Ancona,AN,Marche,11,60020
+Osimo,42034,Ancona,AN,Marche,11,60027
+Ostra,42035,Ancona,AN,Marche,11,60010
+Ostra Vetere,42036,Ancona,AN,Marche,11,60010
+Poggio San Marcello,42037,Ancona,AN,Marche,11,60030
+Polverigi,42038,Ancona,AN,Marche,11,60020
+Rosora,42040,Ancona,AN,Marche,11,60030
+San Marcello,42041,Ancona,AN,Marche,11,60030
+San Paolo di Jesi,42042,Ancona,AN,Marche,11,60038
+Santa Maria Nuova,42043,Ancona,AN,Marche,11,60030
+Sassoferrato,42044,Ancona,AN,Marche,11,60041
+Senigallia,42045,Ancona,AN,Marche,11,60019
+Serra de' Conti,42046,Ancona,AN,Marche,11,60030
+Serra San Quirico,42047,Ancona,AN,Marche,11,60048
+Sirolo,42048,Ancona,AN,Marche,11,60020
+Staffolo,42049,Ancona,AN,Marche,11,60039
+Trecastelli,42050,Ancona,AN,Marche,11,60012
+Apiro,43002,Macerata,MC,Marche,11,62021
+Appignano,43003,Macerata,MC,Marche,11,62010
+Belforte del Chienti,43004,Macerata,MC,Marche,11,62020
+Bolognola,43005,Macerata,MC,Marche,11,62035
+Caldarola,43006,Macerata,MC,Marche,11,62020
+Camerino,43007,Macerata,MC,Marche,11,62032
+Camporotondo di Fiastrone,43008,Macerata,MC,Marche,11,62020
+Castelraimondo,43009,Macerata,MC,Marche,11,62022
+Castelsantangelo sul Nera,43010,Macerata,MC,Marche,11,62039
+Cessapalombo,43011,Macerata,MC,Marche,11,62020
+Cingoli,43012,Macerata,MC,Marche,11,62011
+Civitanova Marche,43013,Macerata,MC,Marche,11,62012
+Colmurano,43014,Macerata,MC,Marche,11,62020
+Corridonia,43015,Macerata,MC,Marche,11,62014
+Esanatoglia,43016,Macerata,MC,Marche,11,62024
+Fiastra,43017,Macerata,MC,Marche,11,62035
+Fiuminata,43019,Macerata,MC,Marche,11,62025
+Gagliole,43020,Macerata,MC,Marche,11,62022
+Gualdo,43021,Macerata,MC,Marche,11,62020
+Loro Piceno,43022,Macerata,MC,Marche,11,62020
+Macerata,43023,Macerata,MC,Marche,11,62100
+Matelica,43024,Macerata,MC,Marche,11,62024
+Mogliano,43025,Macerata,MC,Marche,11,62010
+Montecassiano,43026,Macerata,MC,Marche,11,62010
+Monte Cavallo,43027,Macerata,MC,Marche,11,62036
+Montecosaro,43028,Macerata,MC,Marche,11,62010
+Montefano,43029,Macerata,MC,Marche,11,62010
+Montelupone,43030,Macerata,MC,Marche,11,62010
+Monte San Giusto,43031,Macerata,MC,Marche,11,62015
+Monte San Martino,43032,Macerata,MC,Marche,11,62020
+Morrovalle,43033,Macerata,MC,Marche,11,62010
+Muccia,43034,Macerata,MC,Marche,11,62034
+Penna San Giovanni,43035,Macerata,MC,Marche,11,62020
+Petriolo,43036,Macerata,MC,Marche,11,62014
+Pieve Torina,43038,Macerata,MC,Marche,11,62036
+Pioraco,43039,Macerata,MC,Marche,11,62025
+Poggio San Vicino,43040,Macerata,MC,Marche,11,62021
+Pollenza,43041,Macerata,MC,Marche,11,62010
+Porto Recanati,43042,Macerata,MC,Marche,11,62017
+Potenza Picena,43043,Macerata,MC,Marche,11,62018
+Recanati,43044,Macerata,MC,Marche,11,62019
+Ripe San Ginesio,43045,Macerata,MC,Marche,11,62020
+San Ginesio,43046,Macerata,MC,Marche,11,62026
+San Severino Marche,43047,Macerata,MC,Marche,11,62027
+Sant'Angelo in Pontano,43048,Macerata,MC,Marche,11,62020
+Sarnano,43049,Macerata,MC,Marche,11,62028
+Sefro,43050,Macerata,MC,Marche,11,62025
+Serrapetrona,43051,Macerata,MC,Marche,11,62020
+Serravalle di Chienti,43052,Macerata,MC,Marche,11,62038
+Tolentino,43053,Macerata,MC,Marche,11,62029
+Treia,43054,Macerata,MC,Marche,11,62010
+Urbisaglia,43055,Macerata,MC,Marche,11,62010
+Ussita,43056,Macerata,MC,Marche,11,62039
+Visso,43057,Macerata,MC,Marche,11,62039
+Valfornace,43058,Macerata,MC,Marche,11,62035
+Acquasanta Terme,44001,Ascoli Piceno,AP,Marche,11,63095
+Acquaviva Picena,44002,Ascoli Piceno,AP,Marche,11,63075
+Appignano del Tronto,44005,Ascoli Piceno,AP,Marche,11,63083
+Arquata del Tronto,44006,Ascoli Piceno,AP,Marche,11,63096
+Ascoli Piceno,44007,Ascoli Piceno,AP,Marche,11,63100
+Carassai,44010,Ascoli Piceno,AP,Marche,11,63063
+Castel di Lama,44011,Ascoli Piceno,AP,Marche,11,63082
+Castignano,44012,Ascoli Piceno,AP,Marche,11,63072
+Castorano,44013,Ascoli Piceno,AP,Marche,11,63081
+Colli del Tronto,44014,Ascoli Piceno,AP,Marche,11,63079
+Comunanza,44015,Ascoli Piceno,AP,Marche,11,63087
+Cossignano,44016,Ascoli Piceno,AP,Marche,11,63067
+Cupra Marittima,44017,Ascoli Piceno,AP,Marche,11,63064
+Folignano,44020,Ascoli Piceno,AP,Marche,11,63084
+Force,44021,Ascoli Piceno,AP,Marche,11,63086
+Grottammare,44023,Ascoli Piceno,AP,Marche,11,63066
+Maltignano,44027,Ascoli Piceno,AP,Marche,11,63085
+Massignano,44029,Ascoli Piceno,AP,Marche,11,63061
+Monsampolo del Tronto,44031,Ascoli Piceno,AP,Marche,11,63077
+Montalto delle Marche,44032,Ascoli Piceno,AP,Marche,11,63068
+Montedinove,44034,Ascoli Piceno,AP,Marche,11,63069
+Montefiore dell'Aso,44036,Ascoli Piceno,AP,Marche,11,63062
+Montegallo,44038,Ascoli Piceno,AP,Marche,11,63094
+Montemonaco,44044,Ascoli Piceno,AP,Marche,11,63088
+Monteprandone,44045,Ascoli Piceno,AP,Marche,11,63076
+Offida,44054,Ascoli Piceno,AP,Marche,11,63073
+Palmiano,44056,Ascoli Piceno,AP,Marche,11,63092
+Ripatransone,44063,Ascoli Piceno,AP,Marche,11,63065
+Roccafluvione,44064,Ascoli Piceno,AP,Marche,11,63093
+Rotella,44065,Ascoli Piceno,AP,Marche,11,63071
+San Benedetto del Tronto,44066,Ascoli Piceno,AP,Marche,11,63074
+Spinetoli,44071,Ascoli Piceno,AP,Marche,11,63078
+Venarotta,44073,Ascoli Piceno,AP,Marche,11,63091
+Altidona,109001,Fermo,FM,Marche,11,63824
+Amandola,109002,Fermo,FM,Marche,11,63857
+Belmonte Piceno,109003,Fermo,FM,Marche,11,63838
+Campofilone,109004,Fermo,FM,Marche,11,63828
+Falerone,109005,Fermo,FM,Marche,11,63837
+Fermo,109006,Fermo,FM,Marche,11,63900
+Francavilla d'Ete,109007,Fermo,FM,Marche,11,63816
+Grottazzolina,109008,Fermo,FM,Marche,11,63844
+Lapedona,109009,Fermo,FM,Marche,11,63823
+Magliano di Tenna,109010,Fermo,FM,Marche,11,63832
+Massa Fermana,109011,Fermo,FM,Marche,11,63834
+Monsampietro Morico,109012,Fermo,FM,Marche,11,63842
+Montappone,109013,Fermo,FM,Marche,11,63835
+Montefalcone Appennino,109014,Fermo,FM,Marche,11,63855
+Montefortino,109015,Fermo,FM,Marche,11,63858
+Monte Giberto,109016,Fermo,FM,Marche,11,63846
+Montegiorgio,109017,Fermo,FM,Marche,11,63833
+Montegranaro,109018,Fermo,FM,Marche,11,63812
+Monteleone di Fermo,109019,Fermo,FM,Marche,11,63841
+Montelparo,109020,Fermo,FM,Marche,11,63853
+Monte Rinaldo,109021,Fermo,FM,Marche,11,63852
+Monterubbiano,109022,Fermo,FM,Marche,11,63825
+Monte San Pietrangeli,109023,Fermo,FM,Marche,11,63815
+Monte Urano,109024,Fermo,FM,Marche,11,63813
+Monte Vidon Combatte,109025,Fermo,FM,Marche,11,63847
+Monte Vidon Corrado,109026,Fermo,FM,Marche,11,63836
+Montottone,109027,Fermo,FM,Marche,11,63843
+Moresco,109028,Fermo,FM,Marche,11,63826
+Ortezzano,109029,Fermo,FM,Marche,11,63851
+Pedaso,109030,Fermo,FM,Marche,11,63827
+Petritoli,109031,Fermo,FM,Marche,11,63848
+Ponzano di Fermo,109032,Fermo,FM,Marche,11,63845
+Porto San Giorgio,109033,Fermo,FM,Marche,11,63822
+Porto Sant'Elpidio,109034,Fermo,FM,Marche,11,63821
+Rapagnano,109035,Fermo,FM,Marche,11,63831
+Santa Vittoria in Matenano,109036,Fermo,FM,Marche,11,63854
+Sant'Elpidio a Mare,109037,Fermo,FM,Marche,11,63811
+Servigliano,109038,Fermo,FM,Marche,11,63839
+Smerillo,109039,Fermo,FM,Marche,11,63856
+Torre San Patrizio,109040,Fermo,FM,Marche,11,63814
+Acquapendente,56001,Viterbo,VT,Lazio,12,1021
+Arlena di Castro,56002,Viterbo,VT,Lazio,12,1010
+Bagnoregio,56003,Viterbo,VT,Lazio,12,1022
+Barbarano Romano,56004,Viterbo,VT,Lazio,12,1010
+Bassano Romano,56005,Viterbo,VT,Lazio,12,1030
+Bassano in Teverina,56006,Viterbo,VT,Lazio,12,1030
+Blera,56007,Viterbo,VT,Lazio,12,1010
+Bolsena,56008,Viterbo,VT,Lazio,12,1023
+Bomarzo,56009,Viterbo,VT,Lazio,12,1020
+Calcata,56010,Viterbo,VT,Lazio,12,1030
+Canepina,56011,Viterbo,VT,Lazio,12,1030
+Canino,56012,Viterbo,VT,Lazio,12,1011
+Capodimonte,56013,Viterbo,VT,Lazio,12,1010
+Capranica,56014,Viterbo,VT,Lazio,12,1012
+Caprarola,56015,Viterbo,VT,Lazio,12,1032
+Carbognano,56016,Viterbo,VT,Lazio,12,1030
+Castel Sant'Elia,56017,Viterbo,VT,Lazio,12,1030
+Castiglione in Teverina,56018,Viterbo,VT,Lazio,12,1024
+Celleno,56019,Viterbo,VT,Lazio,12,1020
+Cellere,56020,Viterbo,VT,Lazio,12,1010
+Civita Castellana,56021,Viterbo,VT,Lazio,12,1033
+Civitella d'Agliano,56022,Viterbo,VT,Lazio,12,1020
+Corchiano,56023,Viterbo,VT,Lazio,12,1030
+Fabrica di Roma,56024,Viterbo,VT,Lazio,12,1034
+Faleria,56025,Viterbo,VT,Lazio,12,1030
+Farnese,56026,Viterbo,VT,Lazio,12,1010
+Gallese,56027,Viterbo,VT,Lazio,12,1035
+Gradoli,56028,Viterbo,VT,Lazio,12,1010
+Graffignano,56029,Viterbo,VT,Lazio,12,1020
+Grotte di Castro,56030,Viterbo,VT,Lazio,12,1025
+Ischia di Castro,56031,Viterbo,VT,Lazio,12,1010
+Latera,56032,Viterbo,VT,Lazio,12,1010
+Lubriano,56033,Viterbo,VT,Lazio,12,1020
+Marta,56034,Viterbo,VT,Lazio,12,1010
+Montalto di Castro,56035,Viterbo,VT,Lazio,12,1014
+Montefiascone,56036,Viterbo,VT,Lazio,12,1027
+Monte Romano,56037,Viterbo,VT,Lazio,12,1010
+Monterosi,56038,Viterbo,VT,Lazio,12,1030
+Nepi,56039,Viterbo,VT,Lazio,12,1036
+Onano,56040,Viterbo,VT,Lazio,12,1010
+Oriolo Romano,56041,Viterbo,VT,Lazio,12,1010
+Orte,56042,Viterbo,VT,Lazio,12,1028
+Piansano,56043,Viterbo,VT,Lazio,12,1010
+Proceno,56044,Viterbo,VT,Lazio,12,1020
+Ronciglione,56045,Viterbo,VT,Lazio,12,1037
+Villa San Giovanni in Tuscia,56046,Viterbo,VT,Lazio,12,1010
+San Lorenzo Nuovo,56047,Viterbo,VT,Lazio,12,1020
+Soriano nel Cimino,56048,Viterbo,VT,Lazio,12,1038
+Sutri,56049,Viterbo,VT,Lazio,12,1015
+Tarquinia,56050,Viterbo,VT,Lazio,12,1016
+Tessennano,56051,Viterbo,VT,Lazio,12,1010
+Tuscania,56052,Viterbo,VT,Lazio,12,1017
+Valentano,56053,Viterbo,VT,Lazio,12,1018
+Vallerano,56054,Viterbo,VT,Lazio,12,1030
+Vasanello,56055,Viterbo,VT,Lazio,12,1030
+Vejano,56056,Viterbo,VT,Lazio,12,1010
+Vetralla,56057,Viterbo,VT,Lazio,12,1019
+Vignanello,56058,Viterbo,VT,Lazio,12,1039
+Viterbo,56059,Viterbo,VT,Lazio,12,1100
+Vitorchiano,56060,Viterbo,VT,Lazio,12,1030
+Accumoli,57001,Rieti,RI,Lazio,12,2011
+Amatrice,57002,Rieti,RI,Lazio,12,2012
+Antrodoco,57003,Rieti,RI,Lazio,12,2013
+Ascrea,57004,Rieti,RI,Lazio,12,2020
+Belmonte in Sabina,57005,Rieti,RI,Lazio,12,2020
+Borbona,57006,Rieti,RI,Lazio,12,2010
+Borgorose,57007,Rieti,RI,Lazio,12,2021
+Borgo Velino,57008,Rieti,RI,Lazio,12,2010
+Cantalice,57009,Rieti,RI,Lazio,12,2014
+Cantalupo in Sabina,57010,Rieti,RI,Lazio,12,2040
+Casaprota,57011,Rieti,RI,Lazio,12,2030
+Casperia,57012,Rieti,RI,Lazio,12,2041
+Castel di Tora,57013,Rieti,RI,Lazio,12,2020
+Castelnuovo di Farfa,57014,Rieti,RI,Lazio,12,2031
+Castel Sant'Angelo,57015,Rieti,RI,Lazio,12,2010
+Cittaducale,57016,Rieti,RI,Lazio,12,2015
+Cittareale,57017,Rieti,RI,Lazio,12,2010
+Collalto Sabino,57018,Rieti,RI,Lazio,12,2022
+Colle di Tora,57019,Rieti,RI,Lazio,12,2020
+Collegiove,57020,Rieti,RI,Lazio,12,2020
+Collevecchio,57021,Rieti,RI,Lazio,12,2042
+Colli sul Velino,57022,Rieti,RI,Lazio,12,2010
+Concerviano,57023,Rieti,RI,Lazio,12,2020
+Configni,57024,Rieti,RI,Lazio,12,2040
+Contigliano,57025,Rieti,RI,Lazio,12,2043
+Cottanello,57026,Rieti,RI,Lazio,12,2040
+Fara in Sabina,57027,Rieti,RI,Lazio,12,2032
+Fiamignano,57028,Rieti,RI,Lazio,12,2023
+Forano,57029,Rieti,RI,Lazio,12,2044
+Frasso Sabino,57030,Rieti,RI,Lazio,12,2030
+Greccio,57031,Rieti,RI,Lazio,12,2045
+Labro,57032,Rieti,RI,Lazio,12,2010
+Leonessa,57033,Rieti,RI,Lazio,12,2016
+Longone Sabino,57034,Rieti,RI,Lazio,12,2020
+Magliano Sabina,57035,Rieti,RI,Lazio,12,2046
+Marcetelli,57036,Rieti,RI,Lazio,12,2020
+Micigliano,57037,Rieti,RI,Lazio,12,2010
+Mompeo,57038,Rieti,RI,Lazio,12,2040
+Montasola,57039,Rieti,RI,Lazio,12,2040
+Montebuono,57040,Rieti,RI,Lazio,12,2040
+Monteleone Sabino,57041,Rieti,RI,Lazio,12,2033
+Montenero Sabino,57042,Rieti,RI,Lazio,12,2040
+Monte San Giovanni in Sabina,57043,Rieti,RI,Lazio,12,2040
+Montopoli di Sabina,57044,Rieti,RI,Lazio,12,2034
+Morro Reatino,57045,Rieti,RI,Lazio,12,2010
+Nespolo,57046,Rieti,RI,Lazio,12,2020
+Orvinio,57047,Rieti,RI,Lazio,12,2035
+Paganico Sabino,57048,Rieti,RI,Lazio,12,2020
+Pescorocchiano,57049,Rieti,RI,Lazio,12,2024
+Petrella Salto,57050,Rieti,RI,Lazio,12,2025
+Poggio Bustone,57051,Rieti,RI,Lazio,12,2018
+Poggio Catino,57052,Rieti,RI,Lazio,12,2040
+Poggio Mirteto,57053,Rieti,RI,Lazio,12,2047
+Poggio Moiano,57054,Rieti,RI,Lazio,12,2037
+Poggio Nativo,57055,Rieti,RI,Lazio,12,2030
+Poggio San Lorenzo,57056,Rieti,RI,Lazio,12,2030
+Posta,57057,Rieti,RI,Lazio,12,2019
+Pozzaglia Sabina,57058,Rieti,RI,Lazio,12,2030
+Rieti,57059,Rieti,RI,Lazio,12,2100
+Rivodutri,57060,Rieti,RI,Lazio,12,2010
+Roccantica,57061,Rieti,RI,Lazio,12,2040
+Rocca Sinibalda,57062,Rieti,RI,Lazio,12,2026
+Salisano,57063,Rieti,RI,Lazio,12,2040
+Scandriglia,57064,Rieti,RI,Lazio,12,2038
+Selci,57065,Rieti,RI,Lazio,12,2040
+Stimigliano,57066,Rieti,RI,Lazio,12,2048
+Tarano,57067,Rieti,RI,Lazio,12,2040
+Toffia,57068,Rieti,RI,Lazio,12,2039
+Torricella in Sabina,57069,Rieti,RI,Lazio,12,2030
+Torri in Sabina,57070,Rieti,RI,Lazio,12,2049
+Turania,57071,Rieti,RI,Lazio,12,2020
+Vacone,57072,Rieti,RI,Lazio,12,2040
+Varco Sabino,57073,Rieti,RI,Lazio,12,2020
+Affile,58001,Roma,RM,Lazio,12,21
+Agosta,58002,Roma,RM,Lazio,12,20
+Albano Laziale,58003,Roma,RM,Lazio,12,41
+Allumiere,58004,Roma,RM,Lazio,12,51
+Anguillara Sabazia,58005,Roma,RM,Lazio,12,61
+Anticoli Corrado,58006,Roma,RM,Lazio,12,22
+Anzio,58007,Roma,RM,Lazio,12,42
+Arcinazzo Romano,58008,Roma,RM,Lazio,12,20
+Ariccia,58009,Roma,RM,Lazio,12,40
+Arsoli,58010,Roma,RM,Lazio,12,23
+Artena,58011,Roma,RM,Lazio,12,31
+Bellegra,58012,Roma,RM,Lazio,12,30
+Bracciano,58013,Roma,RM,Lazio,12,62
+Camerata Nuova,58014,Roma,RM,Lazio,12,20
+Campagnano di Roma,58015,Roma,RM,Lazio,12,63
+Canale Monterano,58016,Roma,RM,Lazio,12,60
+Canterano,58017,Roma,RM,Lazio,12,20
+Capena,58018,Roma,RM,Lazio,12,60
+Capranica Prenestina,58019,Roma,RM,Lazio,12,30
+Carpineto Romano,58020,Roma,RM,Lazio,12,32
+Casape,58021,Roma,RM,Lazio,12,10
+Castel Gandolfo,58022,Roma,RM,Lazio,12,40
+Castel Madama,58023,Roma,RM,Lazio,12,24
+Castelnuovo di Porto,58024,Roma,RM,Lazio,12,60
+Castel San Pietro Romano,58025,Roma,RM,Lazio,12,30
+Cave,58026,Roma,RM,Lazio,12,33
+Cerreto Laziale,58027,Roma,RM,Lazio,12,20
+Cervara di Roma,58028,Roma,RM,Lazio,12,20
+Cerveteri,58029,Roma,RM,Lazio,12,52
+Ciciliano,58030,Roma,RM,Lazio,12,20
+Cineto Romano,58031,Roma,RM,Lazio,12,20
+Civitavecchia,58032,Roma,RM,Lazio,12,53
+Civitella San Paolo,58033,Roma,RM,Lazio,12,60
+Colleferro,58034,Roma,RM,Lazio,12,34
+Colonna,58035,Roma,RM,Lazio,12,30
+Fiano Romano,58036,Roma,RM,Lazio,12,65
+Filacciano,58037,Roma,RM,Lazio,12,60
+Formello,58038,Roma,RM,Lazio,12,60
+Frascati,58039,Roma,RM,Lazio,12,44
+Gallicano nel Lazio,58040,Roma,RM,Lazio,12,10
+Gavignano,58041,Roma,RM,Lazio,12,30
+Genazzano,58042,Roma,RM,Lazio,12,30
+Genzano di Roma,58043,Roma,RM,Lazio,12,45
+Gerano,58044,Roma,RM,Lazio,12,25
+Gorga,58045,Roma,RM,Lazio,12,30
+Grottaferrata,58046,Roma,RM,Lazio,12,46
+Guidonia Montecelio,58047,Roma,RM,Lazio,12,12
+Jenne,58048,Roma,RM,Lazio,12,20
+Labico,58049,Roma,RM,Lazio,12,30
+Lanuvio,58050,Roma,RM,Lazio,12,40
+Licenza,58051,Roma,RM,Lazio,12,26
+Magliano Romano,58052,Roma,RM,Lazio,12,60
+Mandela,58053,Roma,RM,Lazio,12,20
+Manziana,58054,Roma,RM,Lazio,12,66
+Marano Equo,58055,Roma,RM,Lazio,12,20
+Marcellina,58056,Roma,RM,Lazio,12,10
+Marino,58057,Roma,RM,Lazio,12,47
+Mazzano Romano,58058,Roma,RM,Lazio,12,60
+Mentana,58059,Roma,RM,Lazio,12,13
+Monte Compatri,58060,Roma,RM,Lazio,12,40
+Monteflavio,58061,Roma,RM,Lazio,12,10
+Montelanico,58062,Roma,RM,Lazio,12,30
+Montelibretti,58063,Roma,RM,Lazio,12,10
+Monte Porzio Catone,58064,Roma,RM,Lazio,12,40
+Monterotondo,58065,Roma,RM,Lazio,12,15
+Montorio Romano,58066,Roma,RM,Lazio,12,10
+Moricone,58067,Roma,RM,Lazio,12,10
+Morlupo,58068,Roma,RM,Lazio,12,67
+Nazzano,58069,Roma,RM,Lazio,12,60
+Nemi,58070,Roma,RM,Lazio,12,40
+Nerola,58071,Roma,RM,Lazio,12,17
+Nettuno,58072,Roma,RM,Lazio,12,48
+Olevano Romano,58073,Roma,RM,Lazio,12,35
+Palestrina,58074,Roma,RM,Lazio,12,36
+Palombara Sabina,58075,Roma,RM,Lazio,12,18
+Percile,58076,Roma,RM,Lazio,12,20
+Pisoniano,58077,Roma,RM,Lazio,12,20
+Poli,58078,Roma,RM,Lazio,12,10
+Pomezia,58079,Roma,RM,Lazio,12,71
+Ponzano Romano,58080,Roma,RM,Lazio,12,60
+Riano,58081,Roma,RM,Lazio,12,60
+Rignano Flaminio,58082,Roma,RM,Lazio,12,68
+Riofreddo,58083,Roma,RM,Lazio,12,20
+Rocca Canterano,58084,Roma,RM,Lazio,12,20
+Rocca di Cave,58085,Roma,RM,Lazio,12,30
+Rocca di Papa,58086,Roma,RM,Lazio,12,40
+Roccagiovine,58087,Roma,RM,Lazio,12,20
+Rocca Priora,58088,Roma,RM,Lazio,12,40
+Rocca Santo Stefano,58089,Roma,RM,Lazio,12,30
+Roiate,58090,Roma,RM,Lazio,12,30
+Roma,58091,Roma,RM,Lazio,12,186
+Roviano,58092,Roma,RM,Lazio,12,27
+Sacrofano,58093,Roma,RM,Lazio,12,60
+Sambuci,58094,Roma,RM,Lazio,12,20
+San Gregorio da Sassola,58095,Roma,RM,Lazio,12,10
+San Polo dei Cavalieri,58096,Roma,RM,Lazio,12,10
+Santa Marinella,58097,Roma,RM,Lazio,12,58
+Sant'Angelo Romano,58098,Roma,RM,Lazio,12,10
+Sant'Oreste,58099,Roma,RM,Lazio,12,60
+San Vito Romano,58100,Roma,RM,Lazio,12,30
+Saracinesco,58101,Roma,RM,Lazio,12,20
+Segni,58102,Roma,RM,Lazio,12,37
+Subiaco,58103,Roma,RM,Lazio,12,28
+Tivoli,58104,Roma,RM,Lazio,12,19
+Tolfa,58105,Roma,RM,Lazio,12,59
+Torrita Tiberina,58106,Roma,RM,Lazio,12,60
+Trevignano Romano,58107,Roma,RM,Lazio,12,69
+Vallepietra,58108,Roma,RM,Lazio,12,20
+Vallinfreda,58109,Roma,RM,Lazio,12,20
+Valmontone,58110,Roma,RM,Lazio,12,38
+Velletri,58111,Roma,RM,Lazio,12,49
+Vicovaro,58112,Roma,RM,Lazio,12,29
+Vivaro Romano,58113,Roma,RM,Lazio,12,20
+Zagarolo,58114,Roma,RM,Lazio,12,39
+Lariano,58115,Roma,RM,Lazio,12,40
+Ladispoli,58116,Roma,RM,Lazio,12,55
+Ardea,58117,Roma,RM,Lazio,12,40
+Ciampino,58118,Roma,RM,Lazio,12,43
+San Cesareo,58119,Roma,RM,Lazio,12,30
+Fiumicino,58120,Roma,RM,Lazio,12,54
+Fonte Nuova,58122,Roma,RM,Lazio,12,13
+Aprilia,59001,Latina,LT,Lazio,12,4011
+Bassiano,59002,Latina,LT,Lazio,12,4010
+Campodimele,59003,Latina,LT,Lazio,12,4020
+Castelforte,59004,Latina,LT,Lazio,12,4021
+Cisterna di Latina,59005,Latina,LT,Lazio,12,4012
+Cori,59006,Latina,LT,Lazio,12,4010
+Fondi,59007,Latina,LT,Lazio,12,4022
+Formia,59008,Latina,LT,Lazio,12,4023
+Gaeta,59009,Latina,LT,Lazio,12,4024
+Itri,59010,Latina,LT,Lazio,12,4020
+Latina,59011,Latina,LT,Lazio,12,4100
+Lenola,59012,Latina,LT,Lazio,12,4025
+Maenza,59013,Latina,LT,Lazio,12,4010
+Minturno,59014,Latina,LT,Lazio,12,4026
+Monte San Biagio,59015,Latina,LT,Lazio,12,4020
+Norma,59016,Latina,LT,Lazio,12,4010
+Pontinia,59017,Latina,LT,Lazio,12,4014
+Ponza,59018,Latina,LT,Lazio,12,4027
+Priverno,59019,Latina,LT,Lazio,12,4015
+Prossedi,59020,Latina,LT,Lazio,12,4010
+Roccagorga,59021,Latina,LT,Lazio,12,4010
+Rocca Massima,59022,Latina,LT,Lazio,12,4010
+Roccasecca dei Volsci,59023,Latina,LT,Lazio,12,4010
+Sabaudia,59024,Latina,LT,Lazio,12,4016
+San Felice Circeo,59025,Latina,LT,Lazio,12,4017
+Santi Cosma e Damiano,59026,Latina,LT,Lazio,12,4020
+Sermoneta,59027,Latina,LT,Lazio,12,4013
+Sezze,59028,Latina,LT,Lazio,12,4018
+Sonnino,59029,Latina,LT,Lazio,12,4010
+Sperlonga,59030,Latina,LT,Lazio,12,4029
+Spigno Saturnia,59031,Latina,LT,Lazio,12,4020
+Terracina,59032,Latina,LT,Lazio,12,4019
+Ventotene,59033,Latina,LT,Lazio,12,4020
+Acquafondata,60001,Frosinone,FR,Lazio,12,3040
+Acuto,60002,Frosinone,FR,Lazio,12,3010
+Alatri,60003,Frosinone,FR,Lazio,12,3011
+Alvito,60004,Frosinone,FR,Lazio,12,3041
+Amaseno,60005,Frosinone,FR,Lazio,12,3021
+Anagni,60006,Frosinone,FR,Lazio,12,3012
+Aquino,60007,Frosinone,FR,Lazio,12,3031
+Arce,60008,Frosinone,FR,Lazio,12,3032
+Arnara,60009,Frosinone,FR,Lazio,12,3020
+Arpino,60010,Frosinone,FR,Lazio,12,3033
+Atina,60011,Frosinone,FR,Lazio,12,3042
+Ausonia,60012,Frosinone,FR,Lazio,12,3040
+Belmonte Castello,60013,Frosinone,FR,Lazio,12,3040
+Boville Ernica,60014,Frosinone,FR,Lazio,12,3022
+Broccostella,60015,Frosinone,FR,Lazio,12,3030
+Campoli Appennino,60016,Frosinone,FR,Lazio,12,3030
+Casalattico,60017,Frosinone,FR,Lazio,12,3030
+Casalvieri,60018,Frosinone,FR,Lazio,12,3034
+Cassino,60019,Frosinone,FR,Lazio,12,3043
+Castelliri,60020,Frosinone,FR,Lazio,12,3030
+Castelnuovo Parano,60021,Frosinone,FR,Lazio,12,3040
+Castrocielo,60022,Frosinone,FR,Lazio,12,3030
+Castro dei Volsci,60023,Frosinone,FR,Lazio,12,3020
+Ceccano,60024,Frosinone,FR,Lazio,12,3023
+Ceprano,60025,Frosinone,FR,Lazio,12,3024
+Cervaro,60026,Frosinone,FR,Lazio,12,3044
+Colfelice,60027,Frosinone,FR,Lazio,12,3030
+Collepardo,60028,Frosinone,FR,Lazio,12,3010
+Colle San Magno,60029,Frosinone,FR,Lazio,12,3030
+Coreno Ausonio,60030,Frosinone,FR,Lazio,12,3040
+Esperia,60031,Frosinone,FR,Lazio,12,3045
+Falvaterra,60032,Frosinone,FR,Lazio,12,3020
+Ferentino,60033,Frosinone,FR,Lazio,12,3013
+Filettino,60034,Frosinone,FR,Lazio,12,3010
+Fiuggi,60035,Frosinone,FR,Lazio,12,3014
+Fontana Liri,60036,Frosinone,FR,Lazio,12,3035
+Fontechiari,60037,Frosinone,FR,Lazio,12,3030
+Frosinone,60038,Frosinone,FR,Lazio,12,3100
+Fumone,60039,Frosinone,FR,Lazio,12,3010
+Gallinaro,60040,Frosinone,FR,Lazio,12,3040
+Giuliano di Roma,60041,Frosinone,FR,Lazio,12,3020
+Guarcino,60042,Frosinone,FR,Lazio,12,3016
+Isola del Liri,60043,Frosinone,FR,Lazio,12,3036
+Monte San Giovanni Campano,60044,Frosinone,FR,Lazio,12,3025
+Morolo,60045,Frosinone,FR,Lazio,12,3017
+Paliano,60046,Frosinone,FR,Lazio,12,3018
+Pastena,60047,Frosinone,FR,Lazio,12,3020
+Patrica,60048,Frosinone,FR,Lazio,12,3010
+Pescosolido,60049,Frosinone,FR,Lazio,12,3030
+Picinisco,60050,Frosinone,FR,Lazio,12,3040
+Pico,60051,Frosinone,FR,Lazio,12,3020
+Piedimonte San Germano,60052,Frosinone,FR,Lazio,12,3030
+Piglio,60053,Frosinone,FR,Lazio,12,3010
+Pignataro Interamna,60054,Frosinone,FR,Lazio,12,3040
+Pofi,60055,Frosinone,FR,Lazio,12,3026
+Pontecorvo,60056,Frosinone,FR,Lazio,12,3037
+Posta Fibreno,60057,Frosinone,FR,Lazio,12,3030
+Ripi,60058,Frosinone,FR,Lazio,12,3027
+Rocca d'Arce,60059,Frosinone,FR,Lazio,12,3030
+Roccasecca,60060,Frosinone,FR,Lazio,12,3038
+San Biagio Saracinisco,60061,Frosinone,FR,Lazio,12,3040
+San Donato Val di Comino,60062,Frosinone,FR,Lazio,12,3046
+San Giorgio a Liri,60063,Frosinone,FR,Lazio,12,3047
+San Giovanni Incarico,60064,Frosinone,FR,Lazio,12,3028
+Sant'Ambrogio sul Garigliano,60065,Frosinone,FR,Lazio,12,3040
+Sant'Andrea del Garigliano,60066,Frosinone,FR,Lazio,12,3040
+Sant'Apollinare,60067,Frosinone,FR,Lazio,12,3048
+Sant'Elia Fiumerapido,60068,Frosinone,FR,Lazio,12,3049
+Santopadre,60069,Frosinone,FR,Lazio,12,3030
+San Vittore del Lazio,60070,Frosinone,FR,Lazio,12,3040
+Serrone,60071,Frosinone,FR,Lazio,12,3010
+Settefrati,60072,Frosinone,FR,Lazio,12,3040
+Sgurgola,60073,Frosinone,FR,Lazio,12,3010
+Sora,60074,Frosinone,FR,Lazio,12,3039
+Strangolagalli,60075,Frosinone,FR,Lazio,12,3020
+Supino,60076,Frosinone,FR,Lazio,12,3019
+Terelle,60077,Frosinone,FR,Lazio,12,3040
+Torre Cajetani,60078,Frosinone,FR,Lazio,12,3010
+Torrice,60079,Frosinone,FR,Lazio,12,3020
+Trevi nel Lazio,60080,Frosinone,FR,Lazio,12,3010
+Trivigliano,60081,Frosinone,FR,Lazio,12,3010
+Vallecorsa,60082,Frosinone,FR,Lazio,12,3020
+Vallemaio,60083,Frosinone,FR,Lazio,12,3040
+Vallerotonda,60084,Frosinone,FR,Lazio,12,3040
+Veroli,60085,Frosinone,FR,Lazio,12,3029
+Vicalvi,60086,Frosinone,FR,Lazio,12,3030
+Vico nel Lazio,60087,Frosinone,FR,Lazio,12,3010
+Villa Latina,60088,Frosinone,FR,Lazio,12,3040
+Villa Santa Lucia,60089,Frosinone,FR,Lazio,12,3030
+Villa Santo Stefano,60090,Frosinone,FR,Lazio,12,3020
+Viticuso,60091,Frosinone,FR,Lazio,12,3040
+Acciano,66001,L'Aquila,AQ,Abruzzo,13,67020
+Aielli,66002,L'Aquila,AQ,Abruzzo,13,67041
+Alfedena,66003,L'Aquila,AQ,Abruzzo,13,67030
+Anversa degli Abruzzi,66004,L'Aquila,AQ,Abruzzo,13,67030
+Ateleta,66005,L'Aquila,AQ,Abruzzo,13,67030
+Avezzano,66006,L'Aquila,AQ,Abruzzo,13,67051
+Balsorano,66007,L'Aquila,AQ,Abruzzo,13,67052
+Barete,66008,L'Aquila,AQ,Abruzzo,13,67010
+Barisciano,66009,L'Aquila,AQ,Abruzzo,13,67021
+Barrea,66010,L'Aquila,AQ,Abruzzo,13,67030
+Bisegna,66011,L'Aquila,AQ,Abruzzo,13,67050
+Bugnara,66012,L'Aquila,AQ,Abruzzo,13,67030
+Cagnano Amiterno,66013,L'Aquila,AQ,Abruzzo,13,67012
+Calascio,66014,L'Aquila,AQ,Abruzzo,13,67020
+Campo di Giove,66015,L'Aquila,AQ,Abruzzo,13,67030
+Campotosto,66016,L'Aquila,AQ,Abruzzo,13,67013
+Canistro,66017,L'Aquila,AQ,Abruzzo,13,67050
+Cansano,66018,L'Aquila,AQ,Abruzzo,13,67030
+Capestrano,66019,L'Aquila,AQ,Abruzzo,13,67022
+Capistrello,66020,L'Aquila,AQ,Abruzzo,13,67053
+Capitignano,66021,L'Aquila,AQ,Abruzzo,13,67014
+Caporciano,66022,L'Aquila,AQ,Abruzzo,13,67020
+Cappadocia,66023,L'Aquila,AQ,Abruzzo,13,67060
+Carapelle Calvisio,66024,L'Aquila,AQ,Abruzzo,13,67020
+Carsoli,66025,L'Aquila,AQ,Abruzzo,13,67061
+Castel del Monte,66026,L'Aquila,AQ,Abruzzo,13,67023
+Castel di Ieri,66027,L'Aquila,AQ,Abruzzo,13,67020
+Castel di Sangro,66028,L'Aquila,AQ,Abruzzo,13,67031
+Castellafiume,66029,L'Aquila,AQ,Abruzzo,13,67050
+Castelvecchio Calvisio,66030,L'Aquila,AQ,Abruzzo,13,67020
+Castelvecchio Subequo,66031,L'Aquila,AQ,Abruzzo,13,67024
+Celano,66032,L'Aquila,AQ,Abruzzo,13,67043
+Cerchio,66033,L'Aquila,AQ,Abruzzo,13,67044
+Civita d'Antino,66034,L'Aquila,AQ,Abruzzo,13,67050
+Civitella Alfedena,66035,L'Aquila,AQ,Abruzzo,13,67030
+Civitella Roveto,66036,L'Aquila,AQ,Abruzzo,13,67054
+Cocullo,66037,L'Aquila,AQ,Abruzzo,13,67030
+Collarmele,66038,L'Aquila,AQ,Abruzzo,13,67040
+Collelongo,66039,L'Aquila,AQ,Abruzzo,13,67050
+Collepietro,66040,L'Aquila,AQ,Abruzzo,13,67020
+Corfinio,66041,L'Aquila,AQ,Abruzzo,13,67030
+Fagnano Alto,66042,L'Aquila,AQ,Abruzzo,13,67020
+Fontecchio,66043,L'Aquila,AQ,Abruzzo,13,67020
+Fossa,66044,L'Aquila,AQ,Abruzzo,13,67020
+Gagliano Aterno,66045,L'Aquila,AQ,Abruzzo,13,67020
+Gioia dei Marsi,66046,L'Aquila,AQ,Abruzzo,13,67055
+Goriano Sicoli,66047,L'Aquila,AQ,Abruzzo,13,67030
+Introdacqua,66048,L'Aquila,AQ,Abruzzo,13,67030
+L'Aquila,66049,L'Aquila,AQ,Abruzzo,13,67100
+Lecce nei Marsi,66050,L'Aquila,AQ,Abruzzo,13,67050
+Luco dei Marsi,66051,L'Aquila,AQ,Abruzzo,13,67056
+Lucoli,66052,L'Aquila,AQ,Abruzzo,13,67045
+Magliano de' Marsi,66053,L'Aquila,AQ,Abruzzo,13,67062
+Massa d'Albe,66054,L'Aquila,AQ,Abruzzo,13,67050
+Molina Aterno,66055,L'Aquila,AQ,Abruzzo,13,67020
+Montereale,66056,L'Aquila,AQ,Abruzzo,13,67015
+Morino,66057,L'Aquila,AQ,Abruzzo,13,67050
+Navelli,66058,L'Aquila,AQ,Abruzzo,13,67020
+Ocre,66059,L'Aquila,AQ,Abruzzo,13,67040
+Ofena,66060,L'Aquila,AQ,Abruzzo,13,67025
+Opi,66061,L'Aquila,AQ,Abruzzo,13,67030
+Oricola,66062,L'Aquila,AQ,Abruzzo,13,67063
+Ortona dei Marsi,66063,L'Aquila,AQ,Abruzzo,13,67050
+Ortucchio,66064,L'Aquila,AQ,Abruzzo,13,67050
+Ovindoli,66065,L'Aquila,AQ,Abruzzo,13,67046
+Pacentro,66066,L'Aquila,AQ,Abruzzo,13,67030
+Pereto,66067,L'Aquila,AQ,Abruzzo,13,67064
+Pescasseroli,66068,L'Aquila,AQ,Abruzzo,13,67032
+Pescina,66069,L'Aquila,AQ,Abruzzo,13,67057
+Pescocostanzo,66070,L'Aquila,AQ,Abruzzo,13,67033
+Pettorano sul Gizio,66071,L'Aquila,AQ,Abruzzo,13,67034
+Pizzoli,66072,L'Aquila,AQ,Abruzzo,13,67017
+Poggio Picenze,66073,L'Aquila,AQ,Abruzzo,13,67026
+Prata d'Ansidonia,66074,L'Aquila,AQ,Abruzzo,13,67020
+Pratola Peligna,66075,L'Aquila,AQ,Abruzzo,13,67035
+Prezza,66076,L'Aquila,AQ,Abruzzo,13,67030
+Raiano,66077,L'Aquila,AQ,Abruzzo,13,67027
+Rivisondoli,66078,L'Aquila,AQ,Abruzzo,13,67036
+Roccacasale,66079,L'Aquila,AQ,Abruzzo,13,67030
+Rocca di Botte,66080,L'Aquila,AQ,Abruzzo,13,67066
+Rocca di Cambio,66081,L'Aquila,AQ,Abruzzo,13,67047
+Rocca di Mezzo,66082,L'Aquila,AQ,Abruzzo,13,67048
+Rocca Pia,66083,L'Aquila,AQ,Abruzzo,13,67030
+Roccaraso,66084,L'Aquila,AQ,Abruzzo,13,67037
+San Benedetto dei Marsi,66085,L'Aquila,AQ,Abruzzo,13,67058
+San Benedetto in Perillis,66086,L'Aquila,AQ,Abruzzo,13,67020
+San Demetrio ne' Vestini,66087,L'Aquila,AQ,Abruzzo,13,67028
+San Pio delle Camere,66088,L'Aquila,AQ,Abruzzo,13,67020
+Sante Marie,66089,L'Aquila,AQ,Abruzzo,13,67067
+Sant'Eusanio Forconese,66090,L'Aquila,AQ,Abruzzo,13,67020
+Santo Stefano di Sessanio,66091,L'Aquila,AQ,Abruzzo,13,67020
+San Vincenzo Valle Roveto,66092,L'Aquila,AQ,Abruzzo,13,67050
+Scanno,66093,L'Aquila,AQ,Abruzzo,13,67038
+Scontrone,66094,L'Aquila,AQ,Abruzzo,13,67030
+Scoppito,66095,L'Aquila,AQ,Abruzzo,13,67019
+Scurcola Marsicana,66096,L'Aquila,AQ,Abruzzo,13,67068
+Secinaro,66097,L'Aquila,AQ,Abruzzo,13,67029
+Sulmona,66098,L'Aquila,AQ,Abruzzo,13,67039
+Tagliacozzo,66099,L'Aquila,AQ,Abruzzo,13,67069
+Tione degli Abruzzi,66100,L'Aquila,AQ,Abruzzo,13,67020
+Tornimparte,66101,L'Aquila,AQ,Abruzzo,13,67049
+Trasacco,66102,L'Aquila,AQ,Abruzzo,13,67059
+Villalago,66103,L'Aquila,AQ,Abruzzo,13,67030
+Villa Santa Lucia degli Abruzzi,66104,L'Aquila,AQ,Abruzzo,13,67020
+Villa Sant'Angelo,66105,L'Aquila,AQ,Abruzzo,13,67020
+Villavallelonga,66106,L'Aquila,AQ,Abruzzo,13,67050
+Villetta Barrea,66107,L'Aquila,AQ,Abruzzo,13,67030
+Vittorito,66108,L'Aquila,AQ,Abruzzo,13,67030
+Alba Adriatica,67001,Teramo,TE,Abruzzo,13,64011
+Ancarano,67002,Teramo,TE,Abruzzo,13,64010
+Arsita,67003,Teramo,TE,Abruzzo,13,64031
+Atri,67004,Teramo,TE,Abruzzo,13,64032
+Basciano,67005,Teramo,TE,Abruzzo,13,64030
+Bellante,67006,Teramo,TE,Abruzzo,13,64020
+Bisenti,67007,Teramo,TE,Abruzzo,13,64033
+Campli,67008,Teramo,TE,Abruzzo,13,64012
+Canzano,67009,Teramo,TE,Abruzzo,13,64020
+Castel Castagna,67010,Teramo,TE,Abruzzo,13,64030
+Castellalto,67011,Teramo,TE,Abruzzo,13,64020
+Castelli,67012,Teramo,TE,Abruzzo,13,64041
+Castiglione Messer Raimondo,67013,Teramo,TE,Abruzzo,13,64034
+Castilenti,67014,Teramo,TE,Abruzzo,13,64035
+Cellino Attanasio,67015,Teramo,TE,Abruzzo,13,64036
+Cermignano,67016,Teramo,TE,Abruzzo,13,64037
+Civitella del Tronto,67017,Teramo,TE,Abruzzo,13,64010
+Colledara,67018,Teramo,TE,Abruzzo,13,64042
+Colonnella,67019,Teramo,TE,Abruzzo,13,64010
+Controguerra,67020,Teramo,TE,Abruzzo,13,64010
+Corropoli,67021,Teramo,TE,Abruzzo,13,64013
+Cortino,67022,Teramo,TE,Abruzzo,13,64040
+Crognaleto,67023,Teramo,TE,Abruzzo,13,64043
+Fano Adriano,67024,Teramo,TE,Abruzzo,13,64044
+Giulianova,67025,Teramo,TE,Abruzzo,13,64021
+Isola del Gran Sasso d'Italia,67026,Teramo,TE,Abruzzo,13,64045
+Montefino,67027,Teramo,TE,Abruzzo,13,64030
+Montorio al Vomano,67028,Teramo,TE,Abruzzo,13,64046
+Morro d'Oro,67029,Teramo,TE,Abruzzo,13,64020
+Mosciano Sant'Angelo,67030,Teramo,TE,Abruzzo,13,64023
+Nereto,67031,Teramo,TE,Abruzzo,13,64015
+Notaresco,67032,Teramo,TE,Abruzzo,13,64024
+Penna Sant'Andrea,67033,Teramo,TE,Abruzzo,13,64039
+Pietracamela,67034,Teramo,TE,Abruzzo,13,64047
+Pineto,67035,Teramo,TE,Abruzzo,13,64025
+Rocca Santa Maria,67036,Teramo,TE,Abruzzo,13,64010
+Roseto degli Abruzzi,67037,Teramo,TE,Abruzzo,13,64026
+Sant'Egidio alla Vibrata,67038,Teramo,TE,Abruzzo,13,64016
+Sant'Omero,67039,Teramo,TE,Abruzzo,13,64027
+Silvi,67040,Teramo,TE,Abruzzo,13,64028
+Teramo,67041,Teramo,TE,Abruzzo,13,64100
+Torano Nuovo,67042,Teramo,TE,Abruzzo,13,64010
+Torricella Sicura,67043,Teramo,TE,Abruzzo,13,64010
+Tortoreto,67044,Teramo,TE,Abruzzo,13,64018
+Tossicia,67045,Teramo,TE,Abruzzo,13,64049
+Valle Castellana,67046,Teramo,TE,Abruzzo,13,64010
+Martinsicuro,67047,Teramo,TE,Abruzzo,13,64014
+Abbateggio,68001,Pescara,PE,Abruzzo,13,65020
+Alanno,68002,Pescara,PE,Abruzzo,13,65020
+Bolognano,68003,Pescara,PE,Abruzzo,13,65020
+Brittoli,68004,Pescara,PE,Abruzzo,13,65010
+Bussi sul Tirino,68005,Pescara,PE,Abruzzo,13,65022
+Cappelle sul Tavo,68006,Pescara,PE,Abruzzo,13,65010
+Caramanico Terme,68007,Pescara,PE,Abruzzo,13,65023
+Carpineto della Nora,68008,Pescara,PE,Abruzzo,13,65010
+Castiglione a Casauria,68009,Pescara,PE,Abruzzo,13,65020
+Catignano,68010,Pescara,PE,Abruzzo,13,65011
+Cepagatti,68011,Pescara,PE,Abruzzo,13,65012
+Città Sant'Angelo,68012,Pescara,PE,Abruzzo,13,65013
+Civitaquana,68013,Pescara,PE,Abruzzo,13,65010
+Civitella Casanova,68014,Pescara,PE,Abruzzo,13,65010
+Collecorvino,68015,Pescara,PE,Abruzzo,13,65010
+Corvara,68016,Pescara,PE,Abruzzo,13,65020
+Cugnoli,68017,Pescara,PE,Abruzzo,13,65020
+Elice,68018,Pescara,PE,Abruzzo,13,65010
+Farindola,68019,Pescara,PE,Abruzzo,13,65010
+Lettomanoppello,68020,Pescara,PE,Abruzzo,13,65020
+Loreto Aprutino,68021,Pescara,PE,Abruzzo,13,65014
+Manoppello,68022,Pescara,PE,Abruzzo,13,65024
+Montebello di Bertona,68023,Pescara,PE,Abruzzo,13,65010
+Montesilvano,68024,Pescara,PE,Abruzzo,13,65015
+Moscufo,68025,Pescara,PE,Abruzzo,13,65010
+Nocciano,68026,Pescara,PE,Abruzzo,13,65010
+Penne,68027,Pescara,PE,Abruzzo,13,65017
+Pescara,68028,Pescara,PE,Abruzzo,13,65121
+Pescosansonesco,68029,Pescara,PE,Abruzzo,13,65020
+Pianella,68030,Pescara,PE,Abruzzo,13,65019
+Picciano,68031,Pescara,PE,Abruzzo,13,65010
+Pietranico,68032,Pescara,PE,Abruzzo,13,65020
+Popoli,68033,Pescara,PE,Abruzzo,13,65026
+Roccamorice,68034,Pescara,PE,Abruzzo,13,65020
+Rosciano,68035,Pescara,PE,Abruzzo,13,65020
+Salle,68036,Pescara,PE,Abruzzo,13,65020
+Sant'Eufemia a Maiella,68037,Pescara,PE,Abruzzo,13,65020
+San Valentino in Abruzzo Citeriore,68038,Pescara,PE,Abruzzo,13,65020
+Scafa,68039,Pescara,PE,Abruzzo,13,65027
+Serramonacesca,68040,Pescara,PE,Abruzzo,13,65025
+Spoltore,68041,Pescara,PE,Abruzzo,13,65010
+Tocco da Casauria,68042,Pescara,PE,Abruzzo,13,65028
+Torre de' Passeri,68043,Pescara,PE,Abruzzo,13,65029
+Turrivalignani,68044,Pescara,PE,Abruzzo,13,65020
+Vicoli,68045,Pescara,PE,Abruzzo,13,65010
+Villa Celiera,68046,Pescara,PE,Abruzzo,13,65010
+Altino,69001,Chieti,CH,Abruzzo,13,66040
+Archi,69002,Chieti,CH,Abruzzo,13,66044
+Ari,69003,Chieti,CH,Abruzzo,13,66010
+Arielli,69004,Chieti,CH,Abruzzo,13,66030
+Atessa,69005,Chieti,CH,Abruzzo,13,66041
+Bomba,69006,Chieti,CH,Abruzzo,13,66042
+Borrello,69007,Chieti,CH,Abruzzo,13,66040
+Bucchianico,69008,Chieti,CH,Abruzzo,13,66011
+Montebello sul Sangro,69009,Chieti,CH,Abruzzo,13,66040
+Canosa Sannita,69010,Chieti,CH,Abruzzo,13,66010
+Carpineto Sinello,69011,Chieti,CH,Abruzzo,13,66030
+Carunchio,69012,Chieti,CH,Abruzzo,13,66050
+Casacanditella,69013,Chieti,CH,Abruzzo,13,66010
+Casalanguida,69014,Chieti,CH,Abruzzo,13,66031
+Casalbordino,69015,Chieti,CH,Abruzzo,13,66021
+Casalincontrada,69016,Chieti,CH,Abruzzo,13,66012
+Casoli,69017,Chieti,CH,Abruzzo,13,66043
+Castel Frentano,69018,Chieti,CH,Abruzzo,13,66032
+Castelguidone,69019,Chieti,CH,Abruzzo,13,66040
+Castiglione Messer Marino,69020,Chieti,CH,Abruzzo,13,66033
+Celenza sul Trigno,69021,Chieti,CH,Abruzzo,13,66050
+Chieti,69022,Chieti,CH,Abruzzo,13,66100
+Civitaluparella,69023,Chieti,CH,Abruzzo,13,66040
+Civitella Messer Raimondo,69024,Chieti,CH,Abruzzo,13,66010
+Colledimacine,69025,Chieti,CH,Abruzzo,13,66010
+Colledimezzo,69026,Chieti,CH,Abruzzo,13,66040
+Crecchio,69027,Chieti,CH,Abruzzo,13,66014
+Cupello,69028,Chieti,CH,Abruzzo,13,66051
+Dogliola,69029,Chieti,CH,Abruzzo,13,66050
+Fara Filiorum Petri,69030,Chieti,CH,Abruzzo,13,66010
+Fara San Martino,69031,Chieti,CH,Abruzzo,13,66015
+Filetto,69032,Chieti,CH,Abruzzo,13,66030
+Fossacesia,69033,Chieti,CH,Abruzzo,13,66022
+Fraine,69034,Chieti,CH,Abruzzo,13,66050
+Francavilla al Mare,69035,Chieti,CH,Abruzzo,13,66023
+Fresagrandinaria,69036,Chieti,CH,Abruzzo,13,66050
+Frisa,69037,Chieti,CH,Abruzzo,13,66030
+Furci,69038,Chieti,CH,Abruzzo,13,66050
+Gamberale,69039,Chieti,CH,Abruzzo,13,66040
+Gessopalena,69040,Chieti,CH,Abruzzo,13,66010
+Gissi,69041,Chieti,CH,Abruzzo,13,66052
+Giuliano Teatino,69042,Chieti,CH,Abruzzo,13,66010
+Guardiagrele,69043,Chieti,CH,Abruzzo,13,66016
+Guilmi,69044,Chieti,CH,Abruzzo,13,66050
+Lama dei Peligni,69045,Chieti,CH,Abruzzo,13,66010
+Lanciano,69046,Chieti,CH,Abruzzo,13,66034
+Lentella,69047,Chieti,CH,Abruzzo,13,66050
+Lettopalena,69048,Chieti,CH,Abruzzo,13,66010
+Liscia,69049,Chieti,CH,Abruzzo,13,66050
+Miglianico,69050,Chieti,CH,Abruzzo,13,66010
+Montazzoli,69051,Chieti,CH,Abruzzo,13,66030
+Monteferrante,69052,Chieti,CH,Abruzzo,13,66040
+Montelapiano,69053,Chieti,CH,Abruzzo,13,66040
+Montenerodomo,69054,Chieti,CH,Abruzzo,13,66010
+Monteodorisio,69055,Chieti,CH,Abruzzo,13,66050
+Mozzagrogna,69056,Chieti,CH,Abruzzo,13,66030
+Orsogna,69057,Chieti,CH,Abruzzo,13,66036
+Ortona,69058,Chieti,CH,Abruzzo,13,66026
+Paglieta,69059,Chieti,CH,Abruzzo,13,66020
+Palena,69060,Chieti,CH,Abruzzo,13,66017
+Palmoli,69061,Chieti,CH,Abruzzo,13,66050
+Palombaro,69062,Chieti,CH,Abruzzo,13,66010
+Pennadomo,69063,Chieti,CH,Abruzzo,13,66040
+Pennapiedimonte,69064,Chieti,CH,Abruzzo,13,66010
+Perano,69065,Chieti,CH,Abruzzo,13,66040
+Pizzoferrato,69066,Chieti,CH,Abruzzo,13,66040
+Poggiofiorito,69067,Chieti,CH,Abruzzo,13,66030
+Pollutri,69068,Chieti,CH,Abruzzo,13,66020
+Pretoro,69069,Chieti,CH,Abruzzo,13,66010
+Quadri,69070,Chieti,CH,Abruzzo,13,66040
+Rapino,69071,Chieti,CH,Abruzzo,13,66010
+Ripa Teatina,69072,Chieti,CH,Abruzzo,13,66010
+Roccamontepiano,69073,Chieti,CH,Abruzzo,13,66010
+Rocca San Giovanni,69074,Chieti,CH,Abruzzo,13,66020
+Roccascalegna,69075,Chieti,CH,Abruzzo,13,66040
+Roccaspinalveti,69076,Chieti,CH,Abruzzo,13,66050
+Roio del Sangro,69077,Chieti,CH,Abruzzo,13,66040
+Rosello,69078,Chieti,CH,Abruzzo,13,66040
+San Buono,69079,Chieti,CH,Abruzzo,13,66050
+San Giovanni Lipioni,69080,Chieti,CH,Abruzzo,13,66050
+San Giovanni Teatino,69081,Chieti,CH,Abruzzo,13,66020
+San Martino sulla Marrucina,69082,Chieti,CH,Abruzzo,13,66010
+San Salvo,69083,Chieti,CH,Abruzzo,13,66050
+Santa Maria Imbaro,69084,Chieti,CH,Abruzzo,13,66030
+Sant'Eusanio del Sangro,69085,Chieti,CH,Abruzzo,13,66037
+San Vito Chietino,69086,Chieti,CH,Abruzzo,13,66038
+Scerni,69087,Chieti,CH,Abruzzo,13,66020
+Schiavi di Abruzzo,69088,Chieti,CH,Abruzzo,13,66045
+Taranta Peligna,69089,Chieti,CH,Abruzzo,13,66018
+Tollo,69090,Chieti,CH,Abruzzo,13,66010
+Torino di Sangro,69091,Chieti,CH,Abruzzo,13,66020
+Tornareccio,69092,Chieti,CH,Abruzzo,13,66046
+Torrebruna,69093,Chieti,CH,Abruzzo,13,66050
+Torrevecchia Teatina,69094,Chieti,CH,Abruzzo,13,66010
+Torricella Peligna,69095,Chieti,CH,Abruzzo,13,66019
+Treglio,69096,Chieti,CH,Abruzzo,13,66030
+Tufillo,69097,Chieti,CH,Abruzzo,13,66050
+Vacri,69098,Chieti,CH,Abruzzo,13,66010
+Vasto,69099,Chieti,CH,Abruzzo,13,66054
+Villalfonsina,69100,Chieti,CH,Abruzzo,13,66020
+Villamagna,69101,Chieti,CH,Abruzzo,13,66010
+Villa Santa Maria,69102,Chieti,CH,Abruzzo,13,66047
+Pietraferrazzana,69103,Chieti,CH,Abruzzo,13,66040
+Fallo,69104,Chieti,CH,Abruzzo,13,66040
+Acquaviva Collecroce,70001,Campobasso,CB,Molise,14,86030
+Baranello,70002,Campobasso,CB,Molise,14,86011
+Bojano,70003,Campobasso,CB,Molise,14,86021
+Bonefro,70004,Campobasso,CB,Molise,14,86041
+Busso,70005,Campobasso,CB,Molise,14,86010
+Campobasso,70006,Campobasso,CB,Molise,14,86100
+Campochiaro,70007,Campobasso,CB,Molise,14,86020
+Campodipietra,70008,Campobasso,CB,Molise,14,86010
+Campolieto,70009,Campobasso,CB,Molise,14,86040
+Campomarino,70010,Campobasso,CB,Molise,14,86042
+Casacalenda,70011,Campobasso,CB,Molise,14,86043
+Casalciprano,70012,Campobasso,CB,Molise,14,86010
+Castelbottaccio,70013,Campobasso,CB,Molise,14,86030
+Castellino del Biferno,70014,Campobasso,CB,Molise,14,86020
+Castelmauro,70015,Campobasso,CB,Molise,14,86031
+Castropignano,70016,Campobasso,CB,Molise,14,86010
+Cercemaggiore,70017,Campobasso,CB,Molise,14,86012
+Cercepiccola,70018,Campobasso,CB,Molise,14,86010
+Civitacampomarano,70019,Campobasso,CB,Molise,14,86030
+Colle d'Anchise,70020,Campobasso,CB,Molise,14,86020
+Colletorto,70021,Campobasso,CB,Molise,14,86044
+Duronia,70022,Campobasso,CB,Molise,14,86020
+Ferrazzano,70023,Campobasso,CB,Molise,14,86010
+Fossalto,70024,Campobasso,CB,Molise,14,86020
+Gambatesa,70025,Campobasso,CB,Molise,14,86013
+Gildone,70026,Campobasso,CB,Molise,14,86010
+Guardialfiera,70027,Campobasso,CB,Molise,14,86030
+Guardiaregia,70028,Campobasso,CB,Molise,14,86014
+Guglionesi,70029,Campobasso,CB,Molise,14,86034
+Jelsi,70030,Campobasso,CB,Molise,14,86015
+Larino,70031,Campobasso,CB,Molise,14,86035
+Limosano,70032,Campobasso,CB,Molise,14,86022
+Lucito,70033,Campobasso,CB,Molise,14,86030
+Lupara,70034,Campobasso,CB,Molise,14,86030
+Macchia Valfortore,70035,Campobasso,CB,Molise,14,86040
+Mafalda,70036,Campobasso,CB,Molise,14,86030
+Matrice,70037,Campobasso,CB,Molise,14,86030
+Mirabello Sannitico,70038,Campobasso,CB,Molise,14,86010
+Molise,70039,Campobasso,CB,Molise,14,86020
+Monacilioni,70040,Campobasso,CB,Molise,14,86040
+Montagano,70041,Campobasso,CB,Molise,14,86023
+Montecilfone,70042,Campobasso,CB,Molise,14,86032
+Montefalcone nel Sannio,70043,Campobasso,CB,Molise,14,86033
+Montelongo,70044,Campobasso,CB,Molise,14,86040
+Montemitro,70045,Campobasso,CB,Molise,14,86030
+Montenero di Bisaccia,70046,Campobasso,CB,Molise,14,86036
+Montorio nei Frentani,70047,Campobasso,CB,Molise,14,86040
+Morrone del Sannio,70048,Campobasso,CB,Molise,14,86040
+Oratino,70049,Campobasso,CB,Molise,14,86010
+Palata,70050,Campobasso,CB,Molise,14,86037
+Petacciato,70051,Campobasso,CB,Molise,14,86038
+Petrella Tifernina,70052,Campobasso,CB,Molise,14,86024
+Pietracatella,70053,Campobasso,CB,Molise,14,86040
+Pietracupa,70054,Campobasso,CB,Molise,14,86020
+Portocannone,70055,Campobasso,CB,Molise,14,86045
+Provvidenti,70056,Campobasso,CB,Molise,14,86040
+Riccia,70057,Campobasso,CB,Molise,14,86016
+Ripabottoni,70058,Campobasso,CB,Molise,14,86040
+Ripalimosani,70059,Campobasso,CB,Molise,14,86025
+Roccavivara,70060,Campobasso,CB,Molise,14,86020
+Rotello,70061,Campobasso,CB,Molise,14,86040
+Salcito,70062,Campobasso,CB,Molise,14,86026
+San Biase,70063,Campobasso,CB,Molise,14,86020
+San Felice del Molise,70064,Campobasso,CB,Molise,14,86030
+San Giacomo degli Schiavoni,70065,Campobasso,CB,Molise,14,86030
+San Giovanni in Galdo,70066,Campobasso,CB,Molise,14,86010
+San Giuliano del Sannio,70067,Campobasso,CB,Molise,14,86010
+San Giuliano di Puglia,70068,Campobasso,CB,Molise,14,86040
+San Martino in Pensilis,70069,Campobasso,CB,Molise,14,86046
+San Massimo,70070,Campobasso,CB,Molise,14,86027
+San Polo Matese,70071,Campobasso,CB,Molise,14,86020
+Santa Croce di Magliano,70072,Campobasso,CB,Molise,14,86047
+Sant'Angelo Limosano,70073,Campobasso,CB,Molise,14,86020
+Sant'Elia a Pianisi,70074,Campobasso,CB,Molise,14,86048
+Sepino,70075,Campobasso,CB,Molise,14,86017
+Spinete,70076,Campobasso,CB,Molise,14,86020
+Tavenna,70077,Campobasso,CB,Molise,14,86030
+Termoli,70078,Campobasso,CB,Molise,14,86039
+Torella del Sannio,70079,Campobasso,CB,Molise,14,86028
+Toro,70080,Campobasso,CB,Molise,14,86018
+Trivento,70081,Campobasso,CB,Molise,14,86029
+Tufara,70082,Campobasso,CB,Molise,14,86010
+Ururi,70083,Campobasso,CB,Molise,14,86049
+Vinchiaturo,70084,Campobasso,CB,Molise,14,86019
+Acquaviva d'Isernia,94001,Isernia,IS,Molise,14,86080
+Agnone,94002,Isernia,IS,Molise,14,86081
+Bagnoli del Trigno,94003,Isernia,IS,Molise,14,86091
+Belmonte del Sannio,94004,Isernia,IS,Molise,14,86080
+Cantalupo nel Sannio,94005,Isernia,IS,Molise,14,86092
+Capracotta,94006,Isernia,IS,Molise,14,86082
+Carovilli,94007,Isernia,IS,Molise,14,86083
+Carpinone,94008,Isernia,IS,Molise,14,86093
+Castel del Giudice,94009,Isernia,IS,Molise,14,86080
+Castelpetroso,94010,Isernia,IS,Molise,14,86090
+Castelpizzuto,94011,Isernia,IS,Molise,14,86090
+Castel San Vincenzo,94012,Isernia,IS,Molise,14,86071
+Castelverrino,94013,Isernia,IS,Molise,14,86080
+Cerro al Volturno,94014,Isernia,IS,Molise,14,86072
+Chiauci,94015,Isernia,IS,Molise,14,86097
+Civitanova del Sannio,94016,Isernia,IS,Molise,14,86094
+Colli a Volturno,94017,Isernia,IS,Molise,14,86073
+Conca Casale,94018,Isernia,IS,Molise,14,86070
+Filignano,94019,Isernia,IS,Molise,14,86074
+Forlì del Sannio,94020,Isernia,IS,Molise,14,86084
+Fornelli,94021,Isernia,IS,Molise,14,86070
+Frosolone,94022,Isernia,IS,Molise,14,86095
+Isernia,94023,Isernia,IS,Molise,14,86170
+Longano,94024,Isernia,IS,Molise,14,86090
+Macchia d'Isernia,94025,Isernia,IS,Molise,14,86070
+Macchiagodena,94026,Isernia,IS,Molise,14,86096
+Miranda,94027,Isernia,IS,Molise,14,86080
+Montaquila,94028,Isernia,IS,Molise,14,86070
+Montenero Val Cocchiara,94029,Isernia,IS,Molise,14,86080
+Monteroduni,94030,Isernia,IS,Molise,14,86075
+Pesche,94031,Isernia,IS,Molise,14,86090
+Pescolanciano,94032,Isernia,IS,Molise,14,86097
+Pescopennataro,94033,Isernia,IS,Molise,14,86080
+Pettoranello del Molise,94034,Isernia,IS,Molise,14,86090
+Pietrabbondante,94035,Isernia,IS,Molise,14,86085
+Pizzone,94036,Isernia,IS,Molise,14,86071
+Poggio Sannita,94037,Isernia,IS,Molise,14,86086
+Pozzilli,94038,Isernia,IS,Molise,14,86077
+Rionero Sannitico,94039,Isernia,IS,Molise,14,86087
+Roccamandolfi,94040,Isernia,IS,Molise,14,86092
+Roccasicura,94041,Isernia,IS,Molise,14,86080
+Rocchetta a Volturno,94042,Isernia,IS,Molise,14,86070
+San Pietro Avellana,94043,Isernia,IS,Molise,14,86088
+Sant'Agapito,94044,Isernia,IS,Molise,14,86070
+Santa Maria del Molise,94045,Isernia,IS,Molise,14,86096
+Sant'Angelo del Pesco,94046,Isernia,IS,Molise,14,86080
+Sant'Elena Sannita,94047,Isernia,IS,Molise,14,86095
+Scapoli,94048,Isernia,IS,Molise,14,86070
+Sessano del Molise,94049,Isernia,IS,Molise,14,86097
+Sesto Campano,94050,Isernia,IS,Molise,14,86078
+Vastogirardi,94051,Isernia,IS,Molise,14,86089
+Venafro,94052,Isernia,IS,Molise,14,86079
+Ailano,61001,Caserta,CE,Campania,15,81010
+Alife,61002,Caserta,CE,Campania,15,81011
+Alvignano,61003,Caserta,CE,Campania,15,81012
+Arienzo,61004,Caserta,CE,Campania,15,81021
+Aversa,61005,Caserta,CE,Campania,15,81031
+Baia e Latina,61006,Caserta,CE,Campania,15,81010
+Bellona,61007,Caserta,CE,Campania,15,81041
+Caianello,61008,Caserta,CE,Campania,15,81059
+Caiazzo,61009,Caserta,CE,Campania,15,81013
+Calvi Risorta,61010,Caserta,CE,Campania,15,81042
+Camigliano,61011,Caserta,CE,Campania,15,81050
+Cancello ed Arnone,61012,Caserta,CE,Campania,15,81030
+Capodrise,61013,Caserta,CE,Campania,15,81020
+Capriati a Volturno,61014,Caserta,CE,Campania,15,81014
+Capua,61015,Caserta,CE,Campania,15,81043
+Carinaro,61016,Caserta,CE,Campania,15,81032
+Carinola,61017,Caserta,CE,Campania,15,81030
+Casagiove,61018,Caserta,CE,Campania,15,81022
+Casal di Principe,61019,Caserta,CE,Campania,15,81033
+Casaluce,61020,Caserta,CE,Campania,15,81030
+Casapulla,61021,Caserta,CE,Campania,15,81020
+Caserta,61022,Caserta,CE,Campania,15,81100
+Castel Campagnano,61023,Caserta,CE,Campania,15,81010
+Castel di Sasso,61024,Caserta,CE,Campania,15,81040
+Castello del Matese,61025,Caserta,CE,Campania,15,81016
+Castel Morrone,61026,Caserta,CE,Campania,15,81020
+Castel Volturno,61027,Caserta,CE,Campania,15,81030
+Cervino,61028,Caserta,CE,Campania,15,81023
+Cesa,61029,Caserta,CE,Campania,15,81030
+Ciorlano,61030,Caserta,CE,Campania,15,81010
+Conca della Campania,61031,Caserta,CE,Campania,15,81044
+Curti,61032,Caserta,CE,Campania,15,81040
+Dragoni,61033,Caserta,CE,Campania,15,81010
+Fontegreca,61034,Caserta,CE,Campania,15,81014
+Formicola,61035,Caserta,CE,Campania,15,81040
+Francolise,61036,Caserta,CE,Campania,15,81050
+Frignano,61037,Caserta,CE,Campania,15,81030
+Gallo Matese,61038,Caserta,CE,Campania,15,81010
+Galluccio,61039,Caserta,CE,Campania,15,81044
+Giano Vetusto,61040,Caserta,CE,Campania,15,81042
+Gioia Sannitica,61041,Caserta,CE,Campania,15,81010
+Grazzanise,61042,Caserta,CE,Campania,15,81046
+Gricignano di Aversa,61043,Caserta,CE,Campania,15,81030
+Letino,61044,Caserta,CE,Campania,15,81010
+Liberi,61045,Caserta,CE,Campania,15,81040
+Lusciano,61046,Caserta,CE,Campania,15,81030
+Macerata Campania,61047,Caserta,CE,Campania,15,81047
+Maddaloni,61048,Caserta,CE,Campania,15,81024
+Marcianise,61049,Caserta,CE,Campania,15,81025
+Marzano Appio,61050,Caserta,CE,Campania,15,81035
+Mignano Monte Lungo,61051,Caserta,CE,Campania,15,81049
+Mondragone,61052,Caserta,CE,Campania,15,81034
+Orta di Atella,61053,Caserta,CE,Campania,15,81030
+Parete,61054,Caserta,CE,Campania,15,81030
+Pastorano,61055,Caserta,CE,Campania,15,81050
+Piana di Monte Verna,61056,Caserta,CE,Campania,15,81013
+Piedimonte Matese,61057,Caserta,CE,Campania,15,81016
+Pietramelara,61058,Caserta,CE,Campania,15,81051
+Pietravairano,61059,Caserta,CE,Campania,15,81040
+Pignataro Maggiore,61060,Caserta,CE,Campania,15,81052
+Pontelatone,61061,Caserta,CE,Campania,15,81040
+Portico di Caserta,61062,Caserta,CE,Campania,15,81050
+Prata Sannita,61063,Caserta,CE,Campania,15,81010
+Pratella,61064,Caserta,CE,Campania,15,81010
+Presenzano,61065,Caserta,CE,Campania,15,81050
+Raviscanina,61066,Caserta,CE,Campania,15,81017
+Recale,61067,Caserta,CE,Campania,15,81020
+Riardo,61068,Caserta,CE,Campania,15,81053
+Rocca d'Evandro,61069,Caserta,CE,Campania,15,81040
+Roccamonfina,61070,Caserta,CE,Campania,15,81035
+Roccaromana,61071,Caserta,CE,Campania,15,81051
+Rocchetta e Croce,61072,Caserta,CE,Campania,15,81042
+Ruviano,61073,Caserta,CE,Campania,15,81010
+San Cipriano d'Aversa,61074,Caserta,CE,Campania,15,81036
+San Felice a Cancello,61075,Caserta,CE,Campania,15,81027
+San Gregorio Matese,61076,Caserta,CE,Campania,15,81010
+San Marcellino,61077,Caserta,CE,Campania,15,81030
+San Nicola la Strada,61078,Caserta,CE,Campania,15,81020
+San Pietro Infine,61079,Caserta,CE,Campania,15,81049
+San Potito Sannitico,61080,Caserta,CE,Campania,15,81016
+San Prisco,61081,Caserta,CE,Campania,15,81054
+Santa Maria a Vico,61082,Caserta,CE,Campania,15,81028
+Santa Maria Capua Vetere,61083,Caserta,CE,Campania,15,81055
+Santa Maria la Fossa,61084,Caserta,CE,Campania,15,81050
+San Tammaro,61085,Caserta,CE,Campania,15,81050
+Sant'Angelo d'Alife,61086,Caserta,CE,Campania,15,81017
+Sant'Arpino,61087,Caserta,CE,Campania,15,81030
+Sessa Aurunca,61088,Caserta,CE,Campania,15,81037
+Sparanise,61089,Caserta,CE,Campania,15,81056
+Succivo,61090,Caserta,CE,Campania,15,81030
+Teano,61091,Caserta,CE,Campania,15,81057
+Teverola,61092,Caserta,CE,Campania,15,81030
+Tora e Piccilli,61093,Caserta,CE,Campania,15,81044
+Trentola Ducenta,61094,Caserta,CE,Campania,15,81038
+Vairano Patenora,61095,Caserta,CE,Campania,15,81058
+Valle Agricola,61096,Caserta,CE,Campania,15,81010
+Valle di Maddaloni,61097,Caserta,CE,Campania,15,81020
+Villa di Briano,61098,Caserta,CE,Campania,15,81030
+Villa Literno,61099,Caserta,CE,Campania,15,81039
+Vitulazio,61100,Caserta,CE,Campania,15,81041
+Falciano del Massico,61101,Caserta,CE,Campania,15,81030
+Cellole,61102,Caserta,CE,Campania,15,81030
+Casapesenna,61103,Caserta,CE,Campania,15,81030
+San Marco Evangelista,61104,Caserta,CE,Campania,15,81020
+Airola,62001,Benevento,BN,Campania,15,82011
+Amorosi,62002,Benevento,BN,Campania,15,82031
+Apice,62003,Benevento,BN,Campania,15,82021
+Apollosa,62004,Benevento,BN,Campania,15,82030
+Arpaia,62005,Benevento,BN,Campania,15,82011
+Arpaise,62006,Benevento,BN,Campania,15,82010
+Baselice,62007,Benevento,BN,Campania,15,82020
+Benevento,62008,Benevento,BN,Campania,15,82100
+Bonea,62009,Benevento,BN,Campania,15,82013
+Bucciano,62010,Benevento,BN,Campania,15,82010
+Buonalbergo,62011,Benevento,BN,Campania,15,82020
+Calvi,62012,Benevento,BN,Campania,15,82018
+Campolattaro,62013,Benevento,BN,Campania,15,82020
+Campoli del Monte Taburno,62014,Benevento,BN,Campania,15,82030
+Casalduni,62015,Benevento,BN,Campania,15,82027
+Castelfranco in Miscano,62016,Benevento,BN,Campania,15,82022
+Castelpagano,62017,Benevento,BN,Campania,15,82024
+Castelpoto,62018,Benevento,BN,Campania,15,82030
+Castelvenere,62019,Benevento,BN,Campania,15,82037
+Castelvetere in Val Fortore,62020,Benevento,BN,Campania,15,82023
+Cautano,62021,Benevento,BN,Campania,15,82030
+Ceppaloni,62022,Benevento,BN,Campania,15,82010
+Cerreto Sannita,62023,Benevento,BN,Campania,15,82032
+Circello,62024,Benevento,BN,Campania,15,82020
+Colle Sannita,62025,Benevento,BN,Campania,15,82024
+Cusano Mutri,62026,Benevento,BN,Campania,15,82033
+Dugenta,62027,Benevento,BN,Campania,15,82030
+Durazzano,62028,Benevento,BN,Campania,15,82015
+Faicchio,62029,Benevento,BN,Campania,15,82030
+Foglianise,62030,Benevento,BN,Campania,15,82030
+Foiano di Val Fortore,62031,Benevento,BN,Campania,15,82020
+Forchia,62032,Benevento,BN,Campania,15,82011
+Fragneto l'Abate,62033,Benevento,BN,Campania,15,82020
+Fragneto Monforte,62034,Benevento,BN,Campania,15,82020
+Frasso Telesino,62035,Benevento,BN,Campania,15,82030
+Ginestra degli Schiavoni,62036,Benevento,BN,Campania,15,82020
+Guardia Sanframondi,62037,Benevento,BN,Campania,15,82034
+Limatola,62038,Benevento,BN,Campania,15,82030
+Melizzano,62039,Benevento,BN,Campania,15,82030
+Moiano,62040,Benevento,BN,Campania,15,82010
+Molinara,62041,Benevento,BN,Campania,15,82020
+Montefalcone di Val Fortore,62042,Benevento,BN,Campania,15,82025
+Montesarchio,62043,Benevento,BN,Campania,15,82016
+Morcone,62044,Benevento,BN,Campania,15,82026
+Paduli,62045,Benevento,BN,Campania,15,82020
+Pago Veiano,62046,Benevento,BN,Campania,15,82020
+Pannarano,62047,Benevento,BN,Campania,15,82017
+Paolisi,62048,Benevento,BN,Campania,15,82011
+Paupisi,62049,Benevento,BN,Campania,15,82030
+Pesco Sannita,62050,Benevento,BN,Campania,15,82020
+Pietraroja,62051,Benevento,BN,Campania,15,82030
+Pietrelcina,62052,Benevento,BN,Campania,15,82020
+Ponte,62053,Benevento,BN,Campania,15,82030
+Pontelandolfo,62054,Benevento,BN,Campania,15,82027
+Puglianello,62055,Benevento,BN,Campania,15,82030
+Reino,62056,Benevento,BN,Campania,15,82020
+San Bartolomeo in Galdo,62057,Benevento,BN,Campania,15,82028
+San Giorgio del Sannio,62058,Benevento,BN,Campania,15,82018
+San Giorgio La Molara,62059,Benevento,BN,Campania,15,82020
+San Leucio del Sannio,62060,Benevento,BN,Campania,15,82010
+San Lorenzello,62061,Benevento,BN,Campania,15,82030
+San Lorenzo Maggiore,62062,Benevento,BN,Campania,15,82034
+San Lupo,62063,Benevento,BN,Campania,15,82034
+San Marco dei Cavoti,62064,Benevento,BN,Campania,15,82029
+San Martino Sannita,62065,Benevento,BN,Campania,15,82010
+San Nazzaro,62066,Benevento,BN,Campania,15,82018
+San Nicola Manfredi,62067,Benevento,BN,Campania,15,82010
+San Salvatore Telesino,62068,Benevento,BN,Campania,15,82030
+Santa Croce del Sannio,62069,Benevento,BN,Campania,15,82020
+Sant'Agata de' Goti,62070,Benevento,BN,Campania,15,82019
+Sant'Angelo a Cupolo,62071,Benevento,BN,Campania,15,82010
+Sassinoro,62072,Benevento,BN,Campania,15,82026
+Solopaca,62073,Benevento,BN,Campania,15,82036
+Telese Terme,62074,Benevento,BN,Campania,15,82037
+Tocco Caudio,62075,Benevento,BN,Campania,15,82030
+Torrecuso,62076,Benevento,BN,Campania,15,82030
+Vitulano,62077,Benevento,BN,Campania,15,82038
+Sant'Arcangelo Trimonte,62078,Benevento,BN,Campania,15,82021
+Acerra,63001,Napoli,NA,Campania,15,80011
+Afragola,63002,Napoli,NA,Campania,15,80021
+Agerola,63003,Napoli,NA,Campania,15,80051
+Anacapri,63004,Napoli,NA,Campania,15,80071
+Arzano,63005,Napoli,NA,Campania,15,80022
+Bacoli,63006,Napoli,NA,Campania,15,80070
+Barano d'Ischia,63007,Napoli,NA,Campania,15,80070
+Boscoreale,63008,Napoli,NA,Campania,15,80041
+Boscotrecase,63009,Napoli,NA,Campania,15,80042
+Brusciano,63010,Napoli,NA,Campania,15,80031
+Caivano,63011,Napoli,NA,Campania,15,80023
+Calvizzano,63012,Napoli,NA,Campania,15,80012
+Camposano,63013,Napoli,NA,Campania,15,80030
+Capri,63014,Napoli,NA,Campania,15,80073
+Carbonara di Nola,63015,Napoli,NA,Campania,15,80030
+Cardito,63016,Napoli,NA,Campania,15,80024
+Casalnuovo di Napoli,63017,Napoli,NA,Campania,15,80013
+Casamarciano,63018,Napoli,NA,Campania,15,80032
+Casamicciola Terme,63019,Napoli,NA,Campania,15,80074
+Casandrino,63020,Napoli,NA,Campania,15,80025
+Casavatore,63021,Napoli,NA,Campania,15,80020
+Casola di Napoli,63022,Napoli,NA,Campania,15,80050
+Casoria,63023,Napoli,NA,Campania,15,80026
+Castellammare di Stabia,63024,Napoli,NA,Campania,15,80053
+Castello di Cisterna,63025,Napoli,NA,Campania,15,80030
+Cercola,63026,Napoli,NA,Campania,15,80040
+Cicciano,63027,Napoli,NA,Campania,15,80033
+Cimitile,63028,Napoli,NA,Campania,15,80030
+Comiziano,63029,Napoli,NA,Campania,15,80030
+Crispano,63030,Napoli,NA,Campania,15,80020
+Forio,63031,Napoli,NA,Campania,15,80075
+Frattamaggiore,63032,Napoli,NA,Campania,15,80027
+Frattaminore,63033,Napoli,NA,Campania,15,80020
+Giugliano in Campania,63034,Napoli,NA,Campania,15,80014
+Gragnano,63035,Napoli,NA,Campania,15,80054
+Grumo Nevano,63036,Napoli,NA,Campania,15,80028
+Ischia,63037,Napoli,NA,Campania,15,80077
+Lacco Ameno,63038,Napoli,NA,Campania,15,80076
+Lettere,63039,Napoli,NA,Campania,15,80050
+Liveri,63040,Napoli,NA,Campania,15,80030
+Marano di Napoli,63041,Napoli,NA,Campania,15,80016
+Mariglianella,63042,Napoli,NA,Campania,15,80030
+Marigliano,63043,Napoli,NA,Campania,15,80034
+Massa Lubrense,63044,Napoli,NA,Campania,15,80061
+Melito di Napoli,63045,Napoli,NA,Campania,15,80017
+Meta,63046,Napoli,NA,Campania,15,80062
+Monte di Procida,63047,Napoli,NA,Campania,15,80070
+Mugnano di Napoli,63048,Napoli,NA,Campania,15,80018
+Napoli,63049,Napoli,NA,Campania,15,80123
+Nola,63050,Napoli,NA,Campania,15,80035
+Ottaviano,63051,Napoli,NA,Campania,15,80044
+Palma Campania,63052,Napoli,NA,Campania,15,80036
+Piano di Sorrento,63053,Napoli,NA,Campania,15,80063
+Pimonte,63054,Napoli,NA,Campania,15,80050
+Poggiomarino,63055,Napoli,NA,Campania,15,80040
+Pollena Trocchia,63056,Napoli,NA,Campania,15,80040
+Pomigliano d'Arco,63057,Napoli,NA,Campania,15,80038
+Pompei,63058,Napoli,NA,Campania,15,80045
+Portici,63059,Napoli,NA,Campania,15,80055
+Pozzuoli,63060,Napoli,NA,Campania,15,80078
+Procida,63061,Napoli,NA,Campania,15,80079
+Qualiano,63062,Napoli,NA,Campania,15,80019
+Quarto,63063,Napoli,NA,Campania,15,80010
+Ercolano,63064,Napoli,NA,Campania,15,80056
+Roccarainola,63065,Napoli,NA,Campania,15,80030
+San Gennaro Vesuviano,63066,Napoli,NA,Campania,15,80040
+San Giorgio a Cremano,63067,Napoli,NA,Campania,15,80046
+San Giuseppe Vesuviano,63068,Napoli,NA,Campania,15,80047
+San Paolo Bel Sito,63069,Napoli,NA,Campania,15,80030
+San Sebastiano al Vesuvio,63070,Napoli,NA,Campania,15,80040
+Sant'Agnello,63071,Napoli,NA,Campania,15,80065
+Sant'Anastasia,63072,Napoli,NA,Campania,15,80048
+Sant'Antimo,63073,Napoli,NA,Campania,15,80029
+Sant'Antonio Abate,63074,Napoli,NA,Campania,15,80057
+San Vitaliano,63075,Napoli,NA,Campania,15,80030
+Saviano,63076,Napoli,NA,Campania,15,80039
+Scisciano,63077,Napoli,NA,Campania,15,80030
+Serrara Fontana,63078,Napoli,NA,Campania,15,80070
+Somma Vesuviana,63079,Napoli,NA,Campania,15,80049
+Sorrento,63080,Napoli,NA,Campania,15,80067
+Striano,63081,Napoli,NA,Campania,15,80040
+Terzigno,63082,Napoli,NA,Campania,15,80040
+Torre Annunziata,63083,Napoli,NA,Campania,15,80058
+Torre del Greco,63084,Napoli,NA,Campania,15,80059
+Tufino,63085,Napoli,NA,Campania,15,80030
+Vico Equense,63086,Napoli,NA,Campania,15,80069
+Villaricca,63087,Napoli,NA,Campania,15,80010
+Visciano,63088,Napoli,NA,Campania,15,80030
+Volla,63089,Napoli,NA,Campania,15,80040
+Santa Maria la Carità,63090,Napoli,NA,Campania,15,80050
+Trecase,63091,Napoli,NA,Campania,15,80040
+Massa di Somma,63092,Napoli,NA,Campania,15,80040
+Aiello del Sabato,64001,Avellino,AV,Campania,15,83020
+Altavilla Irpina,64002,Avellino,AV,Campania,15,83011
+Andretta,64003,Avellino,AV,Campania,15,83040
+Aquilonia,64004,Avellino,AV,Campania,15,83041
+Ariano Irpino,64005,Avellino,AV,Campania,15,83031
+Atripalda,64006,Avellino,AV,Campania,15,83042
+Avella,64007,Avellino,AV,Campania,15,83021
+Avellino,64008,Avellino,AV,Campania,15,83100
+Bagnoli Irpino,64009,Avellino,AV,Campania,15,83043
+Baiano,64010,Avellino,AV,Campania,15,83022
+Bisaccia,64011,Avellino,AV,Campania,15,83044
+Bonito,64012,Avellino,AV,Campania,15,83032
+Cairano,64013,Avellino,AV,Campania,15,83040
+Calabritto,64014,Avellino,AV,Campania,15,83040
+Calitri,64015,Avellino,AV,Campania,15,83045
+Candida,64016,Avellino,AV,Campania,15,83040
+Caposele,64017,Avellino,AV,Campania,15,83040
+Capriglia Irpina,64018,Avellino,AV,Campania,15,83010
+Carife,64019,Avellino,AV,Campania,15,83040
+Casalbore,64020,Avellino,AV,Campania,15,83034
+Cassano Irpino,64021,Avellino,AV,Campania,15,83040
+Castel Baronia,64022,Avellino,AV,Campania,15,83040
+Castelfranci,64023,Avellino,AV,Campania,15,83040
+Castelvetere sul Calore,64024,Avellino,AV,Campania,15,83040
+Cervinara,64025,Avellino,AV,Campania,15,83012
+Cesinali,64026,Avellino,AV,Campania,15,83020
+Chianche,64027,Avellino,AV,Campania,15,83010
+Chiusano di San Domenico,64028,Avellino,AV,Campania,15,83040
+Contrada,64029,Avellino,AV,Campania,15,83020
+Conza della Campania,64030,Avellino,AV,Campania,15,83040
+Domicella,64031,Avellino,AV,Campania,15,83020
+Flumeri,64032,Avellino,AV,Campania,15,83040
+Fontanarosa,64033,Avellino,AV,Campania,15,83040
+Forino,64034,Avellino,AV,Campania,15,83020
+Frigento,64035,Avellino,AV,Campania,15,83040
+Gesualdo,64036,Avellino,AV,Campania,15,83040
+Greci,64037,Avellino,AV,Campania,15,83030
+Grottaminarda,64038,Avellino,AV,Campania,15,83035
+Grottolella,64039,Avellino,AV,Campania,15,83010
+Guardia Lombardi,64040,Avellino,AV,Campania,15,83040
+Lacedonia,64041,Avellino,AV,Campania,15,83046
+Lapio,64042,Avellino,AV,Campania,15,83030
+Lauro,64043,Avellino,AV,Campania,15,83023
+Lioni,64044,Avellino,AV,Campania,15,83047
+Luogosano,64045,Avellino,AV,Campania,15,83040
+Manocalzati,64046,Avellino,AV,Campania,15,83030
+Marzano di Nola,64047,Avellino,AV,Campania,15,83020
+Melito Irpino,64048,Avellino,AV,Campania,15,83030
+Mercogliano,64049,Avellino,AV,Campania,15,83013
+Mirabella Eclano,64050,Avellino,AV,Campania,15,83036
+Montaguto,64051,Avellino,AV,Campania,15,83030
+Montecalvo Irpino,64052,Avellino,AV,Campania,15,83037
+Montefalcione,64053,Avellino,AV,Campania,15,83030
+Monteforte Irpino,64054,Avellino,AV,Campania,15,83024
+Montefredane,64055,Avellino,AV,Campania,15,83030
+Montefusco,64056,Avellino,AV,Campania,15,83030
+Montella,64057,Avellino,AV,Campania,15,83048
+Montemarano,64058,Avellino,AV,Campania,15,83040
+Montemiletto,64059,Avellino,AV,Campania,15,83038
+Monteverde,64060,Avellino,AV,Campania,15,83049
+Morra De Sanctis,64063,Avellino,AV,Campania,15,83040
+Moschiano,64064,Avellino,AV,Campania,15,83020
+Mugnano del Cardinale,64065,Avellino,AV,Campania,15,83027
+Nusco,64066,Avellino,AV,Campania,15,83051
+Ospedaletto d'Alpinolo,64067,Avellino,AV,Campania,15,83014
+Pago del Vallo di Lauro,64068,Avellino,AV,Campania,15,83020
+Parolise,64069,Avellino,AV,Campania,15,83050
+Paternopoli,64070,Avellino,AV,Campania,15,83052
+Petruro Irpino,64071,Avellino,AV,Campania,15,83010
+Pietradefusi,64072,Avellino,AV,Campania,15,83030
+Pietrastornina,64073,Avellino,AV,Campania,15,83015
+Prata di Principato Ultra,64074,Avellino,AV,Campania,15,83030
+Pratola Serra,64075,Avellino,AV,Campania,15,83039
+Quadrelle,64076,Avellino,AV,Campania,15,83020
+Quindici,64077,Avellino,AV,Campania,15,83020
+Roccabascerana,64078,Avellino,AV,Campania,15,83016
+Rocca San Felice,64079,Avellino,AV,Campania,15,83050
+Rotondi,64080,Avellino,AV,Campania,15,83017
+Salza Irpina,64081,Avellino,AV,Campania,15,83050
+San Mango sul Calore,64082,Avellino,AV,Campania,15,83050
+San Martino Valle Caudina,64083,Avellino,AV,Campania,15,83018
+San Michele di Serino,64084,Avellino,AV,Campania,15,83020
+San Nicola Baronia,64085,Avellino,AV,Campania,15,83050
+San Potito Ultra,64086,Avellino,AV,Campania,15,83050
+San Sossio Baronia,64087,Avellino,AV,Campania,15,83050
+Santa Lucia di Serino,64088,Avellino,AV,Campania,15,83020
+Sant'Andrea di Conza,64089,Avellino,AV,Campania,15,83053
+Sant'Angelo all'Esca,64090,Avellino,AV,Campania,15,83050
+Sant'Angelo a Scala,64091,Avellino,AV,Campania,15,83010
+Sant'Angelo dei Lombardi,64092,Avellino,AV,Campania,15,83054
+Santa Paolina,64093,Avellino,AV,Campania,15,83030
+Santo Stefano del Sole,64095,Avellino,AV,Campania,15,83050
+Savignano Irpino,64096,Avellino,AV,Campania,15,83030
+Scampitella,64097,Avellino,AV,Campania,15,83050
+Senerchia,64098,Avellino,AV,Campania,15,83050
+Serino,64099,Avellino,AV,Campania,15,83028
+Sirignano,64100,Avellino,AV,Campania,15,83020
+Solofra,64101,Avellino,AV,Campania,15,83029
+Sorbo Serpico,64102,Avellino,AV,Campania,15,83050
+Sperone,64103,Avellino,AV,Campania,15,83020
+Sturno,64104,Avellino,AV,Campania,15,83055
+Summonte,64105,Avellino,AV,Campania,15,83010
+Taurano,64106,Avellino,AV,Campania,15,83020
+Taurasi,64107,Avellino,AV,Campania,15,83030
+Teora,64108,Avellino,AV,Campania,15,83056
+Torella dei Lombardi,64109,Avellino,AV,Campania,15,83057
+Torre Le Nocelle,64110,Avellino,AV,Campania,15,83030
+Torrioni,64111,Avellino,AV,Campania,15,83010
+Trevico,64112,Avellino,AV,Campania,15,83058
+Tufo,64113,Avellino,AV,Campania,15,83010
+Vallata,64114,Avellino,AV,Campania,15,83059
+Vallesaccarda,64115,Avellino,AV,Campania,15,83050
+Venticano,64116,Avellino,AV,Campania,15,83030
+Villamaina,64117,Avellino,AV,Campania,15,83050
+Villanova del Battista,64118,Avellino,AV,Campania,15,83030
+Volturara Irpina,64119,Avellino,AV,Campania,15,83050
+Zungoli,64120,Avellino,AV,Campania,15,83030
+Montoro,64121,Avellino,AV,Campania,15,83025
+Acerno,65001,Salerno,SA,Campania,15,84042
+Agropoli,65002,Salerno,SA,Campania,15,84043
+Albanella,65003,Salerno,SA,Campania,15,84044
+Alfano,65004,Salerno,SA,Campania,15,84040
+Altavilla Silentina,65005,Salerno,SA,Campania,15,84045
+Amalfi,65006,Salerno,SA,Campania,15,84011
+Angri,65007,Salerno,SA,Campania,15,84012
+Aquara,65008,Salerno,SA,Campania,15,84020
+Ascea,65009,Salerno,SA,Campania,15,84046
+Atena Lucana,65010,Salerno,SA,Campania,15,84030
+Atrani,65011,Salerno,SA,Campania,15,84010
+Auletta,65012,Salerno,SA,Campania,15,84031
+Baronissi,65013,Salerno,SA,Campania,15,84081
+Battipaglia,65014,Salerno,SA,Campania,15,84091
+Bellosguardo,65015,Salerno,SA,Campania,15,84020
+Bracigliano,65016,Salerno,SA,Campania,15,84082
+Buccino,65017,Salerno,SA,Campania,15,84021
+Buonabitacolo,65018,Salerno,SA,Campania,15,84032
+Caggiano,65019,Salerno,SA,Campania,15,84030
+Calvanico,65020,Salerno,SA,Campania,15,84080
+Camerota,65021,Salerno,SA,Campania,15,84040
+Campagna,65022,Salerno,SA,Campania,15,84022
+Campora,65023,Salerno,SA,Campania,15,84040
+Cannalonga,65024,Salerno,SA,Campania,15,84040
+Capaccio Paestum,65025,Salerno,SA,Campania,15,84047
+Casalbuono,65026,Salerno,SA,Campania,15,84030
+Casaletto Spartano,65027,Salerno,SA,Campania,15,84030
+Casal Velino,65028,Salerno,SA,Campania,15,84040
+Caselle in Pittari,65029,Salerno,SA,Campania,15,84030
+Castelcivita,65030,Salerno,SA,Campania,15,84020
+Castellabate,65031,Salerno,SA,Campania,15,84048
+Castelnuovo Cilento,65032,Salerno,SA,Campania,15,84040
+Castelnuovo di Conza,65033,Salerno,SA,Campania,15,84020
+Castel San Giorgio,65034,Salerno,SA,Campania,15,84083
+Castel San Lorenzo,65035,Salerno,SA,Campania,15,84049
+Castiglione del Genovesi,65036,Salerno,SA,Campania,15,84090
+Cava de' Tirreni,65037,Salerno,SA,Campania,15,84013
+Celle di Bulgheria,65038,Salerno,SA,Campania,15,84040
+Centola,65039,Salerno,SA,Campania,15,84051
+Ceraso,65040,Salerno,SA,Campania,15,84052
+Cetara,65041,Salerno,SA,Campania,15,84010
+Cicerale,65042,Salerno,SA,Campania,15,84053
+Colliano,65043,Salerno,SA,Campania,15,84020
+Conca dei Marini,65044,Salerno,SA,Campania,15,84010
+Controne,65045,Salerno,SA,Campania,15,84020
+Contursi Terme,65046,Salerno,SA,Campania,15,84024
+Corbara,65047,Salerno,SA,Campania,15,84010
+Corleto Monforte,65048,Salerno,SA,Campania,15,84020
+Cuccaro Vetere,65049,Salerno,SA,Campania,15,84050
+Eboli,65050,Salerno,SA,Campania,15,84025
+Felitto,65051,Salerno,SA,Campania,15,84055
+Fisciano,65052,Salerno,SA,Campania,15,84084
+Furore,65053,Salerno,SA,Campania,15,84010
+Futani,65054,Salerno,SA,Campania,15,84050
+Giffoni Sei Casali,65055,Salerno,SA,Campania,15,84090
+Giffoni Valle Piana,65056,Salerno,SA,Campania,15,84095
+Gioi,65057,Salerno,SA,Campania,15,84056
+Giungano,65058,Salerno,SA,Campania,15,84050
+Ispani,65059,Salerno,SA,Campania,15,84050
+Laureana Cilento,65060,Salerno,SA,Campania,15,84050
+Laurino,65061,Salerno,SA,Campania,15,84057
+Laurito,65062,Salerno,SA,Campania,15,84050
+Laviano,65063,Salerno,SA,Campania,15,84020
+Lustra,65064,Salerno,SA,Campania,15,84050
+Magliano Vetere,65065,Salerno,SA,Campania,15,84050
+Maiori,65066,Salerno,SA,Campania,15,84010
+Mercato San Severino,65067,Salerno,SA,Campania,15,84085
+Minori,65068,Salerno,SA,Campania,15,84010
+Moio della Civitella,65069,Salerno,SA,Campania,15,84060
+Montano Antilia,65070,Salerno,SA,Campania,15,84060
+Montecorice,65071,Salerno,SA,Campania,15,84060
+Montecorvino Pugliano,65072,Salerno,SA,Campania,15,84090
+Montecorvino Rovella,65073,Salerno,SA,Campania,15,84096
+Monteforte Cilento,65074,Salerno,SA,Campania,15,84060
+Monte San Giacomo,65075,Salerno,SA,Campania,15,84030
+Montesano sulla Marcellana,65076,Salerno,SA,Campania,15,84033
+Morigerati,65077,Salerno,SA,Campania,15,84030
+Nocera Inferiore,65078,Salerno,SA,Campania,15,84014
+Nocera Superiore,65079,Salerno,SA,Campania,15,84015
+Novi Velia,65080,Salerno,SA,Campania,15,84060
+Ogliastro Cilento,65081,Salerno,SA,Campania,15,84061
+Olevano sul Tusciano,65082,Salerno,SA,Campania,15,84062
+Oliveto Citra,65083,Salerno,SA,Campania,15,84020
+Omignano,65084,Salerno,SA,Campania,15,84060
+Orria,65085,Salerno,SA,Campania,15,84060
+Ottati,65086,Salerno,SA,Campania,15,84020
+Padula,65087,Salerno,SA,Campania,15,84034
+Pagani,65088,Salerno,SA,Campania,15,84016
+Palomonte,65089,Salerno,SA,Campania,15,84020
+Pellezzano,65090,Salerno,SA,Campania,15,84080
+Perdifumo,65091,Salerno,SA,Campania,15,84060
+Perito,65092,Salerno,SA,Campania,15,84060
+Pertosa,65093,Salerno,SA,Campania,15,84030
+Petina,65094,Salerno,SA,Campania,15,84020
+Piaggine,65095,Salerno,SA,Campania,15,84065
+Pisciotta,65096,Salerno,SA,Campania,15,84066
+Polla,65097,Salerno,SA,Campania,15,84035
+Pollica,65098,Salerno,SA,Campania,15,84068
+Pontecagnano Faiano,65099,Salerno,SA,Campania,15,84098
+Positano,65100,Salerno,SA,Campania,15,84017
+Postiglione,65101,Salerno,SA,Campania,15,84026
+Praiano,65102,Salerno,SA,Campania,15,84010
+Prignano Cilento,65103,Salerno,SA,Campania,15,84060
+Ravello,65104,Salerno,SA,Campania,15,84010
+Ricigliano,65105,Salerno,SA,Campania,15,84020
+Roccadaspide,65106,Salerno,SA,Campania,15,84069
+Roccagloriosa,65107,Salerno,SA,Campania,15,84060
+Roccapiemonte,65108,Salerno,SA,Campania,15,84086
+Rofrano,65109,Salerno,SA,Campania,15,84070
+Romagnano al Monte,65110,Salerno,SA,Campania,15,84020
+Roscigno,65111,Salerno,SA,Campania,15,84020
+Rutino,65112,Salerno,SA,Campania,15,84070
+Sacco,65113,Salerno,SA,Campania,15,84070
+Sala Consilina,65114,Salerno,SA,Campania,15,84036
+Salento,65115,Salerno,SA,Campania,15,84070
+Salerno,65116,Salerno,SA,Campania,15,84121
+Salvitelle,65117,Salerno,SA,Campania,15,84020
+San Cipriano Picentino,65118,Salerno,SA,Campania,15,84099
+San Giovanni a Piro,65119,Salerno,SA,Campania,15,84070
+San Gregorio Magno,65120,Salerno,SA,Campania,15,84020
+San Mango Piemonte,65121,Salerno,SA,Campania,15,84090
+San Marzano sul Sarno,65122,Salerno,SA,Campania,15,84010
+San Mauro Cilento,65123,Salerno,SA,Campania,15,84070
+San Mauro la Bruca,65124,Salerno,SA,Campania,15,84070
+San Pietro al Tanagro,65125,Salerno,SA,Campania,15,84030
+San Rufo,65126,Salerno,SA,Campania,15,84030
+Santa Marina,65127,Salerno,SA,Campania,15,84070
+Sant'Angelo a Fasanella,65128,Salerno,SA,Campania,15,84027
+Sant'Arsenio,65129,Salerno,SA,Campania,15,84037
+Sant'Egidio del Monte Albino,65130,Salerno,SA,Campania,15,84010
+Santomenna,65131,Salerno,SA,Campania,15,84020
+San Valentino Torio,65132,Salerno,SA,Campania,15,84010
+Sanza,65133,Salerno,SA,Campania,15,84030
+Sapri,65134,Salerno,SA,Campania,15,84073
+Sarno,65135,Salerno,SA,Campania,15,84087
+Sassano,65136,Salerno,SA,Campania,15,84038
+Scafati,65137,Salerno,SA,Campania,15,84018
+Scala,65138,Salerno,SA,Campania,15,84010
+Serramezzana,65139,Salerno,SA,Campania,15,84070
+Serre,65140,Salerno,SA,Campania,15,84028
+Sessa Cilento,65141,Salerno,SA,Campania,15,84074
+Siano,65142,Salerno,SA,Campania,15,84088
+Sicignano degli Alburni,65143,Salerno,SA,Campania,15,84029
+Stella Cilento,65144,Salerno,SA,Campania,15,84070
+Stio,65145,Salerno,SA,Campania,15,84075
+Teggiano,65146,Salerno,SA,Campania,15,84039
+Torchiara,65147,Salerno,SA,Campania,15,84076
+Torraca,65148,Salerno,SA,Campania,15,84030
+Torre Orsaia,65149,Salerno,SA,Campania,15,84077
+Tortorella,65150,Salerno,SA,Campania,15,84030
+Tramonti,65151,Salerno,SA,Campania,15,84010
+Trentinara,65152,Salerno,SA,Campania,15,84070
+Valle dell'Angelo,65153,Salerno,SA,Campania,15,84070
+Vallo della Lucania,65154,Salerno,SA,Campania,15,84078
+Valva,65155,Salerno,SA,Campania,15,84020
+Vibonati,65156,Salerno,SA,Campania,15,84079
+Vietri sul Mare,65157,Salerno,SA,Campania,15,84019
+Bellizzi,65158,Salerno,SA,Campania,15,84092
+Accadia,71001,Foggia,FG,Puglia,16,71021
+Alberona,71002,Foggia,FG,Puglia,16,71031
+Anzano di Puglia,71003,Foggia,FG,Puglia,16,71020
+Apricena,71004,Foggia,FG,Puglia,16,71011
+Ascoli Satriano,71005,Foggia,FG,Puglia,16,71022
+Biccari,71006,Foggia,FG,Puglia,16,71032
+Bovino,71007,Foggia,FG,Puglia,16,71023
+Cagnano Varano,71008,Foggia,FG,Puglia,16,71010
+Candela,71009,Foggia,FG,Puglia,16,71024
+Carapelle,71010,Foggia,FG,Puglia,16,71041
+Carlantino,71011,Foggia,FG,Puglia,16,71030
+Carpino,71012,Foggia,FG,Puglia,16,71010
+Casalnuovo Monterotaro,71013,Foggia,FG,Puglia,16,71033
+Casalvecchio di Puglia,71014,Foggia,FG,Puglia,16,71030
+Castelluccio dei Sauri,71015,Foggia,FG,Puglia,16,71025
+Castelluccio Valmaggiore,71016,Foggia,FG,Puglia,16,71020
+Castelnuovo della Daunia,71017,Foggia,FG,Puglia,16,71034
+Celenza Valfortore,71018,Foggia,FG,Puglia,16,71035
+Celle di San Vito,71019,Foggia,FG,Puglia,16,71020
+Cerignola,71020,Foggia,FG,Puglia,16,71042
+Chieuti,71021,Foggia,FG,Puglia,16,71010
+Deliceto,71022,Foggia,FG,Puglia,16,71026
+Faeto,71023,Foggia,FG,Puglia,16,71020
+Foggia,71024,Foggia,FG,Puglia,16,71121
+Ischitella,71025,Foggia,FG,Puglia,16,71010
+Isole Tremiti,71026,Foggia,FG,Puglia,16,71040
+Lesina,71027,Foggia,FG,Puglia,16,71010
+Lucera,71028,Foggia,FG,Puglia,16,71036
+Manfredonia,71029,Foggia,FG,Puglia,16,71043
+Mattinata,71031,Foggia,FG,Puglia,16,71030
+Monteleone di Puglia,71032,Foggia,FG,Puglia,16,71020
+Monte Sant'Angelo,71033,Foggia,FG,Puglia,16,71037
+Motta Montecorvino,71034,Foggia,FG,Puglia,16,71030
+Orsara di Puglia,71035,Foggia,FG,Puglia,16,71027
+Orta Nova,71036,Foggia,FG,Puglia,16,71045
+Panni,71037,Foggia,FG,Puglia,16,71020
+Peschici,71038,Foggia,FG,Puglia,16,71010
+Pietramontecorvino,71039,Foggia,FG,Puglia,16,71038
+Poggio Imperiale,71040,Foggia,FG,Puglia,16,71010
+Rignano Garganico,71041,Foggia,FG,Puglia,16,71010
+Rocchetta Sant'Antonio,71042,Foggia,FG,Puglia,16,71020
+Rodi Garganico,71043,Foggia,FG,Puglia,16,71012
+Roseto Valfortore,71044,Foggia,FG,Puglia,16,71039
+San Giovanni Rotondo,71046,Foggia,FG,Puglia,16,71013
+San Marco in Lamis,71047,Foggia,FG,Puglia,16,71014
+San Marco la Catola,71048,Foggia,FG,Puglia,16,71030
+San Nicandro Garganico,71049,Foggia,FG,Puglia,16,71015
+San Paolo di Civitate,71050,Foggia,FG,Puglia,16,71010
+San Severo,71051,Foggia,FG,Puglia,16,71016
+Sant'Agata di Puglia,71052,Foggia,FG,Puglia,16,71028
+Serracapriola,71053,Foggia,FG,Puglia,16,71010
+Stornara,71054,Foggia,FG,Puglia,16,71047
+Stornarella,71055,Foggia,FG,Puglia,16,71048
+Torremaggiore,71056,Foggia,FG,Puglia,16,71017
+Troia,71058,Foggia,FG,Puglia,16,71029
+Vico del Gargano,71059,Foggia,FG,Puglia,16,71018
+Vieste,71060,Foggia,FG,Puglia,16,71019
+Volturara Appula,71061,Foggia,FG,Puglia,16,71030
+Volturino,71062,Foggia,FG,Puglia,16,71030
+Ordona,71063,Foggia,FG,Puglia,16,71040
+Zapponeta,71064,Foggia,FG,Puglia,16,71030
+Acquaviva delle Fonti,72001,Bari,BA,Puglia,16,70021
+Adelfia,72002,Bari,BA,Puglia,16,70010
+Alberobello,72003,Bari,BA,Puglia,16,70011
+Altamura,72004,Bari,BA,Puglia,16,70022
+Bari,72006,Bari,BA,Puglia,16,70122
+Binetto,72008,Bari,BA,Puglia,16,70020
+Bitetto,72010,Bari,BA,Puglia,16,70020
+Bitonto,72011,Bari,BA,Puglia,16,70032
+Bitritto,72012,Bari,BA,Puglia,16,70020
+Capurso,72014,Bari,BA,Puglia,16,70010
+Casamassima,72015,Bari,BA,Puglia,16,70010
+Cassano delle Murge,72016,Bari,BA,Puglia,16,70020
+Castellana Grotte,72017,Bari,BA,Puglia,16,70013
+Cellamare,72018,Bari,BA,Puglia,16,70010
+Conversano,72019,Bari,BA,Puglia,16,70014
+Corato,72020,Bari,BA,Puglia,16,70033
+Gioia del Colle,72021,Bari,BA,Puglia,16,70023
+Giovinazzo,72022,Bari,BA,Puglia,16,70054
+Gravina in Puglia,72023,Bari,BA,Puglia,16,70024
+Grumo Appula,72024,Bari,BA,Puglia,16,70025
+Locorotondo,72025,Bari,BA,Puglia,16,70010
+Modugno,72027,Bari,BA,Puglia,16,70026
+Mola di Bari,72028,Bari,BA,Puglia,16,70042
+Molfetta,72029,Bari,BA,Puglia,16,70056
+Monopoli,72030,Bari,BA,Puglia,16,70043
+Noci,72031,Bari,BA,Puglia,16,70015
+Noicattaro,72032,Bari,BA,Puglia,16,70016
+Palo del Colle,72033,Bari,BA,Puglia,16,70027
+Poggiorsini,72034,Bari,BA,Puglia,16,70020
+Polignano a Mare,72035,Bari,BA,Puglia,16,70044
+Putignano,72036,Bari,BA,Puglia,16,70017
+Rutigliano,72037,Bari,BA,Puglia,16,70018
+Ruvo di Puglia,72038,Bari,BA,Puglia,16,70037
+Sammichele di Bari,72039,Bari,BA,Puglia,16,70010
+Sannicandro di Bari,72040,Bari,BA,Puglia,16,70028
+Santeramo in Colle,72041,Bari,BA,Puglia,16,70029
+Terlizzi,72043,Bari,BA,Puglia,16,70038
+Toritto,72044,Bari,BA,Puglia,16,70020
+Triggiano,72046,Bari,BA,Puglia,16,70019
+Turi,72047,Bari,BA,Puglia,16,70010
+Valenzano,72048,Bari,BA,Puglia,16,70010
+Avetrana,73001,Taranto,TA,Puglia,16,74020
+Carosino,73002,Taranto,TA,Puglia,16,74021
+Castellaneta,73003,Taranto,TA,Puglia,16,74011
+Crispiano,73004,Taranto,TA,Puglia,16,74012
+Faggiano,73005,Taranto,TA,Puglia,16,74020
+Fragagnano,73006,Taranto,TA,Puglia,16,74022
+Ginosa,73007,Taranto,TA,Puglia,16,74013
+Grottaglie,73008,Taranto,TA,Puglia,16,74023
+Laterza,73009,Taranto,TA,Puglia,16,74014
+Leporano,73010,Taranto,TA,Puglia,16,74020
+Lizzano,73011,Taranto,TA,Puglia,16,74020
+Manduria,73012,Taranto,TA,Puglia,16,74024
+Martina Franca,73013,Taranto,TA,Puglia,16,74015
+Maruggio,73014,Taranto,TA,Puglia,16,74020
+Massafra,73015,Taranto,TA,Puglia,16,74016
+Monteiasi,73016,Taranto,TA,Puglia,16,74020
+Montemesola,73017,Taranto,TA,Puglia,16,74020
+Monteparano,73018,Taranto,TA,Puglia,16,74020
+Mottola,73019,Taranto,TA,Puglia,16,74017
+Palagianello,73020,Taranto,TA,Puglia,16,74018
+Palagiano,73021,Taranto,TA,Puglia,16,74019
+Pulsano,73022,Taranto,TA,Puglia,16,74026
+Roccaforzata,73023,Taranto,TA,Puglia,16,74020
+San Giorgio Ionico,73024,Taranto,TA,Puglia,16,74027
+San Marzano di San Giuseppe,73025,Taranto,TA,Puglia,16,74020
+Sava,73026,Taranto,TA,Puglia,16,74028
+Taranto,73027,Taranto,TA,Puglia,16,74120
+Torricella,73028,Taranto,TA,Puglia,16,74020
+Statte,73029,Taranto,TA,Puglia,16,74010
+Brindisi,74001,Brindisi,BR,Puglia,16,72100
+Carovigno,74002,Brindisi,BR,Puglia,16,72012
+Ceglie Messapica,74003,Brindisi,BR,Puglia,16,72013
+Cellino San Marco,74004,Brindisi,BR,Puglia,16,72020
+Cisternino,74005,Brindisi,BR,Puglia,16,72014
+Erchie,74006,Brindisi,BR,Puglia,16,72020
+Fasano,74007,Brindisi,BR,Puglia,16,72015
+Francavilla Fontana,74008,Brindisi,BR,Puglia,16,72021
+Latiano,74009,Brindisi,BR,Puglia,16,72022
+Mesagne,74010,Brindisi,BR,Puglia,16,72023
+Oria,74011,Brindisi,BR,Puglia,16,72024
+Ostuni,74012,Brindisi,BR,Puglia,16,72017
+San Donaci,74013,Brindisi,BR,Puglia,16,72025
+San Michele Salentino,74014,Brindisi,BR,Puglia,16,72018
+San Pancrazio Salentino,74015,Brindisi,BR,Puglia,16,72026
+San Pietro Vernotico,74016,Brindisi,BR,Puglia,16,72027
+San Vito dei Normanni,74017,Brindisi,BR,Puglia,16,72019
+Torchiarolo,74018,Brindisi,BR,Puglia,16,72020
+Torre Santa Susanna,74019,Brindisi,BR,Puglia,16,72028
+Villa Castelli,74020,Brindisi,BR,Puglia,16,72029
+Alessano,75002,Lecce,LE,Puglia,16,73031
+Alezio,75003,Lecce,LE,Puglia,16,73011
+Alliste,75004,Lecce,LE,Puglia,16,73040
+Andrano,75005,Lecce,LE,Puglia,16,73032
+Aradeo,75006,Lecce,LE,Puglia,16,73040
+Arnesano,75007,Lecce,LE,Puglia,16,73010
+Bagnolo del Salento,75008,Lecce,LE,Puglia,16,73020
+Botrugno,75009,Lecce,LE,Puglia,16,73020
+Calimera,75010,Lecce,LE,Puglia,16,73021
+Campi Salentina,75011,Lecce,LE,Puglia,16,73012
+Cannole,75012,Lecce,LE,Puglia,16,73020
+Caprarica di Lecce,75013,Lecce,LE,Puglia,16,73010
+Carmiano,75014,Lecce,LE,Puglia,16,73041
+Carpignano Salentino,75015,Lecce,LE,Puglia,16,73020
+Casarano,75016,Lecce,LE,Puglia,16,73042
+Castri di Lecce,75017,Lecce,LE,Puglia,16,73020
+Castrignano de' Greci,75018,Lecce,LE,Puglia,16,73020
+Castrignano del Capo,75019,Lecce,LE,Puglia,16,73040
+Cavallino,75020,Lecce,LE,Puglia,16,73020
+Collepasso,75021,Lecce,LE,Puglia,16,73040
+Copertino,75022,Lecce,LE,Puglia,16,73043
+Corigliano d'Otranto,75023,Lecce,LE,Puglia,16,73022
+Corsano,75024,Lecce,LE,Puglia,16,73033
+Cursi,75025,Lecce,LE,Puglia,16,73020
+Cutrofiano,75026,Lecce,LE,Puglia,16,73020
+Diso,75027,Lecce,LE,Puglia,16,73030
+Gagliano del Capo,75028,Lecce,LE,Puglia,16,73034
+Galatina,75029,Lecce,LE,Puglia,16,73013
+Galatone,75030,Lecce,LE,Puglia,16,73044
+Gallipoli,75031,Lecce,LE,Puglia,16,73014
+Giuggianello,75032,Lecce,LE,Puglia,16,73030
+Giurdignano,75033,Lecce,LE,Puglia,16,73020
+Guagnano,75034,Lecce,LE,Puglia,16,73010
+Lecce,75035,Lecce,LE,Puglia,16,73100
+Lequile,75036,Lecce,LE,Puglia,16,73010
+Leverano,75037,Lecce,LE,Puglia,16,73045
+Lizzanello,75038,Lecce,LE,Puglia,16,73023
+Maglie,75039,Lecce,LE,Puglia,16,73024
+Martano,75040,Lecce,LE,Puglia,16,73025
+Martignano,75041,Lecce,LE,Puglia,16,73020
+Matino,75042,Lecce,LE,Puglia,16,73046
+Melendugno,75043,Lecce,LE,Puglia,16,73026
+Melissano,75044,Lecce,LE,Puglia,16,73040
+Melpignano,75045,Lecce,LE,Puglia,16,73020
+Miggiano,75046,Lecce,LE,Puglia,16,73035
+Minervino di Lecce,75047,Lecce,LE,Puglia,16,73027
+Monteroni di Lecce,75048,Lecce,LE,Puglia,16,73047
+Montesano Salentino,75049,Lecce,LE,Puglia,16,73030
+Morciano di Leuca,75050,Lecce,LE,Puglia,16,73040
+Muro Leccese,75051,Lecce,LE,Puglia,16,73036
+Nardò,75052,Lecce,LE,Puglia,16,73048
+Neviano,75053,Lecce,LE,Puglia,16,73040
+Nociglia,75054,Lecce,LE,Puglia,16,73020
+Novoli,75055,Lecce,LE,Puglia,16,73051
+Ortelle,75056,Lecce,LE,Puglia,16,73030
+Otranto,75057,Lecce,LE,Puglia,16,73028
+Palmariggi,75058,Lecce,LE,Puglia,16,73020
+Parabita,75059,Lecce,LE,Puglia,16,73052
+Patù,75060,Lecce,LE,Puglia,16,73053
+Poggiardo,75061,Lecce,LE,Puglia,16,73037
+Racale,75063,Lecce,LE,Puglia,16,73055
+Ruffano,75064,Lecce,LE,Puglia,16,73049
+Salice Salentino,75065,Lecce,LE,Puglia,16,73015
+Salve,75066,Lecce,LE,Puglia,16,73050
+Sanarica,75067,Lecce,LE,Puglia,16,73030
+San Cesario di Lecce,75068,Lecce,LE,Puglia,16,73016
+San Donato di Lecce,75069,Lecce,LE,Puglia,16,73010
+Sannicola,75070,Lecce,LE,Puglia,16,73017
+San Pietro in Lama,75071,Lecce,LE,Puglia,16,73010
+Santa Cesarea Terme,75072,Lecce,LE,Puglia,16,73020
+Scorrano,75073,Lecce,LE,Puglia,16,73020
+Seclì,75074,Lecce,LE,Puglia,16,73050
+Sogliano Cavour,75075,Lecce,LE,Puglia,16,73010
+Soleto,75076,Lecce,LE,Puglia,16,73010
+Specchia,75077,Lecce,LE,Puglia,16,73040
+Spongano,75078,Lecce,LE,Puglia,16,73038
+Squinzano,75079,Lecce,LE,Puglia,16,73018
+Sternatia,75080,Lecce,LE,Puglia,16,73010
+Supersano,75081,Lecce,LE,Puglia,16,73040
+Surano,75082,Lecce,LE,Puglia,16,73030
+Surbo,75083,Lecce,LE,Puglia,16,73010
+Taurisano,75084,Lecce,LE,Puglia,16,73056
+Taviano,75085,Lecce,LE,Puglia,16,73057
+Tiggiano,75086,Lecce,LE,Puglia,16,73030
+Trepuzzi,75087,Lecce,LE,Puglia,16,73019
+Tricase,75088,Lecce,LE,Puglia,16,73039
+Tuglie,75089,Lecce,LE,Puglia,16,73058
+Ugento,75090,Lecce,LE,Puglia,16,73059
+Uggiano la Chiesa,75091,Lecce,LE,Puglia,16,73020
+Veglie,75092,Lecce,LE,Puglia,16,73010
+Vernole,75093,Lecce,LE,Puglia,16,73029
+Zollino,75094,Lecce,LE,Puglia,16,73010
+San Cassiano,75095,Lecce,LE,Puglia,16,73020
+Castro,75096,Lecce,LE,Puglia,16,73030
+Porto Cesareo,75097,Lecce,LE,Puglia,16,73010
+Presicce-Acquarica,75098,Lecce,LE,Puglia,16,73054
+Andria,110001,Barletta-Andria-Trani,BT,Puglia,16,76123
+Barletta,110002,Barletta-Andria-Trani,BT,Puglia,16,76121
+Bisceglie,110003,Barletta-Andria-Trani,BT,Puglia,16,76011
+Canosa di Puglia,110004,Barletta-Andria-Trani,BT,Puglia,16,76012
+Margherita di Savoia,110005,Barletta-Andria-Trani,BT,Puglia,16,76016
+Minervino Murge,110006,Barletta-Andria-Trani,BT,Puglia,16,76013
+San Ferdinando di Puglia,110007,Barletta-Andria-Trani,BT,Puglia,16,76017
+Spinazzola,110008,Barletta-Andria-Trani,BT,Puglia,16,76014
+Trani,110009,Barletta-Andria-Trani,BT,Puglia,16,76125
+Trinitapoli,110010,Barletta-Andria-Trani,BT,Puglia,16,76015
+Abriola,76001,Potenza,PZ,Basilicata,17,85010
+Acerenza,76002,Potenza,PZ,Basilicata,17,85011
+Albano di Lucania,76003,Potenza,PZ,Basilicata,17,85010
+Anzi,76004,Potenza,PZ,Basilicata,17,85010
+Armento,76005,Potenza,PZ,Basilicata,17,85010
+Atella,76006,Potenza,PZ,Basilicata,17,85020
+Avigliano,76007,Potenza,PZ,Basilicata,17,85021
+Balvano,76008,Potenza,PZ,Basilicata,17,85050
+Banzi,76009,Potenza,PZ,Basilicata,17,85010
+Baragiano,76010,Potenza,PZ,Basilicata,17,85050
+Barile,76011,Potenza,PZ,Basilicata,17,85022
+Bella,76012,Potenza,PZ,Basilicata,17,85051
+Brienza,76013,Potenza,PZ,Basilicata,17,85050
+Brindisi Montagna,76014,Potenza,PZ,Basilicata,17,85010
+Calvello,76015,Potenza,PZ,Basilicata,17,85010
+Calvera,76016,Potenza,PZ,Basilicata,17,85030
+Campomaggiore,76017,Potenza,PZ,Basilicata,17,85010
+Cancellara,76018,Potenza,PZ,Basilicata,17,85010
+Carbone,76019,Potenza,PZ,Basilicata,17,85030
+San Paolo Albanese,76020,Potenza,PZ,Basilicata,17,85030
+Castelgrande,76021,Potenza,PZ,Basilicata,17,85050
+Castelluccio Inferiore,76022,Potenza,PZ,Basilicata,17,85040
+Castelluccio Superiore,76023,Potenza,PZ,Basilicata,17,85040
+Castelmezzano,76024,Potenza,PZ,Basilicata,17,85010
+Castelsaraceno,76025,Potenza,PZ,Basilicata,17,85031
+Castronuovo di Sant'Andrea,76026,Potenza,PZ,Basilicata,17,85030
+Cersosimo,76027,Potenza,PZ,Basilicata,17,85030
+Chiaromonte,76028,Potenza,PZ,Basilicata,17,85032
+Corleto Perticara,76029,Potenza,PZ,Basilicata,17,85012
+Episcopia,76030,Potenza,PZ,Basilicata,17,85033
+Fardella,76031,Potenza,PZ,Basilicata,17,85034
+Filiano,76032,Potenza,PZ,Basilicata,17,85020
+Forenza,76033,Potenza,PZ,Basilicata,17,85023
+Francavilla in Sinni,76034,Potenza,PZ,Basilicata,17,85034
+Gallicchio,76035,Potenza,PZ,Basilicata,17,85010
+Genzano di Lucania,76036,Potenza,PZ,Basilicata,17,85013
+Grumento Nova,76037,Potenza,PZ,Basilicata,17,85050
+Guardia Perticara,76038,Potenza,PZ,Basilicata,17,85010
+Lagonegro,76039,Potenza,PZ,Basilicata,17,85042
+Latronico,76040,Potenza,PZ,Basilicata,17,85043
+Laurenzana,76041,Potenza,PZ,Basilicata,17,85014
+Lauria,76042,Potenza,PZ,Basilicata,17,85044
+Lavello,76043,Potenza,PZ,Basilicata,17,85024
+Maratea,76044,Potenza,PZ,Basilicata,17,85046
+Marsico Nuovo,76045,Potenza,PZ,Basilicata,17,85052
+Marsicovetere,76046,Potenza,PZ,Basilicata,17,85050
+Maschito,76047,Potenza,PZ,Basilicata,17,85020
+Melfi,76048,Potenza,PZ,Basilicata,17,85025
+Missanello,76049,Potenza,PZ,Basilicata,17,85010
+Moliterno,76050,Potenza,PZ,Basilicata,17,85047
+Montemilone,76051,Potenza,PZ,Basilicata,17,85020
+Montemurro,76052,Potenza,PZ,Basilicata,17,85053
+Muro Lucano,76053,Potenza,PZ,Basilicata,17,85054
+Nemoli,76054,Potenza,PZ,Basilicata,17,85040
+Noepoli,76055,Potenza,PZ,Basilicata,17,85035
+Oppido Lucano,76056,Potenza,PZ,Basilicata,17,85015
+Palazzo San Gervasio,76057,Potenza,PZ,Basilicata,17,85026
+Pescopagano,76058,Potenza,PZ,Basilicata,17,85020
+Picerno,76059,Potenza,PZ,Basilicata,17,85055
+Pietragalla,76060,Potenza,PZ,Basilicata,17,85016
+Pietrapertosa,76061,Potenza,PZ,Basilicata,17,85010
+Pignola,76062,Potenza,PZ,Basilicata,17,85010
+Potenza,76063,Potenza,PZ,Basilicata,17,85100
+Rapolla,76064,Potenza,PZ,Basilicata,17,85027
+Rapone,76065,Potenza,PZ,Basilicata,17,85020
+Rionero in Vulture,76066,Potenza,PZ,Basilicata,17,85028
+Ripacandida,76067,Potenza,PZ,Basilicata,17,85020
+Rivello,76068,Potenza,PZ,Basilicata,17,85040
+Roccanova,76069,Potenza,PZ,Basilicata,17,85036
+Rotonda,76070,Potenza,PZ,Basilicata,17,85048
+Ruoti,76071,Potenza,PZ,Basilicata,17,85056
+Ruvo del Monte,76072,Potenza,PZ,Basilicata,17,85020
+San Chirico Nuovo,76073,Potenza,PZ,Basilicata,17,85010
+San Chirico Raparo,76074,Potenza,PZ,Basilicata,17,85030
+San Costantino Albanese,76075,Potenza,PZ,Basilicata,17,85030
+San Fele,76076,Potenza,PZ,Basilicata,17,85020
+San Martino d'Agri,76077,Potenza,PZ,Basilicata,17,85030
+San Severino Lucano,76078,Potenza,PZ,Basilicata,17,85030
+Sant'Angelo Le Fratte,76079,Potenza,PZ,Basilicata,17,85050
+Sant'Arcangelo,76080,Potenza,PZ,Basilicata,17,85037
+Sarconi,76081,Potenza,PZ,Basilicata,17,85050
+Sasso di Castalda,76082,Potenza,PZ,Basilicata,17,85050
+Satriano di Lucania,76083,Potenza,PZ,Basilicata,17,85050
+Savoia di Lucania,76084,Potenza,PZ,Basilicata,17,85050
+Senise,76085,Potenza,PZ,Basilicata,17,85038
+Spinoso,76086,Potenza,PZ,Basilicata,17,85039
+Teana,76087,Potenza,PZ,Basilicata,17,85032
+Terranova di Pollino,76088,Potenza,PZ,Basilicata,17,85030
+Tito,76089,Potenza,PZ,Basilicata,17,85050
+Tolve,76090,Potenza,PZ,Basilicata,17,85017
+Tramutola,76091,Potenza,PZ,Basilicata,17,85057
+Trecchina,76092,Potenza,PZ,Basilicata,17,85049
+Trivigno,76093,Potenza,PZ,Basilicata,17,85018
+Vaglio Basilicata,76094,Potenza,PZ,Basilicata,17,85010
+Venosa,76095,Potenza,PZ,Basilicata,17,85029
+Vietri di Potenza,76096,Potenza,PZ,Basilicata,17,85058
+Viggianello,76097,Potenza,PZ,Basilicata,17,85040
+Viggiano,76098,Potenza,PZ,Basilicata,17,85059
+Ginestra,76099,Potenza,PZ,Basilicata,17,85020
+Paterno,76100,Potenza,PZ,Basilicata,17,85050
+Accettura,77001,Matera,MT,Basilicata,17,75011
+Aliano,77002,Matera,MT,Basilicata,17,75010
+Bernalda,77003,Matera,MT,Basilicata,17,75012
+Calciano,77004,Matera,MT,Basilicata,17,75010
+Cirigliano,77005,Matera,MT,Basilicata,17,75010
+Colobraro,77006,Matera,MT,Basilicata,17,75021
+Craco,77007,Matera,MT,Basilicata,17,75010
+Ferrandina,77008,Matera,MT,Basilicata,17,75013
+Garaguso,77009,Matera,MT,Basilicata,17,75010
+Gorgoglione,77010,Matera,MT,Basilicata,17,75010
+Grassano,77011,Matera,MT,Basilicata,17,75014
+Grottole,77012,Matera,MT,Basilicata,17,75010
+Irsina,77013,Matera,MT,Basilicata,17,75022
+Matera,77014,Matera,MT,Basilicata,17,75100
+Miglionico,77015,Matera,MT,Basilicata,17,75010
+Montalbano Jonico,77016,Matera,MT,Basilicata,17,75023
+Montescaglioso,77017,Matera,MT,Basilicata,17,75024
+Nova Siri,77018,Matera,MT,Basilicata,17,75020
+Oliveto Lucano,77019,Matera,MT,Basilicata,17,75010
+Pisticci,77020,Matera,MT,Basilicata,17,75015
+Policoro,77021,Matera,MT,Basilicata,17,75025
+Pomarico,77022,Matera,MT,Basilicata,17,75016
+Rotondella,77023,Matera,MT,Basilicata,17,75026
+Salandra,77024,Matera,MT,Basilicata,17,75017
+San Giorgio Lucano,77025,Matera,MT,Basilicata,17,75027
+San Mauro Forte,77026,Matera,MT,Basilicata,17,75010
+Stigliano,77027,Matera,MT,Basilicata,17,75018
+Tricarico,77028,Matera,MT,Basilicata,17,75019
+Tursi,77029,Matera,MT,Basilicata,17,75028
+Valsinni,77030,Matera,MT,Basilicata,17,75029
+Scanzano Jonico,77031,Matera,MT,Basilicata,17,75020
+Acquaformosa,78001,Cosenza,CS,Calabria,18,87010
+Acquappesa,78002,Cosenza,CS,Calabria,18,87020
+Acri,78003,Cosenza,CS,Calabria,18,87041
+Aiello Calabro,78004,Cosenza,CS,Calabria,18,87031
+Aieta,78005,Cosenza,CS,Calabria,18,87020
+Albidona,78006,Cosenza,CS,Calabria,18,87070
+Alessandria del Carretto,78007,Cosenza,CS,Calabria,18,87070
+Altilia,78008,Cosenza,CS,Calabria,18,87040
+Altomonte,78009,Cosenza,CS,Calabria,18,87042
+Amantea,78010,Cosenza,CS,Calabria,18,87032
+Amendolara,78011,Cosenza,CS,Calabria,18,87071
+Aprigliano,78012,Cosenza,CS,Calabria,18,87051
+Belmonte Calabro,78013,Cosenza,CS,Calabria,18,87033
+Belsito,78014,Cosenza,CS,Calabria,18,87030
+Belvedere Marittimo,78015,Cosenza,CS,Calabria,18,87021
+Bianchi,78016,Cosenza,CS,Calabria,18,87050
+Bisignano,78017,Cosenza,CS,Calabria,18,87043
+Bocchigliero,78018,Cosenza,CS,Calabria,18,87060
+Bonifati,78019,Cosenza,CS,Calabria,18,87020
+Buonvicino,78020,Cosenza,CS,Calabria,18,87020
+Calopezzati,78021,Cosenza,CS,Calabria,18,87060
+Caloveto,78022,Cosenza,CS,Calabria,18,87060
+Campana,78023,Cosenza,CS,Calabria,18,87061
+Canna,78024,Cosenza,CS,Calabria,18,87070
+Cariati,78025,Cosenza,CS,Calabria,18,87062
+Carolei,78026,Cosenza,CS,Calabria,18,87030
+Carpanzano,78027,Cosenza,CS,Calabria,18,87050
+Cassano all'Ionio,78029,Cosenza,CS,Calabria,18,87011
+Castiglione Cosentino,78030,Cosenza,CS,Calabria,18,87040
+Castrolibero,78031,Cosenza,CS,Calabria,18,87040
+Castroregio,78032,Cosenza,CS,Calabria,18,87070
+Castrovillari,78033,Cosenza,CS,Calabria,18,87012
+Celico,78034,Cosenza,CS,Calabria,18,87053
+Cellara,78035,Cosenza,CS,Calabria,18,87050
+Cerchiara di Calabria,78036,Cosenza,CS,Calabria,18,87070
+Cerisano,78037,Cosenza,CS,Calabria,18,87044
+Cervicati,78038,Cosenza,CS,Calabria,18,87010
+Cerzeto,78039,Cosenza,CS,Calabria,18,87040
+Cetraro,78040,Cosenza,CS,Calabria,18,87022
+Civita,78041,Cosenza,CS,Calabria,18,87010
+Cleto,78042,Cosenza,CS,Calabria,18,87030
+Colosimi,78043,Cosenza,CS,Calabria,18,87050
+Cosenza,78045,Cosenza,CS,Calabria,18,87100
+Cropalati,78046,Cosenza,CS,Calabria,18,87060
+Crosia,78047,Cosenza,CS,Calabria,18,87060
+Diamante,78048,Cosenza,CS,Calabria,18,87023
+Dipignano,78049,Cosenza,CS,Calabria,18,87045
+Domanico,78050,Cosenza,CS,Calabria,18,87030
+Fagnano Castello,78051,Cosenza,CS,Calabria,18,87013
+Falconara Albanese,78052,Cosenza,CS,Calabria,18,87030
+Figline Vegliaturo,78053,Cosenza,CS,Calabria,18,87050
+Firmo,78054,Cosenza,CS,Calabria,18,87010
+Fiumefreddo Bruzio,78055,Cosenza,CS,Calabria,18,87030
+Francavilla Marittima,78056,Cosenza,CS,Calabria,18,87072
+Frascineto,78057,Cosenza,CS,Calabria,18,87010
+Fuscaldo,78058,Cosenza,CS,Calabria,18,87024
+Grimaldi,78059,Cosenza,CS,Calabria,18,87034
+Grisolia,78060,Cosenza,CS,Calabria,18,87020
+Guardia Piemontese,78061,Cosenza,CS,Calabria,18,87020
+Lago,78062,Cosenza,CS,Calabria,18,87035
+Laino Borgo,78063,Cosenza,CS,Calabria,18,87014
+Laino Castello,78064,Cosenza,CS,Calabria,18,87015
+Lappano,78065,Cosenza,CS,Calabria,18,87050
+Lattarico,78066,Cosenza,CS,Calabria,18,87010
+Longobardi,78067,Cosenza,CS,Calabria,18,87030
+Longobucco,78068,Cosenza,CS,Calabria,18,87066
+Lungro,78069,Cosenza,CS,Calabria,18,87010
+Luzzi,78070,Cosenza,CS,Calabria,18,87040
+Maierà,78071,Cosenza,CS,Calabria,18,87020
+Malito,78072,Cosenza,CS,Calabria,18,87030
+Malvito,78073,Cosenza,CS,Calabria,18,87010
+Mandatoriccio,78074,Cosenza,CS,Calabria,18,87060
+Mangone,78075,Cosenza,CS,Calabria,18,87050
+Marano Marchesato,78076,Cosenza,CS,Calabria,18,87040
+Marano Principato,78077,Cosenza,CS,Calabria,18,87040
+Marzi,78078,Cosenza,CS,Calabria,18,87050
+Mendicino,78079,Cosenza,CS,Calabria,18,87040
+Mongrassano,78080,Cosenza,CS,Calabria,18,87040
+Montalto Uffugo,78081,Cosenza,CS,Calabria,18,87046
+Montegiordano,78082,Cosenza,CS,Calabria,18,87070
+Morano Calabro,78083,Cosenza,CS,Calabria,18,87016
+Mormanno,78084,Cosenza,CS,Calabria,18,87026
+Mottafollone,78085,Cosenza,CS,Calabria,18,87010
+Nocara,78086,Cosenza,CS,Calabria,18,87070
+Oriolo,78087,Cosenza,CS,Calabria,18,87073
+Orsomarso,78088,Cosenza,CS,Calabria,18,87020
+Paludi,78089,Cosenza,CS,Calabria,18,87060
+Panettieri,78090,Cosenza,CS,Calabria,18,87050
+Paola,78091,Cosenza,CS,Calabria,18,87027
+Papasidero,78092,Cosenza,CS,Calabria,18,87020
+Parenti,78093,Cosenza,CS,Calabria,18,87040
+Paterno Calabro,78094,Cosenza,CS,Calabria,18,87040
+Pedivigliano,78096,Cosenza,CS,Calabria,18,87050
+Piane Crati,78097,Cosenza,CS,Calabria,18,87050
+Pietrafitta,78098,Cosenza,CS,Calabria,18,87050
+Pietrapaola,78099,Cosenza,CS,Calabria,18,87060
+Plataci,78100,Cosenza,CS,Calabria,18,87070
+Praia a Mare,78101,Cosenza,CS,Calabria,18,87028
+Rende,78102,Cosenza,CS,Calabria,18,87036
+Rocca Imperiale,78103,Cosenza,CS,Calabria,18,87074
+Roggiano Gravina,78104,Cosenza,CS,Calabria,18,87017
+Rogliano,78105,Cosenza,CS,Calabria,18,87054
+Rose,78106,Cosenza,CS,Calabria,18,87040
+Roseto Capo Spulico,78107,Cosenza,CS,Calabria,18,87070
+Rota Greca,78109,Cosenza,CS,Calabria,18,87010
+Rovito,78110,Cosenza,CS,Calabria,18,87050
+San Basile,78111,Cosenza,CS,Calabria,18,87010
+San Benedetto Ullano,78112,Cosenza,CS,Calabria,18,87040
+San Cosmo Albanese,78113,Cosenza,CS,Calabria,18,87060
+San Demetrio Corone,78114,Cosenza,CS,Calabria,18,87069
+San Donato di Ninea,78115,Cosenza,CS,Calabria,18,87010
+San Fili,78116,Cosenza,CS,Calabria,18,87037
+Sangineto,78117,Cosenza,CS,Calabria,18,87020
+San Giorgio Albanese,78118,Cosenza,CS,Calabria,18,87060
+San Giovanni in Fiore,78119,Cosenza,CS,Calabria,18,87055
+San Lorenzo Bellizzi,78120,Cosenza,CS,Calabria,18,87070
+San Lorenzo del Vallo,78121,Cosenza,CS,Calabria,18,87040
+San Lucido,78122,Cosenza,CS,Calabria,18,87038
+San Marco Argentano,78123,Cosenza,CS,Calabria,18,87018
+San Martino di Finita,78124,Cosenza,CS,Calabria,18,87010
+San Nicola Arcella,78125,Cosenza,CS,Calabria,18,87020
+San Pietro in Amantea,78126,Cosenza,CS,Calabria,18,87030
+San Pietro in Guarano,78127,Cosenza,CS,Calabria,18,87047
+San Sosti,78128,Cosenza,CS,Calabria,18,87010
+Santa Caterina Albanese,78129,Cosenza,CS,Calabria,18,87010
+Santa Domenica Talao,78130,Cosenza,CS,Calabria,18,87020
+Sant'Agata di Esaro,78131,Cosenza,CS,Calabria,18,87010
+Santa Maria del Cedro,78132,Cosenza,CS,Calabria,18,87020
+Santa Sofia d'Epiro,78133,Cosenza,CS,Calabria,18,87048
+Santo Stefano di Rogliano,78134,Cosenza,CS,Calabria,18,87056
+San Vincenzo La Costa,78135,Cosenza,CS,Calabria,18,87030
+Saracena,78136,Cosenza,CS,Calabria,18,87010
+Scala Coeli,78137,Cosenza,CS,Calabria,18,87060
+Scalea,78138,Cosenza,CS,Calabria,18,87029
+Scigliano,78139,Cosenza,CS,Calabria,18,87057
+Serra d'Aiello,78140,Cosenza,CS,Calabria,18,87030
+Spezzano Albanese,78142,Cosenza,CS,Calabria,18,87019
+Spezzano della Sila,78143,Cosenza,CS,Calabria,18,87058
+Tarsia,78145,Cosenza,CS,Calabria,18,87040
+Terranova da Sibari,78146,Cosenza,CS,Calabria,18,87010
+Terravecchia,78147,Cosenza,CS,Calabria,18,87060
+Torano Castello,78148,Cosenza,CS,Calabria,18,87010
+Tortora,78149,Cosenza,CS,Calabria,18,87020
+Trebisacce,78150,Cosenza,CS,Calabria,18,87075
+Vaccarizzo Albanese,78152,Cosenza,CS,Calabria,18,87060
+Verbicaro,78153,Cosenza,CS,Calabria,18,87020
+Villapiana,78154,Cosenza,CS,Calabria,18,87076
+Zumpano,78155,Cosenza,CS,Calabria,18,87040
+Casali del Manco,78156,Cosenza,CS,Calabria,18,87050
+Corigliano-Rossano,78157,Cosenza,CS,Calabria,18,87064
+Albi,79002,Catanzaro,CZ,Calabria,18,88055
+Amaroni,79003,Catanzaro,CZ,Calabria,18,88050
+Amato,79004,Catanzaro,CZ,Calabria,18,88040
+Andali,79005,Catanzaro,CZ,Calabria,18,88050
+Argusto,79007,Catanzaro,CZ,Calabria,18,88060
+Badolato,79008,Catanzaro,CZ,Calabria,18,88060
+Belcastro,79009,Catanzaro,CZ,Calabria,18,88050
+Borgia,79011,Catanzaro,CZ,Calabria,18,88021
+Botricello,79012,Catanzaro,CZ,Calabria,18,88070
+Caraffa di Catanzaro,79017,Catanzaro,CZ,Calabria,18,88050
+Cardinale,79018,Catanzaro,CZ,Calabria,18,88062
+Carlopoli,79020,Catanzaro,CZ,Calabria,18,88040
+Catanzaro,79023,Catanzaro,CZ,Calabria,18,88100
+Cenadi,79024,Catanzaro,CZ,Calabria,18,88067
+Centrache,79025,Catanzaro,CZ,Calabria,18,88067
+Cerva,79027,Catanzaro,CZ,Calabria,18,88050
+Chiaravalle Centrale,79029,Catanzaro,CZ,Calabria,18,88064
+Cicala,79030,Catanzaro,CZ,Calabria,18,88040
+Conflenti,79033,Catanzaro,CZ,Calabria,18,88040
+Cortale,79034,Catanzaro,CZ,Calabria,18,88020
+Cropani,79036,Catanzaro,CZ,Calabria,18,88051
+Curinga,79039,Catanzaro,CZ,Calabria,18,88022
+Davoli,79042,Catanzaro,CZ,Calabria,18,88060
+Decollatura,79043,Catanzaro,CZ,Calabria,18,88041
+Falerna,79047,Catanzaro,CZ,Calabria,18,88042
+Feroleto Antico,79048,Catanzaro,CZ,Calabria,18,88040
+Fossato Serralta,79052,Catanzaro,CZ,Calabria,18,88050
+Gagliato,79055,Catanzaro,CZ,Calabria,18,88060
+Gasperina,79056,Catanzaro,CZ,Calabria,18,88060
+Gimigliano,79058,Catanzaro,CZ,Calabria,18,88045
+Girifalco,79059,Catanzaro,CZ,Calabria,18,88024
+Gizzeria,79060,Catanzaro,CZ,Calabria,18,88040
+Guardavalle,79061,Catanzaro,CZ,Calabria,18,88065
+Isca sullo Ionio,79063,Catanzaro,CZ,Calabria,18,88060
+Jacurso,79065,Catanzaro,CZ,Calabria,18,88020
+Magisano,79068,Catanzaro,CZ,Calabria,18,88050
+Maida,79069,Catanzaro,CZ,Calabria,18,88025
+Marcedusa,79071,Catanzaro,CZ,Calabria,18,88050
+Marcellinara,79072,Catanzaro,CZ,Calabria,18,88044
+Martirano,79073,Catanzaro,CZ,Calabria,18,88040
+Martirano Lombardo,79074,Catanzaro,CZ,Calabria,18,88040
+Miglierina,79077,Catanzaro,CZ,Calabria,18,88040
+Montauro,79080,Catanzaro,CZ,Calabria,18,88060
+Montepaone,79081,Catanzaro,CZ,Calabria,18,88060
+Motta Santa Lucia,79083,Catanzaro,CZ,Calabria,18,88040
+Nocera Terinese,79087,Catanzaro,CZ,Calabria,18,88047
+Olivadi,79088,Catanzaro,CZ,Calabria,18,88067
+Palermiti,79089,Catanzaro,CZ,Calabria,18,88050
+Pentone,79092,Catanzaro,CZ,Calabria,18,88050
+Petrizzi,79094,Catanzaro,CZ,Calabria,18,88060
+Petronà,79095,Catanzaro,CZ,Calabria,18,88050
+Pianopoli,79096,Catanzaro,CZ,Calabria,18,88040
+Platania,79099,Catanzaro,CZ,Calabria,18,88040
+San Floro,79108,Catanzaro,CZ,Calabria,18,88021
+San Mango d'Aquino,79110,Catanzaro,CZ,Calabria,18,88040
+San Pietro a Maida,79114,Catanzaro,CZ,Calabria,18,88025
+San Pietro Apostolo,79115,Catanzaro,CZ,Calabria,18,88040
+San Sostene,79116,Catanzaro,CZ,Calabria,18,88060
+Santa Caterina dello Ionio,79117,Catanzaro,CZ,Calabria,18,88060
+Sant'Andrea Apostolo dello Ionio,79118,Catanzaro,CZ,Calabria,18,88060
+San Vito sullo Ionio,79122,Catanzaro,CZ,Calabria,18,88067
+Satriano,79123,Catanzaro,CZ,Calabria,18,88060
+Sellia,79126,Catanzaro,CZ,Calabria,18,88050
+Sellia Marina,79127,Catanzaro,CZ,Calabria,18,88050
+Serrastretta,79129,Catanzaro,CZ,Calabria,18,88040
+Sersale,79130,Catanzaro,CZ,Calabria,18,88054
+Settingiano,79131,Catanzaro,CZ,Calabria,18,88040
+Simeri Crichi,79133,Catanzaro,CZ,Calabria,18,88050
+Sorbo San Basile,79134,Catanzaro,CZ,Calabria,18,88050
+Soverato,79137,Catanzaro,CZ,Calabria,18,88068
+Soveria Mannelli,79138,Catanzaro,CZ,Calabria,18,88049
+Soveria Simeri,79139,Catanzaro,CZ,Calabria,18,88050
+Squillace,79142,Catanzaro,CZ,Calabria,18,88069
+Stalettì,79143,Catanzaro,CZ,Calabria,18,88069
+Taverna,79146,Catanzaro,CZ,Calabria,18,88055
+Tiriolo,79147,Catanzaro,CZ,Calabria,18,88056
+Torre di Ruggiero,79148,Catanzaro,CZ,Calabria,18,88060
+Vallefiorita,79151,Catanzaro,CZ,Calabria,18,88050
+Zagarise,79157,Catanzaro,CZ,Calabria,18,88050
+Lamezia Terme,79160,Catanzaro,CZ,Calabria,18,88046
+Africo,80001,Reggio Calabria,RC,Calabria,18,89030
+Agnana Calabra,80002,Reggio Calabria,RC,Calabria,18,89040
+Anoia,80003,Reggio Calabria,RC,Calabria,18,89020
+Antonimina,80004,Reggio Calabria,RC,Calabria,18,89040
+Ardore,80005,Reggio Calabria,RC,Calabria,18,89031
+Bagaladi,80006,Reggio Calabria,RC,Calabria,18,89060
+Bagnara Calabra,80007,Reggio Calabria,RC,Calabria,18,89011
+Benestare,80008,Reggio Calabria,RC,Calabria,18,89030
+Bianco,80009,Reggio Calabria,RC,Calabria,18,89032
+Bivongi,80010,Reggio Calabria,RC,Calabria,18,89040
+Bova,80011,Reggio Calabria,RC,Calabria,18,89033
+Bovalino,80012,Reggio Calabria,RC,Calabria,18,89034
+Bova Marina,80013,Reggio Calabria,RC,Calabria,18,89035
+Brancaleone,80014,Reggio Calabria,RC,Calabria,18,89036
+Bruzzano Zeffirio,80015,Reggio Calabria,RC,Calabria,18,89030
+Calanna,80016,Reggio Calabria,RC,Calabria,18,89050
+Camini,80017,Reggio Calabria,RC,Calabria,18,89040
+Campo Calabro,80018,Reggio Calabria,RC,Calabria,18,89052
+Candidoni,80019,Reggio Calabria,RC,Calabria,18,89020
+Canolo,80020,Reggio Calabria,RC,Calabria,18,89040
+Caraffa del Bianco,80021,Reggio Calabria,RC,Calabria,18,89030
+Cardeto,80022,Reggio Calabria,RC,Calabria,18,89060
+Careri,80023,Reggio Calabria,RC,Calabria,18,89030
+Casignana,80024,Reggio Calabria,RC,Calabria,18,89030
+Caulonia,80025,Reggio Calabria,RC,Calabria,18,89041
+Ciminà,80026,Reggio Calabria,RC,Calabria,18,89040
+Cinquefrondi,80027,Reggio Calabria,RC,Calabria,18,89021
+Cittanova,80028,Reggio Calabria,RC,Calabria,18,89022
+Condofuri,80029,Reggio Calabria,RC,Calabria,18,89030
+Cosoleto,80030,Reggio Calabria,RC,Calabria,18,89050
+Delianuova,80031,Reggio Calabria,RC,Calabria,18,89012
+Feroleto della Chiesa,80032,Reggio Calabria,RC,Calabria,18,89050
+Ferruzzano,80033,Reggio Calabria,RC,Calabria,18,89030
+Fiumara,80034,Reggio Calabria,RC,Calabria,18,89050
+Galatro,80035,Reggio Calabria,RC,Calabria,18,89054
+Gerace,80036,Reggio Calabria,RC,Calabria,18,89040
+Giffone,80037,Reggio Calabria,RC,Calabria,18,89020
+Gioia Tauro,80038,Reggio Calabria,RC,Calabria,18,89013
+Gioiosa Ionica,80039,Reggio Calabria,RC,Calabria,18,89042
+Grotteria,80040,Reggio Calabria,RC,Calabria,18,89043
+Laganadi,80041,Reggio Calabria,RC,Calabria,18,89050
+Laureana di Borrello,80042,Reggio Calabria,RC,Calabria,18,89023
+Locri,80043,Reggio Calabria,RC,Calabria,18,89044
+Mammola,80044,Reggio Calabria,RC,Calabria,18,89045
+Marina di Gioiosa Ionica,80045,Reggio Calabria,RC,Calabria,18,89046
+Maropati,80046,Reggio Calabria,RC,Calabria,18,89020
+Martone,80047,Reggio Calabria,RC,Calabria,18,89040
+Melicuccà,80048,Reggio Calabria,RC,Calabria,18,89020
+Melicucco,80049,Reggio Calabria,RC,Calabria,18,89020
+Melito di Porto Salvo,80050,Reggio Calabria,RC,Calabria,18,89063
+Molochio,80051,Reggio Calabria,RC,Calabria,18,89010
+Monasterace,80052,Reggio Calabria,RC,Calabria,18,89040
+Montebello Jonico,80053,Reggio Calabria,RC,Calabria,18,89064
+Motta San Giovanni,80054,Reggio Calabria,RC,Calabria,18,89065
+Oppido Mamertina,80055,Reggio Calabria,RC,Calabria,18,89014
+Palizzi,80056,Reggio Calabria,RC,Calabria,18,89038
+Palmi,80057,Reggio Calabria,RC,Calabria,18,89015
+Pazzano,80058,Reggio Calabria,RC,Calabria,18,89040
+Placanica,80059,Reggio Calabria,RC,Calabria,18,89040
+Platì,80060,Reggio Calabria,RC,Calabria,18,89039
+Polistena,80061,Reggio Calabria,RC,Calabria,18,89024
+Portigliola,80062,Reggio Calabria,RC,Calabria,18,89040
+Reggio di Calabria,80063,Reggio Calabria,RC,Calabria,18,89124
+Riace,80064,Reggio Calabria,RC,Calabria,18,89040
+Rizziconi,80065,Reggio Calabria,RC,Calabria,18,89016
+Roccaforte del Greco,80066,Reggio Calabria,RC,Calabria,18,89060
+Roccella Ionica,80067,Reggio Calabria,RC,Calabria,18,89047
+Roghudi,80068,Reggio Calabria,RC,Calabria,18,89060
+Rosarno,80069,Reggio Calabria,RC,Calabria,18,89025
+Samo,80070,Reggio Calabria,RC,Calabria,18,89030
+San Giorgio Morgeto,80071,Reggio Calabria,RC,Calabria,18,89017
+San Giovanni di Gerace,80072,Reggio Calabria,RC,Calabria,18,89040
+San Lorenzo,80073,Reggio Calabria,RC,Calabria,18,89069
+San Luca,80074,Reggio Calabria,RC,Calabria,18,89030
+San Pietro di Caridà,80075,Reggio Calabria,RC,Calabria,18,89020
+San Procopio,80076,Reggio Calabria,RC,Calabria,18,89020
+San Roberto,80077,Reggio Calabria,RC,Calabria,18,89050
+Santa Cristina d'Aspromonte,80078,Reggio Calabria,RC,Calabria,18,89056
+Sant'Agata del Bianco,80079,Reggio Calabria,RC,Calabria,18,89030
+Sant'Alessio in Aspromonte,80080,Reggio Calabria,RC,Calabria,18,89050
+Sant'Eufemia d'Aspromonte,80081,Reggio Calabria,RC,Calabria,18,89027
+Sant'Ilario dello Ionio,80082,Reggio Calabria,RC,Calabria,18,89040
+Santo Stefano in Aspromonte,80083,Reggio Calabria,RC,Calabria,18,89057
+Scido,80084,Reggio Calabria,RC,Calabria,18,89010
+Scilla,80085,Reggio Calabria,RC,Calabria,18,89058
+Seminara,80086,Reggio Calabria,RC,Calabria,18,89028
+Serrata,80087,Reggio Calabria,RC,Calabria,18,89020
+Siderno,80088,Reggio Calabria,RC,Calabria,18,89048
+Sinopoli,80089,Reggio Calabria,RC,Calabria,18,89020
+Staiti,80090,Reggio Calabria,RC,Calabria,18,89030
+Stignano,80091,Reggio Calabria,RC,Calabria,18,89040
+Stilo,80092,Reggio Calabria,RC,Calabria,18,89049
+Taurianova,80093,Reggio Calabria,RC,Calabria,18,89029
+Terranova Sappo Minulio,80094,Reggio Calabria,RC,Calabria,18,89010
+Varapodio,80095,Reggio Calabria,RC,Calabria,18,89010
+Villa San Giovanni,80096,Reggio Calabria,RC,Calabria,18,89018
+San Ferdinando,80097,Reggio Calabria,RC,Calabria,18,89026
+Belvedere di Spinello,101001,Crotone,KR,Calabria,18,88824
+Caccuri,101002,Crotone,KR,Calabria,18,88833
+Carfizzi,101003,Crotone,KR,Calabria,18,88817
+Casabona,101004,Crotone,KR,Calabria,18,88822
+Castelsilano,101005,Crotone,KR,Calabria,18,88834
+Cerenzia,101006,Crotone,KR,Calabria,18,88833
+Cirò,101007,Crotone,KR,Calabria,18,88813
+Cirò Marina,101008,Crotone,KR,Calabria,18,88811
+Cotronei,101009,Crotone,KR,Calabria,18,88836
+Crotone,101010,Crotone,KR,Calabria,18,88900
+Crucoli,101011,Crotone,KR,Calabria,18,88812
+Cutro,101012,Crotone,KR,Calabria,18,88842
+Isola di Capo Rizzuto,101013,Crotone,KR,Calabria,18,88841
+Melissa,101014,Crotone,KR,Calabria,18,88814
+Mesoraca,101015,Crotone,KR,Calabria,18,88838
+Pallagorio,101016,Crotone,KR,Calabria,18,88818
+Petilia Policastro,101017,Crotone,KR,Calabria,18,88837
+Roccabernarda,101018,Crotone,KR,Calabria,18,88835
+Rocca di Neto,101019,Crotone,KR,Calabria,18,88821
+San Mauro Marchesato,101020,Crotone,KR,Calabria,18,88831
+San Nicola dell'Alto,101021,Crotone,KR,Calabria,18,88817
+Santa Severina,101022,Crotone,KR,Calabria,18,88832
+Savelli,101023,Crotone,KR,Calabria,18,88825
+Scandale,101024,Crotone,KR,Calabria,18,88831
+Strongoli,101025,Crotone,KR,Calabria,18,88816
+Umbriatico,101026,Crotone,KR,Calabria,18,88823
+Verzino,101027,Crotone,KR,Calabria,18,88819
+Acquaro,102001,Vibo Valentia,VV,Calabria,18,89832
+Arena,102002,Vibo Valentia,VV,Calabria,18,89832
+Briatico,102003,Vibo Valentia,VV,Calabria,18,89817
+Brognaturo,102004,Vibo Valentia,VV,Calabria,18,89822
+Capistrano,102005,Vibo Valentia,VV,Calabria,18,89818
+Cessaniti,102006,Vibo Valentia,VV,Calabria,18,89816
+Dasà,102007,Vibo Valentia,VV,Calabria,18,89832
+Dinami,102008,Vibo Valentia,VV,Calabria,18,89833
+Drapia,102009,Vibo Valentia,VV,Calabria,18,89862
+Fabrizia,102010,Vibo Valentia,VV,Calabria,18,89823
+Filadelfia,102011,Vibo Valentia,VV,Calabria,18,89814
+Filandari,102012,Vibo Valentia,VV,Calabria,18,89841
+Filogaso,102013,Vibo Valentia,VV,Calabria,18,89843
+Francavilla Angitola,102014,Vibo Valentia,VV,Calabria,18,89815
+Francica,102015,Vibo Valentia,VV,Calabria,18,89851
+Gerocarne,102016,Vibo Valentia,VV,Calabria,18,89831
+Ionadi,102017,Vibo Valentia,VV,Calabria,18,89851
+Joppolo,102018,Vibo Valentia,VV,Calabria,18,89863
+Limbadi,102019,Vibo Valentia,VV,Calabria,18,89844
+Maierato,102020,Vibo Valentia,VV,Calabria,18,89843
+Mileto,102021,Vibo Valentia,VV,Calabria,18,89852
+Mongiana,102022,Vibo Valentia,VV,Calabria,18,89823
+Monterosso Calabro,102023,Vibo Valentia,VV,Calabria,18,89819
+Nardodipace,102024,Vibo Valentia,VV,Calabria,18,89824
+Nicotera,102025,Vibo Valentia,VV,Calabria,18,89844
+Parghelia,102026,Vibo Valentia,VV,Calabria,18,89861
+Pizzo,102027,Vibo Valentia,VV,Calabria,18,89812
+Pizzoni,102028,Vibo Valentia,VV,Calabria,18,89834
+Polia,102029,Vibo Valentia,VV,Calabria,18,89813
+Ricadi,102030,Vibo Valentia,VV,Calabria,18,89866
+Rombiolo,102031,Vibo Valentia,VV,Calabria,18,89841
+San Calogero,102032,Vibo Valentia,VV,Calabria,18,89842
+San Costantino Calabro,102033,Vibo Valentia,VV,Calabria,18,89851
+San Gregorio d'Ippona,102034,Vibo Valentia,VV,Calabria,18,89853
+San Nicola da Crissa,102035,Vibo Valentia,VV,Calabria,18,89821
+Sant'Onofrio,102036,Vibo Valentia,VV,Calabria,18,89843
+Serra San Bruno,102037,Vibo Valentia,VV,Calabria,18,89822
+Simbario,102038,Vibo Valentia,VV,Calabria,18,89822
+Sorianello,102039,Vibo Valentia,VV,Calabria,18,89831
+Soriano Calabro,102040,Vibo Valentia,VV,Calabria,18,89831
+Spadola,102041,Vibo Valentia,VV,Calabria,18,89822
+Spilinga,102042,Vibo Valentia,VV,Calabria,18,89864
+Stefanaconi,102043,Vibo Valentia,VV,Calabria,18,89843
+Tropea,102044,Vibo Valentia,VV,Calabria,18,89861
+Vallelonga,102045,Vibo Valentia,VV,Calabria,18,89821
+Vazzano,102046,Vibo Valentia,VV,Calabria,18,89834
+Vibo Valentia,102047,Vibo Valentia,VV,Calabria,18,89900
+Zaccanopoli,102048,Vibo Valentia,VV,Calabria,18,89867
+Zambrone,102049,Vibo Valentia,VV,Calabria,18,89868
+Zungri,102050,Vibo Valentia,VV,Calabria,18,89867
+Alcamo,81001,Trapani,TP,Sicilia,19,91011
+Buseto Palizzolo,81002,Trapani,TP,Sicilia,19,91012
+Calatafimi-Segesta,81003,Trapani,TP,Sicilia,19,91013
+Campobello di Mazara,81004,Trapani,TP,Sicilia,19,91021
+Castellammare del Golfo,81005,Trapani,TP,Sicilia,19,91014
+Castelvetrano,81006,Trapani,TP,Sicilia,19,91022
+Custonaci,81007,Trapani,TP,Sicilia,19,91015
+Erice,81008,Trapani,TP,Sicilia,19,91016
+Favignana,81009,Trapani,TP,Sicilia,19,91023
+Gibellina,81010,Trapani,TP,Sicilia,19,91024
+Marsala,81011,Trapani,TP,Sicilia,19,91025
+Mazara del Vallo,81012,Trapani,TP,Sicilia,19,91026
+Paceco,81013,Trapani,TP,Sicilia,19,91027
+Pantelleria,81014,Trapani,TP,Sicilia,19,91017
+Partanna,81015,Trapani,TP,Sicilia,19,91028
+Poggioreale,81016,Trapani,TP,Sicilia,19,91020
+Salaparuta,81017,Trapani,TP,Sicilia,19,91020
+Salemi,81018,Trapani,TP,Sicilia,19,91018
+Santa Ninfa,81019,Trapani,TP,Sicilia,19,91029
+San Vito Lo Capo,81020,Trapani,TP,Sicilia,19,91010
+Trapani,81021,Trapani,TP,Sicilia,19,91100
+Valderice,81022,Trapani,TP,Sicilia,19,91019
+Vita,81023,Trapani,TP,Sicilia,19,91010
+Petrosino,81024,Trapani,TP,Sicilia,19,91020
+Misiliscemi,81025,Trapani,TP,Sicilia,19,91031
+Alia,82001,Palermo,PA,Sicilia,19,90021
+Alimena,82002,Palermo,PA,Sicilia,19,90020
+Aliminusa,82003,Palermo,PA,Sicilia,19,90020
+Altavilla Milicia,82004,Palermo,PA,Sicilia,19,90010
+Altofonte,82005,Palermo,PA,Sicilia,19,90030
+Bagheria,82006,Palermo,PA,Sicilia,19,90011
+Balestrate,82007,Palermo,PA,Sicilia,19,90041
+Baucina,82008,Palermo,PA,Sicilia,19,90020
+Belmonte Mezzagno,82009,Palermo,PA,Sicilia,19,90031
+Bisacquino,82010,Palermo,PA,Sicilia,19,90032
+Bolognetta,82011,Palermo,PA,Sicilia,19,90030
+Bompietro,82012,Palermo,PA,Sicilia,19,90020
+Borgetto,82013,Palermo,PA,Sicilia,19,90042
+Caccamo,82014,Palermo,PA,Sicilia,19,90012
+Caltavuturo,82015,Palermo,PA,Sicilia,19,90022
+Campofelice di Fitalia,82016,Palermo,PA,Sicilia,19,90030
+Campofelice di Roccella,82017,Palermo,PA,Sicilia,19,90010
+Campofiorito,82018,Palermo,PA,Sicilia,19,90030
+Camporeale,82019,Palermo,PA,Sicilia,19,90043
+Capaci,82020,Palermo,PA,Sicilia,19,90040
+Carini,82021,Palermo,PA,Sicilia,19,90044
+Castelbuono,82022,Palermo,PA,Sicilia,19,90013
+Casteldaccia,82023,Palermo,PA,Sicilia,19,90014
+Castellana Sicula,82024,Palermo,PA,Sicilia,19,90020
+Castronovo di Sicilia,82025,Palermo,PA,Sicilia,19,90030
+Cefalà Diana,82026,Palermo,PA,Sicilia,19,90030
+Cefalù,82027,Palermo,PA,Sicilia,19,90015
+Cerda,82028,Palermo,PA,Sicilia,19,90010
+Chiusa Sclafani,82029,Palermo,PA,Sicilia,19,90033
+Ciminna,82030,Palermo,PA,Sicilia,19,90023
+Cinisi,82031,Palermo,PA,Sicilia,19,90045
+Collesano,82032,Palermo,PA,Sicilia,19,90016
+Contessa Entellina,82033,Palermo,PA,Sicilia,19,90030
+Corleone,82034,Palermo,PA,Sicilia,19,90034
+Ficarazzi,82035,Palermo,PA,Sicilia,19,90010
+Gangi,82036,Palermo,PA,Sicilia,19,90024
+Geraci Siculo,82037,Palermo,PA,Sicilia,19,90010
+Giardinello,82038,Palermo,PA,Sicilia,19,90040
+Giuliana,82039,Palermo,PA,Sicilia,19,90030
+Godrano,82040,Palermo,PA,Sicilia,19,90030
+Gratteri,82041,Palermo,PA,Sicilia,19,90010
+Isnello,82042,Palermo,PA,Sicilia,19,90010
+Isola delle Femmine,82043,Palermo,PA,Sicilia,19,90040
+Lascari,82044,Palermo,PA,Sicilia,19,90010
+Lercara Friddi,82045,Palermo,PA,Sicilia,19,90025
+Marineo,82046,Palermo,PA,Sicilia,19,90035
+Mezzojuso,82047,Palermo,PA,Sicilia,19,90030
+Misilmeri,82048,Palermo,PA,Sicilia,19,90036
+Monreale,82049,Palermo,PA,Sicilia,19,90046
+Montelepre,82050,Palermo,PA,Sicilia,19,90040
+Montemaggiore Belsito,82051,Palermo,PA,Sicilia,19,90020
+Palazzo Adriano,82052,Palermo,PA,Sicilia,19,90030
+Palermo,82053,Palermo,PA,Sicilia,19,90133
+Partinico,82054,Palermo,PA,Sicilia,19,90047
+Petralia Soprana,82055,Palermo,PA,Sicilia,19,90026
+Petralia Sottana,82056,Palermo,PA,Sicilia,19,90027
+Piana degli Albanesi,82057,Palermo,PA,Sicilia,19,90037
+Polizzi Generosa,82058,Palermo,PA,Sicilia,19,90028
+Pollina,82059,Palermo,PA,Sicilia,19,90010
+Prizzi,82060,Palermo,PA,Sicilia,19,90038
+Roccamena,82061,Palermo,PA,Sicilia,19,90040
+Roccapalumba,82062,Palermo,PA,Sicilia,19,90020
+San Cipirello,82063,Palermo,PA,Sicilia,19,90040
+San Giuseppe Jato,82064,Palermo,PA,Sicilia,19,90048
+San Mauro Castelverde,82065,Palermo,PA,Sicilia,19,90010
+Santa Cristina Gela,82066,Palermo,PA,Sicilia,19,90030
+Santa Flavia,82067,Palermo,PA,Sicilia,19,90017
+Sciara,82068,Palermo,PA,Sicilia,19,90020
+Sclafani Bagni,82069,Palermo,PA,Sicilia,19,90020
+Termini Imerese,82070,Palermo,PA,Sicilia,19,90018
+Terrasini,82071,Palermo,PA,Sicilia,19,90049
+Torretta,82072,Palermo,PA,Sicilia,19,90040
+Trabia,82073,Palermo,PA,Sicilia,19,90019
+Trappeto,82074,Palermo,PA,Sicilia,19,90040
+Ustica,82075,Palermo,PA,Sicilia,19,90010
+Valledolmo,82076,Palermo,PA,Sicilia,19,90029
+Ventimiglia di Sicilia,82077,Palermo,PA,Sicilia,19,90020
+Vicari,82078,Palermo,PA,Sicilia,19,90020
+Villabate,82079,Palermo,PA,Sicilia,19,90039
+Villafrati,82080,Palermo,PA,Sicilia,19,90030
+Scillato,82081,Palermo,PA,Sicilia,19,90020
+Blufi,82082,Palermo,PA,Sicilia,19,90020
+Alcara li Fusi,83001,Messina,ME,Sicilia,19,98070
+Alì,83002,Messina,ME,Sicilia,19,98020
+Alì Terme,83003,Messina,ME,Sicilia,19,98021
+Antillo,83004,Messina,ME,Sicilia,19,98030
+Barcellona Pozzo di Gotto,83005,Messina,ME,Sicilia,19,98051
+Basicò,83006,Messina,ME,Sicilia,19,98060
+Brolo,83007,Messina,ME,Sicilia,19,98061
+Capizzi,83008,Messina,ME,Sicilia,19,98031
+Capo d'Orlando,83009,Messina,ME,Sicilia,19,98071
+Capri Leone,83010,Messina,ME,Sicilia,19,98070
+Caronia,83011,Messina,ME,Sicilia,19,98072
+Casalvecchio Siculo,83012,Messina,ME,Sicilia,19,98032
+Castel di Lucio,83013,Messina,ME,Sicilia,19,98070
+Castell'Umberto,83014,Messina,ME,Sicilia,19,98070
+Castelmola,83015,Messina,ME,Sicilia,19,98030
+Castroreale,83016,Messina,ME,Sicilia,19,98053
+Cesarò,83017,Messina,ME,Sicilia,19,98033
+Condrò,83018,Messina,ME,Sicilia,19,98040
+Falcone,83019,Messina,ME,Sicilia,19,98060
+Ficarra,83020,Messina,ME,Sicilia,19,98062
+Fiumedinisi,83021,Messina,ME,Sicilia,19,98022
+Floresta,83022,Messina,ME,Sicilia,19,98030
+Fondachelli-Fantina,83023,Messina,ME,Sicilia,19,98050
+Forza d'Agrò,83024,Messina,ME,Sicilia,19,98030
+Francavilla di Sicilia,83025,Messina,ME,Sicilia,19,98034
+Frazzanò,83026,Messina,ME,Sicilia,19,98070
+Furci Siculo,83027,Messina,ME,Sicilia,19,98023
+Furnari,83028,Messina,ME,Sicilia,19,98054
+Gaggi,83029,Messina,ME,Sicilia,19,98030
+Galati Mamertino,83030,Messina,ME,Sicilia,19,98070
+Gallodoro,83031,Messina,ME,Sicilia,19,98030
+Giardini-Naxos,83032,Messina,ME,Sicilia,19,98035
+Gioiosa Marea,83033,Messina,ME,Sicilia,19,98063
+Graniti,83034,Messina,ME,Sicilia,19,98036
+Gualtieri Sicaminò,83035,Messina,ME,Sicilia,19,98040
+Itala,83036,Messina,ME,Sicilia,19,98025
+Leni,83037,Messina,ME,Sicilia,19,98050
+Letojanni,83038,Messina,ME,Sicilia,19,98037
+Librizzi,83039,Messina,ME,Sicilia,19,98064
+Limina,83040,Messina,ME,Sicilia,19,98030
+Lipari,83041,Messina,ME,Sicilia,19,98055
+Longi,83042,Messina,ME,Sicilia,19,98070
+Malfa,83043,Messina,ME,Sicilia,19,98050
+Malvagna,83044,Messina,ME,Sicilia,19,98030
+Mandanici,83045,Messina,ME,Sicilia,19,98020
+Mazzarrà Sant'Andrea,83046,Messina,ME,Sicilia,19,98056
+Merì,83047,Messina,ME,Sicilia,19,98040
+Messina,83048,Messina,ME,Sicilia,19,98122
+Milazzo,83049,Messina,ME,Sicilia,19,98057
+Militello Rosmarino,83050,Messina,ME,Sicilia,19,98070
+Mirto,83051,Messina,ME,Sicilia,19,98070
+Mistretta,83052,Messina,ME,Sicilia,19,98073
+Moio Alcantara,83053,Messina,ME,Sicilia,19,98030
+Monforte San Giorgio,83054,Messina,ME,Sicilia,19,98041
+Mongiuffi Melia,83055,Messina,ME,Sicilia,19,98030
+Montagnareale,83056,Messina,ME,Sicilia,19,98060
+Montalbano Elicona,83057,Messina,ME,Sicilia,19,98065
+Motta Camastra,83058,Messina,ME,Sicilia,19,98030
+Motta d'Affermo,83059,Messina,ME,Sicilia,19,98070
+Naso,83060,Messina,ME,Sicilia,19,98074
+Nizza di Sicilia,83061,Messina,ME,Sicilia,19,98026
+Novara di Sicilia,83062,Messina,ME,Sicilia,19,98058
+Oliveri,83063,Messina,ME,Sicilia,19,98060
+Pace del Mela,83064,Messina,ME,Sicilia,19,98042
+Pagliara,83065,Messina,ME,Sicilia,19,98020
+Patti,83066,Messina,ME,Sicilia,19,98066
+Pettineo,83067,Messina,ME,Sicilia,19,98070
+Piraino,83068,Messina,ME,Sicilia,19,98060
+Raccuja,83069,Messina,ME,Sicilia,19,98067
+Reitano,83070,Messina,ME,Sicilia,19,98070
+Roccafiorita,83071,Messina,ME,Sicilia,19,98030
+Roccalumera,83072,Messina,ME,Sicilia,19,98027
+Roccavaldina,83073,Messina,ME,Sicilia,19,98040
+Roccella Valdemone,83074,Messina,ME,Sicilia,19,98030
+Rodì Milici,83075,Messina,ME,Sicilia,19,98059
+Rometta,83076,Messina,ME,Sicilia,19,98043
+San Filippo del Mela,83077,Messina,ME,Sicilia,19,98044
+San Fratello,83078,Messina,ME,Sicilia,19,98075
+San Marco d'Alunzio,83079,Messina,ME,Sicilia,19,98070
+San Pier Niceto,83080,Messina,ME,Sicilia,19,98045
+San Piero Patti,83081,Messina,ME,Sicilia,19,98068
+San Salvatore di Fitalia,83082,Messina,ME,Sicilia,19,98070
+Santa Domenica Vittoria,83083,Messina,ME,Sicilia,19,98030
+Sant'Agata di Militello,83084,Messina,ME,Sicilia,19,98076
+Sant'Alessio Siculo,83085,Messina,ME,Sicilia,19,98030
+Santa Lucia del Mela,83086,Messina,ME,Sicilia,19,98046
+Santa Marina Salina,83087,Messina,ME,Sicilia,19,98050
+Sant'Angelo di Brolo,83088,Messina,ME,Sicilia,19,98060
+Santa Teresa di Riva,83089,Messina,ME,Sicilia,19,98028
+San Teodoro,83090,Messina,ME,Sicilia,19,98030
+Santo Stefano di Camastra,83091,Messina,ME,Sicilia,19,98077
+Saponara,83092,Messina,ME,Sicilia,19,98047
+Savoca,83093,Messina,ME,Sicilia,19,98038
+Scaletta Zanclea,83094,Messina,ME,Sicilia,19,98029
+Sinagra,83095,Messina,ME,Sicilia,19,98069
+Spadafora,83096,Messina,ME,Sicilia,19,98048
+Taormina,83097,Messina,ME,Sicilia,19,98039
+Torregrotta,83098,Messina,ME,Sicilia,19,98040
+Tortorici,83099,Messina,ME,Sicilia,19,98078
+Tripi,83100,Messina,ME,Sicilia,19,98060
+Tusa,83101,Messina,ME,Sicilia,19,98079
+Ucria,83102,Messina,ME,Sicilia,19,98060
+Valdina,83103,Messina,ME,Sicilia,19,98040
+Venetico,83104,Messina,ME,Sicilia,19,98040
+Villafranca Tirrena,83105,Messina,ME,Sicilia,19,98049
+Terme Vigliatore,83106,Messina,ME,Sicilia,19,98050
+Acquedolci,83107,Messina,ME,Sicilia,19,98070
+Torrenova,83108,Messina,ME,Sicilia,19,98070
+Agrigento,84001,Agrigento,AG,Sicilia,19,92100
+Alessandria della Rocca,84002,Agrigento,AG,Sicilia,19,92010
+Aragona,84003,Agrigento,AG,Sicilia,19,92021
+Bivona,84004,Agrigento,AG,Sicilia,19,92010
+Burgio,84005,Agrigento,AG,Sicilia,19,92010
+Calamonaci,84006,Agrigento,AG,Sicilia,19,92010
+Caltabellotta,84007,Agrigento,AG,Sicilia,19,92010
+Camastra,84008,Agrigento,AG,Sicilia,19,92020
+Cammarata,84009,Agrigento,AG,Sicilia,19,92022
+Campobello di Licata,84010,Agrigento,AG,Sicilia,19,92023
+Canicattì,84011,Agrigento,AG,Sicilia,19,92024
+Casteltermini,84012,Agrigento,AG,Sicilia,19,92025
+Castrofilippo,84013,Agrigento,AG,Sicilia,19,92020
+Cattolica Eraclea,84014,Agrigento,AG,Sicilia,19,92011
+Cianciana,84015,Agrigento,AG,Sicilia,19,92012
+Comitini,84016,Agrigento,AG,Sicilia,19,92020
+Favara,84017,Agrigento,AG,Sicilia,19,92026
+Grotte,84018,Agrigento,AG,Sicilia,19,92020
+Joppolo Giancaxio,84019,Agrigento,AG,Sicilia,19,92010
+Lampedusa e Linosa,84020,Agrigento,AG,Sicilia,19,92010
+Licata,84021,Agrigento,AG,Sicilia,19,92027
+Lucca Sicula,84022,Agrigento,AG,Sicilia,19,92010
+Menfi,84023,Agrigento,AG,Sicilia,19,92013
+Montallegro,84024,Agrigento,AG,Sicilia,19,92010
+Montevago,84025,Agrigento,AG,Sicilia,19,92010
+Naro,84026,Agrigento,AG,Sicilia,19,92028
+Palma di Montechiaro,84027,Agrigento,AG,Sicilia,19,92020
+Porto Empedocle,84028,Agrigento,AG,Sicilia,19,92014
+Racalmuto,84029,Agrigento,AG,Sicilia,19,92020
+Raffadali,84030,Agrigento,AG,Sicilia,19,92015
+Ravanusa,84031,Agrigento,AG,Sicilia,19,92029
+Realmonte,84032,Agrigento,AG,Sicilia,19,92010
+Ribera,84033,Agrigento,AG,Sicilia,19,92016
+Sambuca di Sicilia,84034,Agrigento,AG,Sicilia,19,92017
+San Biagio Platani,84035,Agrigento,AG,Sicilia,19,92020
+San Giovanni Gemini,84036,Agrigento,AG,Sicilia,19,92020
+Santa Elisabetta,84037,Agrigento,AG,Sicilia,19,92020
+Santa Margherita di Belice,84038,Agrigento,AG,Sicilia,19,92018
+Sant'Angelo Muxaro,84039,Agrigento,AG,Sicilia,19,92020
+Santo Stefano Quisquina,84040,Agrigento,AG,Sicilia,19,92020
+Sciacca,84041,Agrigento,AG,Sicilia,19,92019
+Siculiana,84042,Agrigento,AG,Sicilia,19,92010
+Villafranca Sicula,84043,Agrigento,AG,Sicilia,19,92020
+Acquaviva Platani,85001,Caltanissetta,CL,Sicilia,19,93010
+Bompensiere,85002,Caltanissetta,CL,Sicilia,19,93010
+Butera,85003,Caltanissetta,CL,Sicilia,19,93011
+Caltanissetta,85004,Caltanissetta,CL,Sicilia,19,93100
+Campofranco,85005,Caltanissetta,CL,Sicilia,19,93010
+Delia,85006,Caltanissetta,CL,Sicilia,19,93010
+Gela,85007,Caltanissetta,CL,Sicilia,19,93012
+Marianopoli,85008,Caltanissetta,CL,Sicilia,19,93010
+Mazzarino,85009,Caltanissetta,CL,Sicilia,19,93013
+Milena,85010,Caltanissetta,CL,Sicilia,19,93010
+Montedoro,85011,Caltanissetta,CL,Sicilia,19,93010
+Mussomeli,85012,Caltanissetta,CL,Sicilia,19,93014
+Niscemi,85013,Caltanissetta,CL,Sicilia,19,93015
+Resuttano,85014,Caltanissetta,CL,Sicilia,19,93010
+Riesi,85015,Caltanissetta,CL,Sicilia,19,93016
+San Cataldo,85016,Caltanissetta,CL,Sicilia,19,93017
+Santa Caterina Villarmosa,85017,Caltanissetta,CL,Sicilia,19,93018
+Serradifalco,85018,Caltanissetta,CL,Sicilia,19,93010
+Sommatino,85019,Caltanissetta,CL,Sicilia,19,93019
+Sutera,85020,Caltanissetta,CL,Sicilia,19,93010
+Vallelunga Pratameno,85021,Caltanissetta,CL,Sicilia,19,93010
+Villalba,85022,Caltanissetta,CL,Sicilia,19,93010
+Agira,86001,Enna,EN,Sicilia,19,94011
+Aidone,86002,Enna,EN,Sicilia,19,94010
+Assoro,86003,Enna,EN,Sicilia,19,94010
+Barrafranca,86004,Enna,EN,Sicilia,19,94012
+Calascibetta,86005,Enna,EN,Sicilia,19,94010
+Catenanuova,86006,Enna,EN,Sicilia,19,94010
+Centuripe,86007,Enna,EN,Sicilia,19,94010
+Cerami,86008,Enna,EN,Sicilia,19,94010
+Enna,86009,Enna,EN,Sicilia,19,94100
+Gagliano Castelferrato,86010,Enna,EN,Sicilia,19,94010
+Leonforte,86011,Enna,EN,Sicilia,19,94013
+Nicosia,86012,Enna,EN,Sicilia,19,94014
+Nissoria,86013,Enna,EN,Sicilia,19,94010
+Piazza Armerina,86014,Enna,EN,Sicilia,19,94015
+Pietraperzia,86015,Enna,EN,Sicilia,19,94016
+Regalbuto,86016,Enna,EN,Sicilia,19,94017
+Sperlinga,86017,Enna,EN,Sicilia,19,94010
+Troina,86018,Enna,EN,Sicilia,19,94018
+Valguarnera Caropepe,86019,Enna,EN,Sicilia,19,94019
+Villarosa,86020,Enna,EN,Sicilia,19,94010
+Aci Bonaccorsi,87001,Catania,CT,Sicilia,19,95020
+Aci Castello,87002,Catania,CT,Sicilia,19,95021
+Aci Catena,87003,Catania,CT,Sicilia,19,95022
+Acireale,87004,Catania,CT,Sicilia,19,95024
+Aci Sant'Antonio,87005,Catania,CT,Sicilia,19,95025
+Adrano,87006,Catania,CT,Sicilia,19,95031
+Belpasso,87007,Catania,CT,Sicilia,19,95032
+Biancavilla,87008,Catania,CT,Sicilia,19,95033
+Bronte,87009,Catania,CT,Sicilia,19,95034
+Calatabiano,87010,Catania,CT,Sicilia,19,95011
+Caltagirone,87011,Catania,CT,Sicilia,19,95041
+Camporotondo Etneo,87012,Catania,CT,Sicilia,19,95040
+Castel di Iudica,87013,Catania,CT,Sicilia,19,95040
+Castiglione di Sicilia,87014,Catania,CT,Sicilia,19,95012
+Catania,87015,Catania,CT,Sicilia,19,95124
+Fiumefreddo di Sicilia,87016,Catania,CT,Sicilia,19,95013
+Giarre,87017,Catania,CT,Sicilia,19,95014
+Grammichele,87018,Catania,CT,Sicilia,19,95042
+Gravina di Catania,87019,Catania,CT,Sicilia,19,95030
+Licodia Eubea,87020,Catania,CT,Sicilia,19,95040
+Linguaglossa,87021,Catania,CT,Sicilia,19,95015
+Maletto,87022,Catania,CT,Sicilia,19,95035
+Mascali,87023,Catania,CT,Sicilia,19,95016
+Mascalucia,87024,Catania,CT,Sicilia,19,95030
+Militello in Val di Catania,87025,Catania,CT,Sicilia,19,95043
+Milo,87026,Catania,CT,Sicilia,19,95010
+Mineo,87027,Catania,CT,Sicilia,19,95044
+Mirabella Imbaccari,87028,Catania,CT,Sicilia,19,95040
+Misterbianco,87029,Catania,CT,Sicilia,19,95045
+Motta Sant'Anastasia,87030,Catania,CT,Sicilia,19,95040
+Nicolosi,87031,Catania,CT,Sicilia,19,95030
+Palagonia,87032,Catania,CT,Sicilia,19,95046
+Paternò,87033,Catania,CT,Sicilia,19,95047
+Pedara,87034,Catania,CT,Sicilia,19,95030
+Piedimonte Etneo,87035,Catania,CT,Sicilia,19,95017
+Raddusa,87036,Catania,CT,Sicilia,19,95040
+Ramacca,87037,Catania,CT,Sicilia,19,95040
+Randazzo,87038,Catania,CT,Sicilia,19,95036
+Riposto,87039,Catania,CT,Sicilia,19,95018
+San Cono,87040,Catania,CT,Sicilia,19,95040
+San Giovanni la Punta,87041,Catania,CT,Sicilia,19,95037
+San Gregorio di Catania,87042,Catania,CT,Sicilia,19,95027
+San Michele di Ganzaria,87043,Catania,CT,Sicilia,19,95040
+San Pietro Clarenza,87044,Catania,CT,Sicilia,19,95030
+Sant'Agata li Battiati,87045,Catania,CT,Sicilia,19,95030
+Sant'Alfio,87046,Catania,CT,Sicilia,19,95010
+Santa Maria di Licodia,87047,Catania,CT,Sicilia,19,95038
+Santa Venerina,87048,Catania,CT,Sicilia,19,95010
+Scordia,87049,Catania,CT,Sicilia,19,95048
+Trecastagni,87050,Catania,CT,Sicilia,19,95039
+Tremestieri Etneo,87051,Catania,CT,Sicilia,19,95030
+Valverde,87052,Catania,CT,Sicilia,19,95028
+Viagrande,87053,Catania,CT,Sicilia,19,95029
+Vizzini,87054,Catania,CT,Sicilia,19,95049
+Zafferana Etnea,87055,Catania,CT,Sicilia,19,95019
+Mazzarrone,87056,Catania,CT,Sicilia,19,95040
+Maniace,87057,Catania,CT,Sicilia,19,95030
+Ragalna,87058,Catania,CT,Sicilia,19,95030
+Acate,88001,Ragusa,RG,Sicilia,19,97011
+Chiaramonte Gulfi,88002,Ragusa,RG,Sicilia,19,97012
+Comiso,88003,Ragusa,RG,Sicilia,19,97013
+Giarratana,88004,Ragusa,RG,Sicilia,19,97010
+Ispica,88005,Ragusa,RG,Sicilia,19,97014
+Modica,88006,Ragusa,RG,Sicilia,19,97015
+Monterosso Almo,88007,Ragusa,RG,Sicilia,19,97010
+Pozzallo,88008,Ragusa,RG,Sicilia,19,97016
+Ragusa,88009,Ragusa,RG,Sicilia,19,97100
+Santa Croce Camerina,88010,Ragusa,RG,Sicilia,19,97017
+Scicli,88011,Ragusa,RG,Sicilia,19,97018
+Vittoria,88012,Ragusa,RG,Sicilia,19,97019
+Augusta,89001,Siracusa,SR,Sicilia,19,96011
+Avola,89002,Siracusa,SR,Sicilia,19,96012
+Buccheri,89003,Siracusa,SR,Sicilia,19,96010
+Buscemi,89004,Siracusa,SR,Sicilia,19,96010
+Canicattini Bagni,89005,Siracusa,SR,Sicilia,19,96010
+Carlentini,89006,Siracusa,SR,Sicilia,19,96013
+Cassaro,89007,Siracusa,SR,Sicilia,19,96010
+Ferla,89008,Siracusa,SR,Sicilia,19,96010
+Floridia,89009,Siracusa,SR,Sicilia,19,96014
+Francofonte,89010,Siracusa,SR,Sicilia,19,96015
+Lentini,89011,Siracusa,SR,Sicilia,19,96016
+Melilli,89012,Siracusa,SR,Sicilia,19,96010
+Noto,89013,Siracusa,SR,Sicilia,19,96017
+Pachino,89014,Siracusa,SR,Sicilia,19,96018
+Palazzolo Acreide,89015,Siracusa,SR,Sicilia,19,96010
+Rosolini,89016,Siracusa,SR,Sicilia,19,96019
+Siracusa,89017,Siracusa,SR,Sicilia,19,96100
+Solarino,89018,Siracusa,SR,Sicilia,19,96010
+Sortino,89019,Siracusa,SR,Sicilia,19,96010
+Portopalo di Capo Passero,89020,Siracusa,SR,Sicilia,19,96010
+Priolo Gargallo,89021,Siracusa,SR,Sicilia,19,96010
+Aggius,90001,Sassari,SS,Sardegna,20,7020
+Alà dei Sardi,90002,Sassari,SS,Sardegna,20,7020
+Alghero,90003,Sassari,SS,Sardegna,20,7041
+Anela,90004,Sassari,SS,Sardegna,20,7010
+Ardara,90005,Sassari,SS,Sardegna,20,7010
+Arzachena,90006,Sassari,SS,Sardegna,20,7021
+Banari,90007,Sassari,SS,Sardegna,20,7040
+Benetutti,90008,Sassari,SS,Sardegna,20,7010
+Berchidda,90009,Sassari,SS,Sardegna,20,7022
+Bessude,90010,Sassari,SS,Sardegna,20,7040
+Bonnanaro,90011,Sassari,SS,Sardegna,20,7043
+Bono,90012,Sassari,SS,Sardegna,20,7011
+Bonorva,90013,Sassari,SS,Sardegna,20,7012
+Bortigiadas,90014,Sassari,SS,Sardegna,20,7030
+Borutta,90015,Sassari,SS,Sardegna,20,7040
+Bottidda,90016,Sassari,SS,Sardegna,20,7010
+Buddusò,90017,Sassari,SS,Sardegna,20,7020
+Bultei,90018,Sassari,SS,Sardegna,20,7010
+Bulzi,90019,Sassari,SS,Sardegna,20,7030
+Burgos,90020,Sassari,SS,Sardegna,20,7010
+Calangianus,90021,Sassari,SS,Sardegna,20,7023
+Cargeghe,90022,Sassari,SS,Sardegna,20,7030
+Castelsardo,90023,Sassari,SS,Sardegna,20,7031
+Cheremule,90024,Sassari,SS,Sardegna,20,7040
+Chiaramonti,90025,Sassari,SS,Sardegna,20,7030
+Codrongianos,90026,Sassari,SS,Sardegna,20,7040
+Cossoine,90027,Sassari,SS,Sardegna,20,7010
+Esporlatu,90028,Sassari,SS,Sardegna,20,7010
+Florinas,90029,Sassari,SS,Sardegna,20,7030
+Giave,90030,Sassari,SS,Sardegna,20,7010
+Illorai,90031,Sassari,SS,Sardegna,20,7010
+Ittireddu,90032,Sassari,SS,Sardegna,20,7010
+Ittiri,90033,Sassari,SS,Sardegna,20,7044
+Laerru,90034,Sassari,SS,Sardegna,20,7030
+La Maddalena,90035,Sassari,SS,Sardegna,20,7024
+Luogosanto,90036,Sassari,SS,Sardegna,20,7020
+Luras,90037,Sassari,SS,Sardegna,20,7025
+Mara,90038,Sassari,SS,Sardegna,20,7010
+Martis,90039,Sassari,SS,Sardegna,20,7030
+Monteleone Rocca Doria,90040,Sassari,SS,Sardegna,20,7010
+Monti,90041,Sassari,SS,Sardegna,20,7020
+Mores,90042,Sassari,SS,Sardegna,20,7013
+Muros,90043,Sassari,SS,Sardegna,20,7030
+Nughedu San Nicolò,90044,Sassari,SS,Sardegna,20,7010
+Nule,90045,Sassari,SS,Sardegna,20,7010
+Nulvi,90046,Sassari,SS,Sardegna,20,7032
+Olbia,90047,Sassari,SS,Sardegna,20,7026
+Olmedo,90048,Sassari,SS,Sardegna,20,7040
+Oschiri,90049,Sassari,SS,Sardegna,20,7027
+Osilo,90050,Sassari,SS,Sardegna,20,7033
+Ossi,90051,Sassari,SS,Sardegna,20,7045
+Ozieri,90052,Sassari,SS,Sardegna,20,7014
+Padria,90053,Sassari,SS,Sardegna,20,7015
+Palau,90054,Sassari,SS,Sardegna,20,7020
+Pattada,90055,Sassari,SS,Sardegna,20,7016
+Perfugas,90056,Sassari,SS,Sardegna,20,7034
+Ploaghe,90057,Sassari,SS,Sardegna,20,7017
+Porto Torres,90058,Sassari,SS,Sardegna,20,7046
+Pozzomaggiore,90059,Sassari,SS,Sardegna,20,7018
+Putifigari,90060,Sassari,SS,Sardegna,20,7040
+Romana,90061,Sassari,SS,Sardegna,20,7010
+Aglientu,90062,Sassari,SS,Sardegna,20,7020
+Santa Teresa Gallura,90063,Sassari,SS,Sardegna,20,7028
+Sassari,90064,Sassari,SS,Sardegna,20,7100
+Sedini,90065,Sassari,SS,Sardegna,20,7035
+Semestene,90066,Sassari,SS,Sardegna,20,7010
+Sennori,90067,Sassari,SS,Sardegna,20,7036
+Siligo,90068,Sassari,SS,Sardegna,20,7040
+Sorso,90069,Sassari,SS,Sardegna,20,7037
+Tempio Pausania,90070,Sassari,SS,Sardegna,20,7029
+Thiesi,90071,Sassari,SS,Sardegna,20,7047
+Tissi,90072,Sassari,SS,Sardegna,20,7040
+Torralba,90073,Sassari,SS,Sardegna,20,7048
+Trinità d'Agultu e Vignola,90074,Sassari,SS,Sardegna,20,7038
+Tula,90075,Sassari,SS,Sardegna,20,7010
+Uri,90076,Sassari,SS,Sardegna,20,7040
+Usini,90077,Sassari,SS,Sardegna,20,7049
+Villanova Monteleone,90078,Sassari,SS,Sardegna,20,7019
+Valledoria,90079,Sassari,SS,Sardegna,20,7039
+Telti,90080,Sassari,SS,Sardegna,20,7020
+Badesi,90081,Sassari,SS,Sardegna,20,7030
+Viddalba,90082,Sassari,SS,Sardegna,20,7030
+Golfo Aranci,90083,Sassari,SS,Sardegna,20,7020
+Loiri Porto San Paolo,90084,Sassari,SS,Sardegna,20,7020
+Sant'Antonio di Gallura,90085,Sassari,SS,Sardegna,20,7030
+Tergu,90086,Sassari,SS,Sardegna,20,7030
+Santa Maria Coghinas,90087,Sassari,SS,Sardegna,20,7030
+Erula,90088,Sassari,SS,Sardegna,20,7030
+Stintino,90089,Sassari,SS,Sardegna,20,7040
+Padru,90090,Sassari,SS,Sardegna,20,7020
+Budoni,90091,Sassari,SS,Sardegna,20,8020
+San Teodoro,90092,Sassari,SS,Sardegna,20,07052
+Aritzo,91001,Nuoro,NU,Sardegna,20,8031
+Arzana,91002,Nuoro,NU,Sardegna,20,8040
+Atzara,91003,Nuoro,NU,Sardegna,20,8030
+Austis,91004,Nuoro,NU,Sardegna,20,8030
+Bari Sardo,91005,Nuoro,NU,Sardegna,20,8042
+Baunei,91006,Nuoro,NU,Sardegna,20,8040
+Belvì,91007,Nuoro,NU,Sardegna,20,8030
+Birori,91008,Nuoro,NU,Sardegna,20,8010
+Bitti,91009,Nuoro,NU,Sardegna,20,8021
+Bolotana,91010,Nuoro,NU,Sardegna,20,8011
+Borore,91011,Nuoro,NU,Sardegna,20,8016
+Bortigali,91012,Nuoro,NU,Sardegna,20,8012
+Desulo,91016,Nuoro,NU,Sardegna,20,8032
+Dorgali,91017,Nuoro,NU,Sardegna,20,8022
+Dualchi,91018,Nuoro,NU,Sardegna,20,8010
+Elini,91019,Nuoro,NU,Sardegna,20,8040
+Fonni,91024,Nuoro,NU,Sardegna,20,8023
+Gadoni,91025,Nuoro,NU,Sardegna,20,8030
+Gairo,91026,Nuoro,NU,Sardegna,20,8040
+Galtellì,91027,Nuoro,NU,Sardegna,20,8020
+Gavoi,91028,Nuoro,NU,Sardegna,20,8020
+Girasole,91031,Nuoro,NU,Sardegna,20,8040
+Ilbono,91032,Nuoro,NU,Sardegna,20,8040
+Irgoli,91033,Nuoro,NU,Sardegna,20,8020
+Jerzu,91035,Nuoro,NU,Sardegna,20,8044
+Lanusei,91037,Nuoro,NU,Sardegna,20,8045
+Lei,91038,Nuoro,NU,Sardegna,20,8010
+Loceri,91039,Nuoro,NU,Sardegna,20,8040
+Loculi,91040,Nuoro,NU,Sardegna,20,8020
+Lodè,91041,Nuoro,NU,Sardegna,20,8020
+Lotzorai,91042,Nuoro,NU,Sardegna,20,8040
+Lula,91043,Nuoro,NU,Sardegna,20,8020
+Macomer,91044,Nuoro,NU,Sardegna,20,8015
+Mamoiada,91046,Nuoro,NU,Sardegna,20,8024
+Meana Sardo,91047,Nuoro,NU,Sardegna,20,8030
+Noragugume,91050,Nuoro,NU,Sardegna,20,8010
+Nuoro,91051,Nuoro,NU,Sardegna,20,8100
+Oliena,91055,Nuoro,NU,Sardegna,20,8025
+Ollolai,91056,Nuoro,NU,Sardegna,20,8020
+Olzai,91057,Nuoro,NU,Sardegna,20,8020
+Onanì,91058,Nuoro,NU,Sardegna,20,8020
+Onifai,91059,Nuoro,NU,Sardegna,20,8020
+Oniferi,91060,Nuoro,NU,Sardegna,20,8020
+Orani,91061,Nuoro,NU,Sardegna,20,8026
+Orgosolo,91062,Nuoro,NU,Sardegna,20,8027
+Orosei,91063,Nuoro,NU,Sardegna,20,8028
+Orotelli,91064,Nuoro,NU,Sardegna,20,8020
+Ortueri,91066,Nuoro,NU,Sardegna,20,8036
+Orune,91067,Nuoro,NU,Sardegna,20,8020
+Osidda,91068,Nuoro,NU,Sardegna,20,8020
+Osini,91069,Nuoro,NU,Sardegna,20,8040
+Ottana,91070,Nuoro,NU,Sardegna,20,8020
+Ovodda,91071,Nuoro,NU,Sardegna,20,8020
+Perdasdefogu,91072,Nuoro,NU,Sardegna,20,8046
+Posada,91073,Nuoro,NU,Sardegna,20,8020
+Sarule,91077,Nuoro,NU,Sardegna,20,8020
+Silanus,91083,Nuoro,NU,Sardegna,20,8017
+Sindia,91084,Nuoro,NU,Sardegna,20,8018
+Siniscola,91085,Nuoro,NU,Sardegna,20,8029
+Sorgono,91086,Nuoro,NU,Sardegna,20,8038
+Talana,91088,Nuoro,NU,Sardegna,20,8040
+Tertenia,91089,Nuoro,NU,Sardegna,20,8047
+Teti,91090,Nuoro,NU,Sardegna,20,8030
+Tiana,91091,Nuoro,NU,Sardegna,20,8020
+Tonara,91093,Nuoro,NU,Sardegna,20,8039
+Torpè,91094,Nuoro,NU,Sardegna,20,8020
+Tortolì,91095,Nuoro,NU,Sardegna,20,8048
+Triei,91097,Nuoro,NU,Sardegna,20,8040
+Ulassai,91098,Nuoro,NU,Sardegna,20,8040
+Urzulei,91099,Nuoro,NU,Sardegna,20,8040
+Ussassai,91100,Nuoro,NU,Sardegna,20,8040
+Villagrande Strisaili,91101,Nuoro,NU,Sardegna,20,8049
+Cardedu,91103,Nuoro,NU,Sardegna,20,8040
+Lodine,91104,Nuoro,NU,Sardegna,20,8020
+Assemini,92003,Cagliari,CA,Sardegna,20,9032
+Cagliari,92009,Cagliari,CA,Sardegna,20,9124
+Capoterra,92011,Cagliari,CA,Sardegna,20,9012
+Decimomannu,92015,Cagliari,CA,Sardegna,20,9033
+Maracalagonis,92037,Cagliari,CA,Sardegna,20,9040
+Pula,92050,Cagliari,CA,Sardegna,20,9010
+Quartu Sant'Elena,92051,Cagliari,CA,Sardegna,20,9045
+Sarroch,92066,Cagliari,CA,Sardegna,20,9018
+Selargius,92068,Cagliari,CA,Sardegna,20,9047
+Sestu,92074,Cagliari,CA,Sardegna,20,9028
+Settimo San Pietro,92075,Cagliari,CA,Sardegna,20,9040
+Sinnai,92080,Cagliari,CA,Sardegna,20,9048
+Uta,92090,Cagliari,CA,Sardegna,20,9010
+Villa San Pietro,92099,Cagliari,CA,Sardegna,20,9010
+Quartucciu,92105,Cagliari,CA,Sardegna,20,9044
+Elmas,92108,Cagliari,CA,Sardegna,20,9030
+Monserrato,92109,Cagliari,CA,Sardegna,20,9042
+Abbasanta,95001,Oristano,OR,Sardegna,20,9071
+Aidomaggiore,95002,Oristano,OR,Sardegna,20,9070
+Albagiara,95003,Oristano,OR,Sardegna,20,9090
+Ales,95004,Oristano,OR,Sardegna,20,9091
+Allai,95005,Oristano,OR,Sardegna,20,9080
+Arborea,95006,Oristano,OR,Sardegna,20,9092
+Ardauli,95007,Oristano,OR,Sardegna,20,9081
+Assolo,95008,Oristano,OR,Sardegna,20,9080
+Asuni,95009,Oristano,OR,Sardegna,20,9080
+Baradili,95010,Oristano,OR,Sardegna,20,9090
+Baratili San Pietro,95011,Oristano,OR,Sardegna,20,9070
+Baressa,95012,Oristano,OR,Sardegna,20,9090
+Bauladu,95013,Oristano,OR,Sardegna,20,9070
+Bidonì,95014,Oristano,OR,Sardegna,20,9080
+Bonarcado,95015,Oristano,OR,Sardegna,20,9070
+Boroneddu,95016,Oristano,OR,Sardegna,20,9080
+Busachi,95017,Oristano,OR,Sardegna,20,9082
+Cabras,95018,Oristano,OR,Sardegna,20,9072
+Cuglieri,95019,Oristano,OR,Sardegna,20,9073
+Fordongianus,95020,Oristano,OR,Sardegna,20,9083
+Ghilarza,95021,Oristano,OR,Sardegna,20,9074
+Gonnoscodina,95022,Oristano,OR,Sardegna,20,9090
+Gonnosnò,95023,Oristano,OR,Sardegna,20,9090
+Gonnostramatza,95024,Oristano,OR,Sardegna,20,9093
+Marrubiu,95025,Oristano,OR,Sardegna,20,9094
+Masullas,95026,Oristano,OR,Sardegna,20,9090
+Milis,95027,Oristano,OR,Sardegna,20,9070
+Mogorella,95028,Oristano,OR,Sardegna,20,9080
+Mogoro,95029,Oristano,OR,Sardegna,20,9095
+Morgongiori,95030,Oristano,OR,Sardegna,20,9090
+Narbolia,95031,Oristano,OR,Sardegna,20,9070
+Neoneli,95032,Oristano,OR,Sardegna,20,9080
+Norbello,95033,Oristano,OR,Sardegna,20,9070
+Nughedu Santa Vittoria,95034,Oristano,OR,Sardegna,20,9080
+Nurachi,95035,Oristano,OR,Sardegna,20,9070
+Nureci,95036,Oristano,OR,Sardegna,20,9080
+Ollastra,95037,Oristano,OR,Sardegna,20,9088
+Oristano,95038,Oristano,OR,Sardegna,20,9170
+Palmas Arborea,95039,Oristano,OR,Sardegna,20,9090
+Pau,95040,Oristano,OR,Sardegna,20,9090
+Paulilatino,95041,Oristano,OR,Sardegna,20,9070
+Pompu,95042,Oristano,OR,Sardegna,20,9093
+Riola Sardo,95043,Oristano,OR,Sardegna,20,9070
+Ruinas,95044,Oristano,OR,Sardegna,20,9085
+Samugheo,95045,Oristano,OR,Sardegna,20,9086
+San Nicolò d'Arcidano,95046,Oristano,OR,Sardegna,20,9097
+Santa Giusta,95047,Oristano,OR,Sardegna,20,9096
+Villa Sant'Antonio,95048,Oristano,OR,Sardegna,20,9080
+Santu Lussurgiu,95049,Oristano,OR,Sardegna,20,9075
+San Vero Milis,95050,Oristano,OR,Sardegna,20,9070
+Scano di Montiferro,95051,Oristano,OR,Sardegna,20,9078
+Sedilo,95052,Oristano,OR,Sardegna,20,9076
+Seneghe,95053,Oristano,OR,Sardegna,20,9070
+Senis,95054,Oristano,OR,Sardegna,20,9080
+Sennariolo,95055,Oristano,OR,Sardegna,20,9078
+Siamaggiore,95056,Oristano,OR,Sardegna,20,9070
+Siamanna,95057,Oristano,OR,Sardegna,20,9080
+Simala,95058,Oristano,OR,Sardegna,20,9090
+Simaxis,95059,Oristano,OR,Sardegna,20,9088
+Sini,95060,Oristano,OR,Sardegna,20,9090
+Siris,95061,Oristano,OR,Sardegna,20,9090
+Solarussa,95062,Oristano,OR,Sardegna,20,9077
+Sorradile,95063,Oristano,OR,Sardegna,20,9080
+Tadasuni,95064,Oristano,OR,Sardegna,20,9080
+Terralba,95065,Oristano,OR,Sardegna,20,9098
+Tramatza,95066,Oristano,OR,Sardegna,20,9070
+Tresnuraghes,95067,Oristano,OR,Sardegna,20,9079
+Ulà Tirso,95068,Oristano,OR,Sardegna,20,9080
+Uras,95069,Oristano,OR,Sardegna,20,9099
+Usellus,95070,Oristano,OR,Sardegna,20,9090
+Villanova Truschedu,95071,Oristano,OR,Sardegna,20,9084
+Villaurbana,95072,Oristano,OR,Sardegna,20,9080
+Villa Verde,95073,Oristano,OR,Sardegna,20,9090
+Zeddiani,95074,Oristano,OR,Sardegna,20,9070
+Zerfaliu,95075,Oristano,OR,Sardegna,20,9070
+Siapiccia,95076,Oristano,OR,Sardegna,20,9080
+Curcuris,95077,Oristano,OR,Sardegna,20,9090
+Soddì,95078,Oristano,OR,Sardegna,20,9080
+Bosa,95079,Oristano,OR,Sardegna,20,8013
+Flussio,95080,Oristano,OR,Sardegna,20,8010
+Laconi,95082,Oristano,OR,Sardegna,20,8034
+Magomadas,95083,Oristano,OR,Sardegna,20,8010
+Modolo,95084,Oristano,OR,Sardegna,20,8019
+Montresta,95085,Oristano,OR,Sardegna,20,8010
+Sagama,95086,Oristano,OR,Sardegna,20,8010
+Suni,95087,Oristano,OR,Sardegna,20,8010
+Tinnura,95088,Oristano,OR,Sardegna,20,8010
+Arbus,111001,Sud Sardegna,SU,Sardegna,20,9031
+Armungia,111002,Sud Sardegna,SU,Sardegna,20,9040
+Ballao,111003,Sud Sardegna,SU,Sardegna,20,9040
+Barrali,111004,Sud Sardegna,SU,Sardegna,20,9040
+Barumini,111005,Sud Sardegna,SU,Sardegna,20,9021
+Buggerru,111006,Sud Sardegna,SU,Sardegna,20,9010
+Burcei,111007,Sud Sardegna,SU,Sardegna,20,9040
+Calasetta,111008,Sud Sardegna,SU,Sardegna,20,9011
+Carbonia,111009,Sud Sardegna,SU,Sardegna,20,9013
+Carloforte,111010,Sud Sardegna,SU,Sardegna,20,9014
+Castiadas,111011,Sud Sardegna,SU,Sardegna,20,9040
+Collinas,111012,Sud Sardegna,SU,Sardegna,20,9020
+Decimoputzu,111013,Sud Sardegna,SU,Sardegna,20,9010
+Dolianova,111014,Sud Sardegna,SU,Sardegna,20,9041
+Domus de Maria,111015,Sud Sardegna,SU,Sardegna,20,9010
+Domusnovas,111016,Sud Sardegna,SU,Sardegna,20,9015
+Donori,111017,Sud Sardegna,SU,Sardegna,20,9040
+Escalaplano,111018,Sud Sardegna,SU,Sardegna,20,8043
+Escolca,111019,Sud Sardegna,SU,Sardegna,20,8030
+Esterzili,111020,Sud Sardegna,SU,Sardegna,20,8030
+Fluminimaggiore,111021,Sud Sardegna,SU,Sardegna,20,9010
+Furtei,111022,Sud Sardegna,SU,Sardegna,20,9040
+Genoni,111023,Sud Sardegna,SU,Sardegna,20,8030
+Genuri,111024,Sud Sardegna,SU,Sardegna,20,9020
+Gergei,111025,Sud Sardegna,SU,Sardegna,20,8030
+Gesico,111026,Sud Sardegna,SU,Sardegna,20,9040
+Gesturi,111027,Sud Sardegna,SU,Sardegna,20,9020
+Giba,111028,Sud Sardegna,SU,Sardegna,20,9010
+Goni,111029,Sud Sardegna,SU,Sardegna,20,9040
+Gonnesa,111030,Sud Sardegna,SU,Sardegna,20,9010
+Gonnosfanadiga,111031,Sud Sardegna,SU,Sardegna,20,9035
+Guamaggiore,111032,Sud Sardegna,SU,Sardegna,20,9040
+Guasila,111033,Sud Sardegna,SU,Sardegna,20,9040
+Guspini,111034,Sud Sardegna,SU,Sardegna,20,9036
+Iglesias,111035,Sud Sardegna,SU,Sardegna,20,9016
+Isili,111036,Sud Sardegna,SU,Sardegna,20,8033
+Las Plassas,111037,Sud Sardegna,SU,Sardegna,20,9020
+Lunamatrona,111038,Sud Sardegna,SU,Sardegna,20,9022
+Mandas,111039,Sud Sardegna,SU,Sardegna,20,9040
+Masainas,111040,Sud Sardegna,SU,Sardegna,20,9010
+Monastir,111041,Sud Sardegna,SU,Sardegna,20,9023
+Muravera,111042,Sud Sardegna,SU,Sardegna,20,9043
+Musei,111043,Sud Sardegna,SU,Sardegna,20,9010
+Narcao,111044,Sud Sardegna,SU,Sardegna,20,9010
+Nuragus,111045,Sud Sardegna,SU,Sardegna,20,8030
+Nurallao,111046,Sud Sardegna,SU,Sardegna,20,8030
+Nuraminis,111047,Sud Sardegna,SU,Sardegna,20,9024
+Nurri,111048,Sud Sardegna,SU,Sardegna,20,8035
+Nuxis,111049,Sud Sardegna,SU,Sardegna,20,9010
+Orroli,111050,Sud Sardegna,SU,Sardegna,20,8030
+Ortacesus,111051,Sud Sardegna,SU,Sardegna,20,9040
+Pabillonis,111052,Sud Sardegna,SU,Sardegna,20,9030
+Pauli Arbarei,111053,Sud Sardegna,SU,Sardegna,20,9020
+Perdaxius,111054,Sud Sardegna,SU,Sardegna,20,9010
+Pimentel,111055,Sud Sardegna,SU,Sardegna,20,9020
+Piscinas,111056,Sud Sardegna,SU,Sardegna,20,9010
+Portoscuso,111057,Sud Sardegna,SU,Sardegna,20,9010
+Sadali,111058,Sud Sardegna,SU,Sardegna,20,8030
+Samassi,111059,Sud Sardegna,SU,Sardegna,20,9030
+Samatzai,111060,Sud Sardegna,SU,Sardegna,20,9020
+San Basilio,111061,Sud Sardegna,SU,Sardegna,20,9040
+San Gavino Monreale,111062,Sud Sardegna,SU,Sardegna,20,9037
+San Giovanni Suergiu,111063,Sud Sardegna,SU,Sardegna,20,9010
+San Nicolò Gerrei,111064,Sud Sardegna,SU,Sardegna,20,9040
+San Sperate,111065,Sud Sardegna,SU,Sardegna,20,9026
+San Vito,111066,Sud Sardegna,SU,Sardegna,20,9040
+Sanluri,111067,Sud Sardegna,SU,Sardegna,20,9025
+Santadi,111068,Sud Sardegna,SU,Sardegna,20,9010
+Sant'Andrea Frius,111069,Sud Sardegna,SU,Sardegna,20,9040
+Sant'Anna Arresi,111070,Sud Sardegna,SU,Sardegna,20,9010
+Sant'Antioco,111071,Sud Sardegna,SU,Sardegna,20,9017
+Sardara,111072,Sud Sardegna,SU,Sardegna,20,9030
+Segariu,111073,Sud Sardegna,SU,Sardegna,20,9040
+Selegas,111074,Sud Sardegna,SU,Sardegna,20,9040
+Senorbì,111075,Sud Sardegna,SU,Sardegna,20,9040
+Serdiana,111076,Sud Sardegna,SU,Sardegna,20,9040
+Serramanna,111077,Sud Sardegna,SU,Sardegna,20,9038
+Serrenti,111078,Sud Sardegna,SU,Sardegna,20,9027
+Serri,111079,Sud Sardegna,SU,Sardegna,20,8030
+Setzu,111080,Sud Sardegna,SU,Sardegna,20,9029
+Seui,111081,Sud Sardegna,SU,Sardegna,20,8037
+Seulo,111082,Sud Sardegna,SU,Sardegna,20,8030
+Siddi,111083,Sud Sardegna,SU,Sardegna,20,9020
+Siliqua,111084,Sud Sardegna,SU,Sardegna,20,9010
+Silius,111085,Sud Sardegna,SU,Sardegna,20,9040
+Siurgus Donigala,111086,Sud Sardegna,SU,Sardegna,20,9040
+Soleminis,111087,Sud Sardegna,SU,Sardegna,20,9040
+Suelli,111088,Sud Sardegna,SU,Sardegna,20,9040
+Teulada,111089,Sud Sardegna,SU,Sardegna,20,9019
+Tratalias,111090,Sud Sardegna,SU,Sardegna,20,9010
+Tuili,111091,Sud Sardegna,SU,Sardegna,20,9029
+Turri,111092,Sud Sardegna,SU,Sardegna,20,9020
+Ussana,111093,Sud Sardegna,SU,Sardegna,20,9020
+Ussaramanna,111094,Sud Sardegna,SU,Sardegna,20,9020
+Vallermosa,111095,Sud Sardegna,SU,Sardegna,20,9010
+Villacidro,111096,Sud Sardegna,SU,Sardegna,20,9039
+Villamar,111097,Sud Sardegna,SU,Sardegna,20,9020
+Villamassargia,111098,Sud Sardegna,SU,Sardegna,20,9010
+Villanova Tulo,111099,Sud Sardegna,SU,Sardegna,20,8030
+Villanovaforru,111100,Sud Sardegna,SU,Sardegna,20,9020
+Villanovafranca,111101,Sud Sardegna,SU,Sardegna,20,9020
+Villaperuccio,111102,Sud Sardegna,SU,Sardegna,20,9010
+Villaputzu,111103,Sud Sardegna,SU,Sardegna,20,9040
+Villasalto,111104,Sud Sardegna,SU,Sardegna,20,9040
+Villasimius,111105,Sud Sardegna,SU,Sardegna,20,9049
+Villasor,111106,Sud Sardegna,SU,Sardegna,20,9034
+Villaspeciosa,111107,Sud Sardegna,SU,Sardegna,20,9010
diff --git a/edera-api/api/src/main/resources/csv/nations.csv b/edera-api/api/src/main/resources/csv/nations.csv
new file mode 100644 (file)
index 0000000..6623e1b
--- /dev/null
@@ -0,0 +1,233 @@
+code;description
+AD;ANDORRA
+AE;EMIRATI ARABI UNITI
+AG;ANTIGUA/BARBUDA
+AI;ANGUILLA
+AL;ALBANIA
+AM;ARMENIA
+AN;ANTILLE OLANDESI
+AO;ANGOLA
+AR;ARGENTINA
+AS;SAMOA (USA)
+AT;AUSTRIA
+AU;AUSTRALIA
+AW;ARUBA
+AZ;AZERBAIGIAN
+BA;BOSNIA-ERZEGOVINA
+BB;BARBADOS
+BD;BANGLADESH
+BE;BELGIO
+BF;BURKINA FASO
+BG;BULGARIA
+BH;BAHREIN
+BI;BURUNDI
+BJ;BENIN
+BM;BERMUDE
+BN;BRUNEI
+BO;BOLIVIA
+BR;BRASILE
+BS;BAHAMA
+BT;BHUTAN
+BW;BOTSWANA
+BY;BIELORUSSIA
+BZ;BELIZE
+CA;CANADA
+CC;ISOLE COCOS
+CD;REP. DEMOCRATICA CO
+CF;CENTRAFRICA
+CG;REPUBBLICA DEL CONG
+CH;SVIZZERA
+CI;COSTA D'AVORIO
+CK;ARCIPELAGO DI COOK
+CL;CILE
+CM;CAMERUN
+CN;CINA
+CO;COLOMBIA
+CR;COSTA RICA
+CU;CUBA
+CV;CIPRO
+CX;ISOLE CHRISTMAS
+CY;CIPRO
+CZ;REPUBBLICA CECA
+DE;GERMANIA
+DJ;GIBUTI
+DK;DANIMARCA
+DM;DOMINICA
+DO;REPUBBLICA DOMINICA
+DZ;ALGERIA
+EC;ECUADOR
+EE;ESTONIA
+EG;EGITTO
+ER;ERITREA
+ES;SPAGNA
+ET;ETIOPIA
+FI;FINLANDIA
+FJ;ISOLE FIGI
+FK;ISOLE FALKLAND
+FO;ISOLE FAEROER
+FR;FRANCIA
+GA;GABON
+GB;GRAN BRETAGNA
+GD;GRENADA
+GE;GEORGIA
+GF;GUIANA FRANCESE
+GH;GHANA
+GI;GIBILTERRA
+GL;GROENLANDIA
+GM;GAMBIA
+GN;REPUBBLICA DI GUINE
+GP;GUADALUPA
+GQ;GUINEA EQUATORIALE
+GR;GRECIA
+GS;GEORGIA DEL SUD E S
+GT;GUATEMALA
+GU;GUAM
+GW;GUINEA-BISSAU
+GY;GUIANA
+HK;HONG KONG
+HN;HONDURAS
+HR;CROAZIA
+HT;HAITI
+HU;UNGHERIA
+ID;INDONESIA
+IE;IRLANDA
+IL;ISRAELE
+IN;INDIA
+IQ;IRAQ
+IR;IRAN
+IS;ISLANDA
+IT;ITALIA
+JM;GIAMAICA
+JO;GIORDANIA
+JP;GIAPPONE
+KE;KENYA
+KG;KIRGHIZISTAN
+KH;CAMBOGIA
+KI;KIRIBATI
+KM;COMORE
+KN;SAN CRISTOFORO E NE
+KP;COREA DEL NORD
+KR;COREA DEL SUD
+KW;KUWAIT
+KY;CAYMAN
+KZ;KAZAKISTAN
+LA;LAOS
+LB;LIBANO
+LC;SANTA LUCIA
+LI;LIECHTENSTEIN
+LK;SRI LANKA
+LR;LIBERIA
+LS;LESOTHO
+LT;LITUANIA
+LU;LUSSEMBURGO
+LV;LETTONIA
+LY;LIBIA
+MA;MAROCCO
+MC;MONTECARLO
+MD;MOLDAVIA
+MG;MADAGASCAR
+MH;ISOLE MARSHALL
+MK;MACEDONIA
+ML;MALI
+MM;MYANMAR (UNIONE)
+MN;MONGOLIA
+MO;MACAO
+MP;ISOLE MARIANNE
+MQ;MARTINICA
+MR;MAURITANIA
+MS;MONTSERRAT
+MT;MALTA
+MU;MAURIZIO
+MV;MALDIVE
+MW;MALAWI
+MX;MESSICO
+MY;MALAISIA
+MZ;MOZAMBICO
+NA;NAMIBIA
+NC;NUOVA CALEDONIA
+NE;NIGER
+NF;ISOLA DI NORFOLK
+NG;NIGERIA
+NI;NICARAGUA
+NL;OLANDA
+NO;NORVEGIA
+NP;NEPAL
+NR;NAURU
+NZ;NUOVA ZELANDA
+OM;OMAN
+PA;PANAMA
+PE;PERU'
+PF;POLINESIA FRANCESE
+PG;PAPUA-NUOVA GUINEA
+PH;FILIPPINE
+PK;PAKISTAN
+PL;POLONIA
+PM;SAINT-PIERRE ET MIQ
+PN;PITCAIRN
+PR;PORTORICO
+PT;PORTOGALLO
+PW;ISOLE PALAU
+PY;PARAGUAY
+QA;QATAR
+RE;REUNION
+RO;ROMANIA
+RU;RUSSIA
+RW;RUANDA
+SA;ARABIA SAUDITA
+SB;ISOLE SALOMONE
+SC;SEYCHELLES
+SD;SUDAN
+SE;SVEZIA
+SG;SINGAPORE
+SH;ASCENSION
+SI;SLOVENIA
+SK;REPUBBLICA SLOVACCA
+SL;SIERRA LEONE
+SM;SAN MARINO
+SN;SENEGAL
+SO;SOMALIA
+SR;SURINAME
+ST;SAO TOME' E PRINCIP
+SU;UNIONE SOVIETICA
+SV;SALVADOR
+SY;SIRIA
+SZ;SWAZILAND
+TC;TURKS E CAICOS
+TD;CIAD
+TF;ANTARTIDE FRANCESE
+TG;TOGO
+TH;TAILANDIA
+TJ;TAGISKISTAN
+TK;ISOLE TOKELAU
+TM;TURKMENISTAN
+TN;TUNISIA
+TO;TONGA
+TP;TIMOR ORIENTALE
+TR;TURCHIA
+TT;TRINIDAD E TOBAGO
+TV;TUVALU
+TW;TAIWAN
+TZ;TANZANIA
+UA;UCRAINA
+UG;UGANDA
+UM;ISOLE MINORI (USA)
+US;STATI UNITI D'AMERI
+UY;URUGUAY
+UZ;UZBEKISTAN
+VA;CITTA' DEL VATICANO
+VC;SAINT VINCENT E GRE
+VE;VENEZUELA
+VG;ISOLE VERGINI (BRIT
+VI;ISOLE VERGINI (USA)
+VN;VIETNAM
+VU;VANUATU
+WF;WALLIS E FUTUNA
+WS;SAMOA OCCIDENTALI
+XZ;KOSOVO
+YE;YEMEN
+YT;MAYOTTE
+YU;SERBIA E MONTENEGRO
+ZA;SUD AFRICA
+ZM;ZAMBIA
+ZR;ZAIRE
+ZW;ZIMBABWE
diff --git a/edera-api/api/src/main/resources/csv/provinces.csv b/edera-api/api/src/main/resources/csv/provinces.csv
new file mode 100644 (file)
index 0000000..9b6b3ae
--- /dev/null
@@ -0,0 +1,112 @@
+name;region;code;nationCode
+Agrigento;Sicilia;AG;IT
+Alessandria;Piemonte;AL;IT
+Ancona;Marche;AN;IT
+Aosta;Valle d'Aosta;AO;IT
+Arezzo;Toscana;AR;IT
+Ascoli Piceno;Marche;AP;IT
+Asti;Piemonte;AT;IT
+Avellino;Campania;AV;IT
+Bari;Puglia;BA;IT
+Barletta-Andria-Trani;Puglia;BT;IT
+Belluno;Veneto;BL;IT
+Benevento;Campania;BN;IT
+Bergamo;Lombardia;BG;IT
+Biella;Piemonte;BI;IT
+Bologna;Emilia Romagna;BO;IT
+Bolzano;Trentino-Alto Adige;BZ;IT
+Brescia;Lombardia;BS;IT
+Brindisi;Puglia;BR;IT
+Cagliari;Sardegna;CA;IT
+Caltanissetta;Sicilia;CL;IT
+Campobasso;Molise;CB;IT
+Carbonia-Iglesias;Sardegna;CI;IT
+Caserta;Campania;CE;IT
+Catania;Sicilia;CT;IT
+Catanzaro;Calabria;CZ;IT
+Chieti;Abruzzo;CH;IT
+Como;Lombardia;CO;IT
+Cosenza;Calabria;CS;IT
+Cremona;Lombardia;CR;IT
+Crotone;Calabria;KR;IT
+Cuneo;Piemonte;CN;IT
+Enna;Sicilia;EN;IT
+Fermo;Marche;FM;IT
+Ferrara;Emilia Romagna;FE;IT
+Firenze;Toscana;FI;IT
+Foggia;Puglia;FG;IT
+Forl�-Cesena;Emilia Romagna;FC;IT
+Frosinone;Lazio;FR;IT
+Genova;Liguria;GE;IT
+Gorizia;Friuli-Venezia Giulia;GO;IT
+Grosseto;Toscana;GR;IT
+Imperia;Liguria;IM;IT
+Isernia;Molise;IS;IT
+La Spezia;Liguria;SP;IT
+L'Aquila;Abruzzo;AQ;IT
+Latina;Lazio;LT;IT
+Lecce;Puglia;LE;IT
+Lecco;Lombardia;LC;IT
+Livorno;Toscana;LI;IT
+Lodi;Lombardia;LO;IT
+Lucca;Toscana;LU;IT
+Macerata;Marche;MC;IT
+Mantova;Lombardia;MN;IT
+Massa-Carrara;Toscana;MS;IT
+Matera;Basilicata;MT;IT
+Messina;Sicilia;ME;IT
+Milano;Lombardia;MI;IT
+Modena;Emilia Romagna;MO;IT
+Monza e della Brianza;Lombardia;MB;IT
+Napoli;Campania;NA;IT
+Novara;Piemonte;NO;IT
+Nuoro;Sardegna;NU;IT
+Olbia-Tempio;Sardegna;OT;IT
+Oristano;Sardegna;OR;IT
+Padova;Veneto;PD;IT
+Palermo;Sicilia;PA;IT
+Parma;Emilia Romagna;PR;IT
+Pavia;Lombardia;PV;IT
+Perugia;Umbria;PG;IT
+Pesaro e Urbino;Marche;PU;IT
+Pescara;Abruzzo;PE;IT
+Piacenza;Emilia Romagna;PC;IT
+Pisa;Toscana;PI;IT
+Pistoia;Toscana;PT;IT
+Pordenone;Friuli-Venezia Giulia;PN;IT
+Potenza;Basilicata;PZ;IT
+Prato;Toscana;PO;IT
+Ragusa;Sicilia;RG;IT
+Ravenna;Emilia Romagna;RA;IT
+Reggio Calabria;Calabria;RC;IT
+Reggio Emilia;Emilia Romagna;RE;IT
+Rieti;Lazio;RI;IT
+Rimini;Emilia Romagna;RN;IT
+Roma;Lazio;RM;IT
+Rovigo;Veneto;RO;IT
+Salerno;Campania;SA;IT
+Medio Campidano;Sardegna;VS;IT
+Sassari;Sardegna;SS;IT
+Savona;Liguria;SV;IT
+Siena;Toscana;SI;IT
+Siracusa;Sicilia;SR;IT
+Sondrio;Lombardia;SO;IT
+Taranto;Puglia;TA;IT
+Teramo;Abruzzo;TE;IT
+Terni;Umbria;TR;IT
+Torino;Piemonte;TO;IT
+Ogliastra;Sardegna;OG;IT
+Trapani;Sicilia;TP;IT
+Trento;Trentino-Alto Adige;TN;IT
+Treviso;Veneto;TV;IT
+Trieste;Friuli-Venezia Giulia;TS;IT
+Udine;Friuli-Venezia Giulia;UD;IT
+Varese;Lombardia;VA;IT
+Venezia;Veneto;VE;IT
+Verbano-Cusio-Ossola;Piemonte;VB;IT
+Vercelli;Piemonte;VC;IT
+Verona;Veneto;VR;IT
+Vibo Valentia;Calabria;VV;IT
+Vicenza;Veneto;VI;IT
+Viterbo;Lazio;VT;IT
+Sud Sardegna;Sardegna;SU;IT
\ No newline at end of file
diff --git a/edera-api/api/src/main/resources/csv/regions.csv b/edera-api/api/src/main/resources/csv/regions.csv
new file mode 100644 (file)
index 0000000..3d71b6c
--- /dev/null
@@ -0,0 +1,21 @@
+code;description;nationCode;
+1;Abruzzo;IT;
+2;Basilicata;IT;
+3;Calabria;IT;
+4;Campania;IT;
+5;Emilia Romagna;IT;
+6;Friuli-Venezia Giulia;IT;
+7;Lazio;IT;
+8;Liguria;IT;
+9;Lombardia;IT;
+10;Marche;IT;
+11;Molise;IT;
+12;Piemonte;IT;
+13;Puglia;IT;
+14;Sardegna;IT;
+15;Sicilia;IT;
+16;Toscana;IT;
+17;Trentino-Alto Adige;IT;
+18;Umbria;IT;
+19;Valle d'Aosta;IT;
+20;Veneto;IT;
\ No newline at end of file
diff --git a/edera-api/api/src/main/resources/docx/helper.docx b/edera-api/api/src/main/resources/docx/helper.docx
new file mode 100644 (file)
index 0000000..1bc4b5e
Binary files /dev/null and b/edera-api/api/src/main/resources/docx/helper.docx differ
diff --git a/edera-api/api/src/main/resources/i18n.json b/edera-api/api/src/main/resources/i18n.json
new file mode 100644 (file)
index 0000000..04afad3
--- /dev/null
@@ -0,0 +1,3136 @@
+[ {
+  "id" : "it.Forbidden",
+  "code" : "Forbidden",
+  "lang" : "it",
+  "text" : "Accesso non consentito"
+}, {
+  "id" : "it.app.date_format.long",
+  "code" : "app.date_format.long",
+  "lang" : "it",
+  "text" : "DD/MM/YYYY, HH:mm:ss"
+}, {
+  "id" : "it.app.input.max_length_info",
+  "code" : "app.input.max_length_info",
+  "lang" : "it",
+  "text" : "Usati %{count} su %{max} caratteri disponibili (spazi inclusi)"
+}, {
+  "id" : "it.app.label.day",
+  "code" : "app.label.day",
+  "lang" : "it",
+  "text" : "Data"
+}, {
+  "id" : "it.app.label.end_at",
+  "code" : "app.label.end_at",
+  "lang" : "it",
+  "text" : "Alla data"
+}, {
+  "id" : "it.app.welcome",
+  "code" : "app.welcome",
+  "lang" : "it",
+  "text" : "Benvenuto %{full_name}"
+}, {
+  "id" : "it.crud.error.generic",
+  "code" : "crud.error.generic",
+  "lang" : "it",
+  "text" : "Errore generico: impossibile eseguire l'operazione richiesta"
+}, {
+  "id" : "it.crud.error.serialization",
+  "code" : "crud.error.serialization",
+  "lang" : "it",
+  "text" : "Errore nella serializzazione dei dati"
+}, {
+  "id" : "it.docx-template.type.maintenance",
+  "code" : "docx-template.type.maintenance",
+  "lang" : "it",
+  "text" : "Manutenzione"
+}, {
+  "id" : "it.error.invalid_json_response",
+  "code" : "error.invalid_json_response",
+  "lang" : "it",
+  "text" : "Risposta JSON non valida"
+}, {
+  "id" : "it.error.validation",
+  "code" : "error.validation",
+  "lang" : "it",
+  "text" : "Errore di validazione"
+}, {
+  "id" : "it.iam.error.email-address-already-exists",
+  "code" : "iam.error.email-address-already-exists",
+  "lang" : "it",
+  "text" : "Indirizzo e-mail già utilizzato"
+}, {
+  "id" : "it.iam.error.unauthorized",
+  "code" : "iam.error.unauthorized",
+  "lang" : "it",
+  "text" : "Non Autorizzato"
+}, {
+  "id" : "it.iam.token-expired",
+  "code" : "iam.token-expired",
+  "lang" : "it",
+  "text" : "Sessione scaduta"
+}, {
+  "id" : "it.menu.groups.admin",
+  "code" : "menu.groups.admin",
+  "lang" : "it",
+  "text" : "Amministrazione"
+}, {
+  "id" : "it.menu.items.command-logs",
+  "code" : "menu.items.command-logs",
+  "lang" : "it",
+  "text" : "Comandi (Logs)"
+}, {
+  "id" : "it.menu.items.entities/i18n-message",
+  "code" : "menu.items.entities/i18n-message",
+  "lang" : "it",
+  "text" : "Messaggi (i18n)"
+}, {
+  "id" : "it.menu.items.entities/notification",
+  "code" : "menu.items.entities/notification",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.menu.items.entities/user",
+  "code" : "menu.items.entities/user",
+  "lang" : "it",
+  "text" : "Utenti"
+}, {
+  "id" : "it.menu.items.notifications",
+  "code" : "menu.items.notifications",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.menu.items.users",
+  "code" : "menu.items.users",
+  "lang" : "it",
+  "text" : "Utenti"
+}, {
+  "id" : "it.ra.action.add",
+  "code" : "ra.action.add",
+  "lang" : "it",
+  "text" : "Aggiungi"
+}, {
+  "id" : "it.ra.action.add_filter",
+  "code" : "ra.action.add_filter",
+  "lang" : "it",
+  "text" : "Filtri"
+}, {
+  "id" : "it.ra.action.all",
+  "code" : "ra.action.all",
+  "lang" : "it",
+  "text" : "(Tutti)"
+}, {
+  "id" : "it.ra.action.back",
+  "code" : "ra.action.back",
+  "lang" : "it",
+  "text" : "Indietro"
+}, {
+  "id" : "it.ra.action.bulk_actions",
+  "code" : "ra.action.bulk_actions",
+  "lang" : "it",
+  "text" : "%{smart_count} selezionati"
+}, {
+  "id" : "it.ra.action.cancel",
+  "code" : "ra.action.cancel",
+  "lang" : "it",
+  "text" : "Annulla"
+}, {
+  "id" : "it.ra.action.clear_array_input",
+  "code" : "ra.action.clear_array_input",
+  "lang" : "it",
+  "text" : "Rimuovi tutti gli elementi"
+}, {
+  "id" : "it.ra.action.clear_input_value",
+  "code" : "ra.action.clear_input_value",
+  "lang" : "it",
+  "text" : "Svuota il modulo"
+}, {
+  "id" : "it.ra.action.clone",
+  "code" : "ra.action.clone",
+  "lang" : "it",
+  "text" : "Duplica"
+}, {
+  "id" : "it.ra.action.close",
+  "code" : "ra.action.close",
+  "lang" : "it",
+  "text" : "Chiudi"
+}, {
+  "id" : "it.ra.action.close_menu",
+  "code" : "ra.action.close_menu",
+  "lang" : "it",
+  "text" : "Chiudi il menu"
+}, {
+  "id" : "it.ra.action.confirm",
+  "code" : "ra.action.confirm",
+  "lang" : "it",
+  "text" : "Conferma"
+}, {
+  "id" : "it.ra.action.create",
+  "code" : "ra.action.create",
+  "lang" : "it",
+  "text" : "Crea"
+}, {
+  "id" : "it.ra.action.delete",
+  "code" : "ra.action.delete",
+  "lang" : "it",
+  "text" : "Cancella"
+}, {
+  "id" : "it.ra.action.edit",
+  "code" : "ra.action.edit",
+  "lang" : "it",
+  "text" : "Modifica"
+}, {
+  "id" : "it.ra.action.expand",
+  "code" : "ra.action.expand",
+  "lang" : "it",
+  "text" : "Espandi"
+}, {
+  "id" : "it.ra.action.export",
+  "code" : "ra.action.export",
+  "lang" : "it",
+  "text" : "Esporta"
+}, {
+  "id" : "it.ra.action.list",
+  "code" : "ra.action.list",
+  "lang" : "it",
+  "text" : "Elenco"
+}, {
+  "id" : "it.ra.action.move_down",
+  "code" : "ra.action.move_down",
+  "lang" : "it",
+  "text" : "Sposta sotto"
+}, {
+  "id" : "it.ra.action.move_up",
+  "code" : "ra.action.move_up",
+  "lang" : "it",
+  "text" : "Sposta sopra"
+}, {
+  "id" : "it.ra.action.next",
+  "code" : "ra.action.next",
+  "lang" : "it",
+  "text" : "Prosegui"
+}, {
+  "id" : "it.ra.action.open",
+  "code" : "ra.action.open",
+  "lang" : "it",
+  "text" : "Apri"
+}, {
+  "id" : "it.ra.action.open_menu",
+  "code" : "ra.action.open_menu",
+  "lang" : "it",
+  "text" : "Apri il menu"
+}, {
+  "id" : "it.ra.action.refresh",
+  "code" : "ra.action.refresh",
+  "lang" : "it",
+  "text" : "Aggiorna"
+}, {
+  "id" : "it.ra.action.remove",
+  "code" : "ra.action.remove",
+  "lang" : "it",
+  "text" : "Rimuovi"
+}, {
+  "id" : "it.ra.action.remove_filter",
+  "code" : "ra.action.remove_filter",
+  "lang" : "it",
+  "text" : "Rimuovi questo filtro"
+}, {
+  "id" : "it.ra.action.remove_item",
+  "code" : "ra.action.remove_item",
+  "lang" : "it",
+  "text" : "Sei sicuro di voler cancellare?"
+}, {
+  "id" : "it.ra.action.save",
+  "code" : "ra.action.save",
+  "lang" : "it",
+  "text" : "Salva"
+}, {
+  "id" : "it.ra.action.search",
+  "code" : "ra.action.search",
+  "lang" : "it",
+  "text" : "Ricerca"
+}, {
+  "id" : "it.ra.action.show",
+  "code" : "ra.action.show",
+  "lang" : "it",
+  "text" : "Mostra"
+}, {
+  "id" : "it.ra.action.sort",
+  "code" : "ra.action.sort",
+  "lang" : "it",
+  "text" : "Ordina"
+}, {
+  "id" : "it.ra.action.undo",
+  "code" : "ra.action.undo",
+  "lang" : "it",
+  "text" : "Annulla"
+}, {
+  "id" : "it.ra.action.unselect",
+  "code" : "ra.action.unselect",
+  "lang" : "it",
+  "text" : "Annulla selezione"
+}, {
+  "id" : "it.ra.action.view_all",
+  "code" : "ra.action.view_all",
+  "lang" : "it",
+  "text" : "Visualizza Tutto"
+}, {
+  "id" : "it.ra.actions.print_docx",
+  "code" : "ra.actions.print_docx",
+  "lang" : "it",
+  "text" : "Stampa modulo..."
+}, {
+  "id" : "it.ra.active.false",
+  "code" : "ra.active.false",
+  "lang" : "it",
+  "text" : "Disattivata"
+}, {
+  "id" : "it.ra.active.true",
+  "code" : "ra.active.true",
+  "lang" : "it",
+  "text" : "Attiva"
+}, {
+  "id" : "it.ra.activity.schedule.description",
+  "code" : "ra.activity.schedule.description",
+  "lang" : "it",
+  "text" : "E’ possibile programmare una nuova attività di tipo %s presso il cliente %s sul macchinario %s del fornitore %s, relativo al protocollo %s"
+}, {
+  "id" : "it.ra.activity.schedule.title",
+  "code" : "ra.activity.schedule.title",
+  "lang" : "it",
+  "text" : "Nuova attività in programma"
+}, {
+  "id" : "it.ra.activity.status.CLOSED",
+  "code" : "ra.activity.status.CLOSED",
+  "lang" : "it",
+  "text" : "Chiuso"
+}, {
+  "id" : "it.ra.activity.status.OPEN",
+  "code" : "ra.activity.status.OPEN",
+  "lang" : "it",
+  "text" : "Aperto"
+}, {
+  "id" : "it.ra.attachment.info",
+  "code" : "ra.attachment.info",
+  "lang" : "it",
+  "text" : "Creato da %{user} %{created}"
+}, {
+  "id" : "it.ra.audit-log.entities/create",
+  "code" : "ra.audit-log.entities/create",
+  "lang" : "it",
+  "text" : "Creata entità di tipo %{entity}"
+}, {
+  "id" : "it.ra.audit-log.entities/edit",
+  "code" : "ra.audit-log.entities/edit",
+  "lang" : "it",
+  "text" : "Modificata entità di tipo %{entity} con id %{id}"
+}, {
+  "id" : "it.ra.audit-log.entities/find",
+  "code" : "ra.audit-log.entities/find",
+  "lang" : "it",
+  "text" : "Ricerca per %{entity}"
+}, {
+  "id" : "it.ra.audit-log.entities/get",
+  "code" : "ra.audit-log.entities/get",
+  "lang" : "it",
+  "text" : "Visualizzata entità %{entity} con id %{id}"
+}, {
+  "id" : "it.ra.audit-log.entities/post",
+  "code" : "ra.audit-log.entities/post",
+  "lang" : "it",
+  "text" : "Creata nuova entità di tipo %{entity}"
+}, {
+  "id" : "it.ra.audit_log.default_explanation",
+  "code" : "ra.audit_log.default_explanation",
+  "lang" : "it",
+  "text" : "Operazione non identificata"
+}, {
+  "id" : "it.ra.audit_log.default_message",
+  "code" : "ra.audit_log.default_message",
+  "lang" : "it",
+  "text" : "Eseguita chiamata %{method} su %{url}"
+}, {
+  "id" : "it.ra.auth.account",
+  "code" : "ra.auth.account",
+  "lang" : "it",
+  "text" : "Username o email"
+}, {
+  "id" : "it.ra.auth.activate",
+  "code" : "ra.auth.activate",
+  "lang" : "it",
+  "text" : "Attivazione Account"
+}, {
+  "id" : "it.ra.auth.activate_pending",
+  "code" : "ra.auth.activate_pending",
+  "lang" : "it",
+  "text" : "Verifica dell'account in corso..."
+}, {
+  "id" : "it.ra.auth.activate_success",
+  "code" : "ra.auth.activate_success",
+  "lang" : "it",
+  "text" : "Account attivato correttamente"
+}, {
+  "id" : "it.ra.auth.already_have_account",
+  "code" : "ra.auth.already_have_account",
+  "lang" : "it",
+  "text" : "Hai già un account?"
+}, {
+  "id" : "it.ra.auth.auth_check_error",
+  "code" : "ra.auth.auth_check_error",
+  "lang" : "it",
+  "text" : "E' necessario accedere per continuare"
+}, {
+  "id" : "it.ra.auth.back_to_login",
+  "code" : "ra.auth.back_to_login",
+  "lang" : "it",
+  "text" : "Torna al login"
+}, {
+  "id" : "it.ra.auth.change_password",
+  "code" : "ra.auth.change_password",
+  "lang" : "it",
+  "text" : "Cambia Password"
+}, {
+  "id" : "it.ra.auth.create_account",
+  "code" : "ra.auth.create_account",
+  "lang" : "it",
+  "text" : "Registrati"
+}, {
+  "id" : "it.ra.auth.first_access.change_password",
+  "code" : "ra.auth.first_access.change_password",
+  "lang" : "it",
+  "text" : "Attenzione"
+}, {
+  "id" : "it.ra.auth.first_access.change_password_helper",
+  "code" : "ra.auth.first_access.change_password_helper",
+  "lang" : "it",
+  "text" : "Questo Ã¨ il tuo primo accesso alla piattaforma e devi necessariamente cambiare la tua password, compila i campi di seguito e premi salva per confermare l'operazione."
+}, {
+  "id" : "it.ra.auth.forgot_password",
+  "code" : "ra.auth.forgot_password",
+  "lang" : "it",
+  "text" : "Password dimenticata?"
+}, {
+  "id" : "it.ra.auth.impersonating.undo",
+  "code" : "ra.auth.impersonating.undo",
+  "lang" : "it",
+  "text" : "Smetti di impersonate %{name}"
+}, {
+  "id" : "it.ra.auth.impersonating.undo.short",
+  "code" : "ra.auth.impersonating.undo.short",
+  "lang" : "it",
+  "text" : "Esci da %{name}"
+}, {
+  "id" : "it.ra.auth.login.spid",
+  "code" : "ra.auth.login.spid",
+  "lang" : "it",
+  "text" : "Entra con SPID/CIE/CNS"
+}, {
+  "id" : "it.ra.auth.logout",
+  "code" : "ra.auth.logout",
+  "lang" : "it",
+  "text" : "Disconnessione"
+}, {
+  "id" : "it.ra.auth.password",
+  "code" : "ra.auth.password",
+  "lang" : "it",
+  "text" : "Password"
+}, {
+  "id" : "it.ra.auth.password_changed",
+  "code" : "ra.auth.password_changed",
+  "lang" : "it",
+  "text" : "Password aggiornata correttamente"
+}, {
+  "id" : "it.ra.auth.password_placeholder",
+  "code" : "ra.auth.password_placeholder",
+  "lang" : "it",
+  "text" : "La tua password"
+}, {
+  "id" : "it.ra.auth.password_reset.button",
+  "code" : "ra.auth.password_reset.button",
+  "lang" : "it",
+  "text" : "Reimposta"
+}, {
+  "id" : "it.ra.auth.password_reset.title",
+  "code" : "ra.auth.password_reset.title",
+  "lang" : "it",
+  "text" : "Reimposta password"
+}, {
+  "id" : "it.ra.auth.recover",
+  "code" : "ra.auth.recover",
+  "lang" : "it",
+  "text" : "Invia E-mail conferma reset password"
+}, {
+  "id" : "it.ra.auth.recover.inbox_help",
+  "code" : "ra.auth.recover.inbox_help",
+  "lang" : "it",
+  "text" : "Ricorda di controllare la cartella SPAM"
+}, {
+  "id" : "it.ra.auth.recover.title",
+  "code" : "ra.auth.recover.title",
+  "lang" : "it",
+  "text" : "Recupera Password"
+}, {
+  "id" : "it.ra.auth.recover_ok",
+  "code" : "ra.auth.recover_ok",
+  "lang" : "it",
+  "text" : "Recupero password eseguito, contorlla la tua e-mail"
+}, {
+  "id" : "it.ra.auth.register",
+  "code" : "ra.auth.register",
+  "lang" : "it",
+  "text" : "Non hai ancora un account?"
+}, {
+  "id" : "it.ra.auth.register.title",
+  "code" : "ra.auth.register.title",
+  "lang" : "it",
+  "text" : "Registrati"
+}, {
+  "id" : "it.ra.auth.register_iam.error.email-address-already-exists",
+  "code" : "ra.auth.register_iam.error.email-address-already-exists",
+  "lang" : "it",
+  "text" : "Non Ã¨ possibile utilizzare questo indirizzo e-mail"
+}, {
+  "id" : "it.ra.auth.register_iam.error.email-not-valid",
+  "code" : "ra.auth.register_iam.error.email-not-valid",
+  "lang" : "it",
+  "text" : "L'indirizzo e-mail inserito non Ã¨ valido"
+}, {
+  "id" : "it.ra.auth.register_ok",
+  "code" : "ra.auth.register_ok",
+  "lang" : "it",
+  "text" : "Registrazione avvenuta correttamente, controlla la tua e-mail per confermare i dati inseriti"
+}, {
+  "id" : "it.ra.auth.reset_password",
+  "code" : "ra.auth.reset_password",
+  "lang" : "it",
+  "text" : "Recupera Password"
+}, {
+  "id" : "it.ra.auth.sign_in",
+  "code" : "ra.auth.sign_in",
+  "lang" : "it",
+  "text" : "Login"
+}, {
+  "id" : "it.ra.auth.sign_in_error",
+  "code" : "ra.auth.sign_in_error",
+  "lang" : "it",
+  "text" : "Autenticazione fallita, riprovare."
+}, {
+  "id" : "it.ra.auth.sign_in_success",
+  "code" : "ra.auth.sign_in_success",
+  "lang" : "it",
+  "text" : "Utente impersona con successo, ricaricamento pagina in corso..."
+}, {
+  "id" : "it.ra.auth.sign_out_success",
+  "code" : "ra.auth.sign_out_success",
+  "lang" : "it",
+  "text" : "Logout da altro utente eseguito con successo, ricaricamento pagina in corso..."
+}, {
+  "id" : "it.ra.auth.stop_impersonate",
+  "code" : "ra.auth.stop_impersonate",
+  "lang" : "it",
+  "text" : "Smetti di impersonare"
+}, {
+  "id" : "it.ra.auth.user_menu",
+  "code" : "ra.auth.user_menu",
+  "lang" : "it",
+  "text" : "Profilo"
+}, {
+  "id" : "it.ra.auth.username",
+  "code" : "ra.auth.username",
+  "lang" : "it",
+  "text" : "Nome utente"
+}, {
+  "id" : "it.ra.boolean.false",
+  "code" : "ra.boolean.false",
+  "lang" : "it",
+  "text" : "No"
+}, {
+  "id" : "it.ra.boolean.null",
+  "code" : "ra.boolean.null",
+  "lang" : "it",
+  "text" : "(Tutti)"
+}, {
+  "id" : "it.ra.boolean.true",
+  "code" : "ra.boolean.true",
+  "lang" : "it",
+  "text" : "Si"
+}, {
+  "id" : "it.ra.custom_pages.welcome.subtitle",
+  "code" : "ra.custom_pages.welcome.subtitle",
+  "lang" : "it",
+  "text" : "Utilizza le voci di menu a sinistra per navigare ed usufruire delle funzioni applicative."
+}, {
+  "id" : "it.ra.custom_pages.welcome.title",
+  "code" : "ra.custom_pages.welcome.title",
+  "lang" : "it",
+  "text" : "Ciao!"
+}, {
+  "id" : "it.ra.error.activity.save.supply-not-found",
+  "code" : "ra.error.activity.save.supply-not-found",
+  "lang" : "it",
+  "text" : "Errore durante il salvataggio: l'approvvigionamento selezionato Ã¨ inesistente"
+}, {
+  "id" : "it.ra.error.equipment-type.delete",
+  "code" : "ra.error.equipment-type.delete",
+  "lang" : "it",
+  "text" : "Impossibile eliminare il Tipo di Attrezzatura"
+}, {
+  "id" : "it.ra.error.invalid-date-range",
+  "code" : "ra.error.invalid-date-range",
+  "lang" : "it",
+  "text" : "Impossibile filtrare con le date selezionate"
+}, {
+  "id" : "it.ra.error.maintenance.save.activity-not-found",
+  "code" : "ra.error.maintenance.save.activity-not-found",
+  "lang" : "it",
+  "text" : "Errore durante il salvataggio: l'attività selezionata Ã¨ inesistente"
+}, {
+  "id" : "it.ra.error.maintenance.save.protocol-not-found",
+  "code" : "ra.error.maintenance.save.protocol-not-found",
+  "lang" : "it",
+  "text" : "Errore durante il salvataggio: il protocollo Ã¨ inesistente"
+}, {
+  "id" : "it.ra.error.maintenance.save.supply-not-found",
+  "code" : "ra.error.maintenance.save.supply-not-found",
+  "lang" : "it",
+  "text" : "Errore durante il salvataggio: l'approvvigionamento Ã¨ inesistente"
+}, {
+  "id" : "it.ra.error.price-list-item.save.equipmentId-already-exists",
+  "code" : "ra.error.price-list-item.save.equipmentId-already-exists",
+  "lang" : "it",
+  "text" : "Impossibile salvare l'entità: l'Attrezzatura selezionata Ã¨ già presente nel listino corrente"
+}, {
+  "id" : "it.ra.error.price-list.save.start-after-end",
+  "code" : "ra.error.price-list.save.start-after-end",
+  "lang" : "it",
+  "text" : "Impossibile salvare il Listino: Data Inizio non può essere maggiore della data di Data Fine"
+}, {
+  "id" : "it.ra.error.supplier.delete",
+  "code" : "ra.error.supplier.delete",
+  "lang" : "it",
+  "text" : "Impossibile eliminare il Fornitore"
+}, {
+  "id" : "it.ra.error.supply.save.protocol-not-found",
+  "code" : "ra.error.supply.save.protocol-not-found",
+  "lang" : "it",
+  "text" : "Errore durante il salvataggio: il protocollo selezionato Ã¨ inesistente"
+}, {
+  "id" : "it.ra.form.docx_print.dirty",
+  "code" : "ra.form.docx_print.dirty",
+  "lang" : "it",
+  "text" : "Attenzione"
+}, {
+  "id" : "it.ra.form.docx_print.dirty_message",
+  "code" : "ra.form.docx_print.dirty_message",
+  "lang" : "it",
+  "text" : "Se successivamente a questa modifica intendi stampare dei moduli devi sapere che i dati attualmente modificati non saranno presi in considerazione fino a quando gli stessi non saranno salvati."
+}, {
+  "id" : "it.ra.image.upload_several",
+  "code" : "ra.image.upload_several",
+  "lang" : "it",
+  "text" : "Trascina le immagini da caricare, oppure clicca per selezionarle."
+}, {
+  "id" : "it.ra.image.upload_single",
+  "code" : "ra.image.upload_single",
+  "lang" : "it",
+  "text" : "Trascina l'immagine da caricare, oppure clicca per selezionarla."
+}, {
+  "id" : "it.ra.input.file.upload_several",
+  "code" : "ra.input.file.upload_several",
+  "lang" : "it",
+  "text" : "Trascina i files da caricare, oppure clicca per selezionare."
+}, {
+  "id" : "it.ra.input.file.upload_single",
+  "code" : "ra.input.file.upload_single",
+  "lang" : "it",
+  "text" : "Trascina il file da caricare, oppure clicca per selezionarlo."
+}, {
+  "id" : "it.ra.input.image.upload_single",
+  "code" : "ra.input.image.upload_single",
+  "lang" : "it",
+  "text" : "Seleziona o sposta qui il file da caricare"
+}, {
+  "id" : "it.ra.input.password.toggle_hidden",
+  "code" : "ra.input.password.toggle_hidden",
+  "lang" : "it",
+  "text" : "Nascondi Password"
+}, {
+  "id" : "it.ra.input.password.toggle_visible",
+  "code" : "ra.input.password.toggle_visible",
+  "lang" : "it",
+  "text" : "Mostra Password"
+}, {
+  "id" : "it.ra.menu.control-panel",
+  "code" : "ra.menu.control-panel",
+  "lang" : "it",
+  "text" : "Impostazioni"
+}, {
+  "id" : "it.ra.menu.custom_pages",
+  "code" : "ra.menu.custom_pages",
+  "lang" : "it",
+  "text" : "Dashboard"
+}, {
+  "id" : "it.ra.menu.dashboard",
+  "code" : "ra.menu.dashboard",
+  "lang" : "it",
+  "text" : "Dashboard"
+}, {
+  "id" : "it.ra.menu.item.custom_page",
+  "code" : "ra.menu.item.custom_page",
+  "lang" : "it",
+  "text" : "Home"
+}, {
+  "id" : "it.ra.menu.item.entities/activity",
+  "code" : "ra.menu.item.entities/activity",
+  "lang" : "it",
+  "text" : "Attività"
+}, {
+  "id" : "it.ra.menu.item.entities/activity-type",
+  "code" : "ra.menu.item.entities/activity-type",
+  "lang" : "it",
+  "text" : "Tipi Attività"
+}, {
+  "id" : "it.ra.menu.item.entities/audit-log",
+  "code" : "ra.menu.item.entities/audit-log",
+  "lang" : "it",
+  "text" : "Audit Log"
+}, {
+  "id" : "it.ra.menu.item.entities/customer",
+  "code" : "ra.menu.item.entities/customer",
+  "lang" : "it",
+  "text" : "Clienti"
+}, {
+  "id" : "it.ra.menu.item.entities/device",
+  "code" : "ra.menu.item.entities/device",
+  "lang" : "it",
+  "text" : "Dispositivi"
+}, {
+  "id" : "it.ra.menu.item.entities/docx-template",
+  "code" : "ra.menu.item.entities/docx-template",
+  "lang" : "it",
+  "text" : "Modulistica"
+}, {
+  "id" : "it.ra.menu.item.entities/equipment",
+  "code" : "ra.menu.item.entities/equipment",
+  "lang" : "it",
+  "text" : "Attrezzature"
+}, {
+  "id" : "it.ra.menu.item.entities/equipment-type",
+  "code" : "ra.menu.item.entities/equipment-type",
+  "lang" : "it",
+  "text" : "Tipi Attrezzatura"
+}, {
+  "id" : "it.ra.menu.item.entities/i18n-message",
+  "code" : "ra.menu.item.entities/i18n-message",
+  "lang" : "it",
+  "text" : "Messaggi (i18n)"
+}, {
+  "id" : "it.ra.menu.item.entities/maintenance",
+  "code" : "ra.menu.item.entities/maintenance",
+  "lang" : "it",
+  "text" : "Manutenzioni"
+}, {
+  "id" : "it.ra.menu.item.entities/notification",
+  "code" : "ra.menu.item.entities/notification",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.ra.menu.item.entities/protocol",
+  "code" : "ra.menu.item.entities/protocol",
+  "lang" : "it",
+  "text" : "Protocolli"
+}, {
+  "id" : "it.ra.menu.item.entities/rfid-device",
+  "code" : "ra.menu.item.entities/rfid-device",
+  "lang" : "it",
+  "text" : "RFID"
+}, {
+  "id" : "it.ra.menu.item.entities/rfid-device-track",
+  "code" : "ra.menu.item.entities/rfid-device-track",
+  "lang" : "it",
+  "text" : "Tracciamento RFID"
+}, {
+  "id" : "it.ra.menu.item.entities/supply",
+  "code" : "ra.menu.item.entities/supply",
+  "lang" : "it",
+  "text" : "Approvvigionamenti"
+}, {
+  "id" : "it.ra.menu.item.entities/user",
+  "code" : "ra.menu.item.entities/user",
+  "lang" : "it",
+  "text" : "Utenti"
+}, {
+  "id" : "it.ra.menu.item.notification",
+  "code" : "ra.menu.item.notification",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.ra.menu.item.position",
+  "code" : "ra.menu.item.position",
+  "lang" : "it",
+  "text" : "Posizione"
+}, {
+  "id" : "it.ra.menu.item.supplier",
+  "code" : "ra.menu.item.supplier",
+  "lang" : "it",
+  "text" : "Fornitori"
+}, {
+  "id" : "it.ra.menu.maintenance",
+  "code" : "ra.menu.maintenance",
+  "lang" : "it",
+  "text" : "Manutenzione"
+}, {
+  "id" : "it.ra.menu.security",
+  "code" : "ra.menu.security",
+  "lang" : "it",
+  "text" : "Sicurezza"
+}, {
+  "id" : "it.ra.message.about",
+  "code" : "ra.message.about",
+  "lang" : "it",
+  "text" : "Informazioni"
+}, {
+  "id" : "it.ra.message.are_you_sure",
+  "code" : "ra.message.are_you_sure",
+  "lang" : "it",
+  "text" : "Sei sicuro?"
+}, {
+  "id" : "it.ra.message.bulk_delete_content",
+  "code" : "ra.message.bulk_delete_content",
+  "lang" : "it",
+  "text" : "Sei sicuro di voler cancellare questo %{name}? |||| Sei sicuro di voler eliminare questi %{smart_count}?"
+}, {
+  "id" : "it.ra.message.bulk_delete_title",
+  "code" : "ra.message.bulk_delete_title",
+  "lang" : "it",
+  "text" : "Delete %{name} |||| Delete %{smart_count} %{name} items"
+}, {
+  "id" : "it.ra.message.clear_array_input",
+  "code" : "ra.message.clear_array_input",
+  "lang" : "it",
+  "text" : "Sei sicuro di voler rimuovere tutti gli elementi?"
+}, {
+  "id" : "it.ra.message.confirm_delete",
+  "code" : "ra.message.confirm_delete",
+  "lang" : "it",
+  "text" : "Sei sicuro di voler cancellare questo elemento?"
+}, {
+  "id" : "it.ra.message.delete_content",
+  "code" : "ra.message.delete_content",
+  "lang" : "it",
+  "text" : "Sicuro di voler cancellare questo elemento?"
+}, {
+  "id" : "it.ra.message.delete_title",
+  "code" : "ra.message.delete_title",
+  "lang" : "it",
+  "text" : "Conferma cancellazione"
+}, {
+  "id" : "it.ra.message.details",
+  "code" : "ra.message.details",
+  "lang" : "it",
+  "text" : "Dettagli"
+}, {
+  "id" : "it.ra.message.error",
+  "code" : "ra.message.error",
+  "lang" : "it",
+  "text" : "Si Ã¨ verificato un errore locale per cui non Ã¨ stato possibile completare l'operazione. "
+}, {
+  "id" : "it.ra.message.error_more_info",
+  "code" : "ra.message.error_more_info",
+  "lang" : "it",
+  "text" : "Per ottenere maggiori informazioni utilizza Strumenti di Sviluppo del tuo browser."
+}, {
+  "id" : "it.ra.message.invalid_form",
+  "code" : "ra.message.invalid_form",
+  "lang" : "it",
+  "text" : "Il modulo non Ã¨ valido. Si prega di verificare la presenza di errori."
+}, {
+  "id" : "it.ra.message.loading",
+  "code" : "ra.message.loading",
+  "lang" : "it",
+  "text" : "La pagina si sta caricando, solo un momento per favore"
+}, {
+  "id" : "it.ra.message.no",
+  "code" : "ra.message.no",
+  "lang" : "it",
+  "text" : "No"
+}, {
+  "id" : "it.ra.message.not_found",
+  "code" : "ra.message.not_found",
+  "lang" : "it",
+  "text" : "Hai inserito un URL errato, oppure hai cliccato un link errato"
+}, {
+  "id" : "it.ra.message.profile_updated",
+  "code" : "ra.message.profile_updated",
+  "lang" : "it",
+  "text" : "Profilo aggiornato correttamente"
+}, {
+  "id" : "it.ra.message.remove_item",
+  "code" : "ra.message.remove_item",
+  "lang" : "it",
+  "text" : "Clicca \"Conferma\" per cancellare"
+}, {
+  "id" : "it.ra.message.unsaved_changes",
+  "code" : "ra.message.unsaved_changes",
+  "lang" : "it",
+  "text" : "Alcune modifiche non sono state salvate. Sei sicuro di volerle ignorare?"
+}, {
+  "id" : "it.ra.message.yes",
+  "code" : "ra.message.yes",
+  "lang" : "it",
+  "text" : "Si"
+}, {
+  "id" : "it.ra.navigation.next",
+  "code" : "ra.navigation.next",
+  "lang" : "it",
+  "text" : "Successivo"
+}, {
+  "id" : "it.ra.navigation.no_more_results",
+  "code" : "ra.navigation.no_more_results",
+  "lang" : "it",
+  "text" : "La pagina numero %{page} Ã¨ fuori dall'intervallo. Prova la pagina precedente."
+}, {
+  "id" : "it.ra.navigation.no_results",
+  "code" : "ra.navigation.no_results",
+  "lang" : "it",
+  "text" : "Nessun risultato trovato"
+}, {
+  "id" : "it.ra.navigation.page_out_from_begin",
+  "code" : "ra.navigation.page_out_from_begin",
+  "lang" : "it",
+  "text" : "Il numero di pagina deve essere maggiore di 1"
+}, {
+  "id" : "it.ra.navigation.page_out_from_end",
+  "code" : "ra.navigation.page_out_from_end",
+  "lang" : "it",
+  "text" : "Fine della paginazione"
+}, {
+  "id" : "it.ra.navigation.page_out_of_boundaries",
+  "code" : "ra.navigation.page_out_of_boundaries",
+  "lang" : "it",
+  "text" : "Il numero di pagina %{page} Ã¨ fuori dei limiti"
+}, {
+  "id" : "it.ra.navigation.page_range_info",
+  "code" : "ra.navigation.page_range_info",
+  "lang" : "it",
+  "text" : "%{offsetBegin}-%{offsetEnd} di %{total}"
+}, {
+  "id" : "it.ra.navigation.page_rows_per_page",
+  "code" : "ra.navigation.page_rows_per_page",
+  "lang" : "it",
+  "text" : "Righe per pagina"
+}, {
+  "id" : "it.ra.navigation.prev",
+  "code" : "ra.navigation.prev",
+  "lang" : "it",
+  "text" : "Precedente"
+}, {
+  "id" : "it.ra.navigation.skip_nav",
+  "code" : "ra.navigation.skip_nav",
+  "lang" : "it",
+  "text" : "Vai al contenuto"
+}, {
+  "id" : "it.ra.no",
+  "code" : "ra.no",
+  "lang" : "it",
+  "text" : "No"
+}, {
+  "id" : "it.ra.no_results",
+  "code" : "ra.no_results",
+  "lang" : "it",
+  "text" : "Nessun elemento registrato."
+}, {
+  "id" : "it.ra.notification",
+  "code" : "ra.notification",
+  "lang" : "it",
+  "text" : "Notifica"
+}, {
+  "id" : "it.ra.notification.bad_item",
+  "code" : "ra.notification.bad_item",
+  "lang" : "it",
+  "text" : "Record errato"
+}, {
+  "id" : "it.ra.notification.canceled",
+  "code" : "ra.notification.canceled",
+  "lang" : "it",
+  "text" : "Azione annullata"
+}, {
+  "id" : "it.ra.notification.created",
+  "code" : "ra.notification.created",
+  "lang" : "it",
+  "text" : "Salvataggio eseguito"
+}, {
+  "id" : "it.ra.notification.data_provider_error",
+  "code" : "ra.notification.data_provider_error",
+  "lang" : "it",
+  "text" : "Errore del data provider. Controlla la console per i dettagli."
+}, {
+  "id" : "it.ra.notification.deleted",
+  "code" : "ra.notification.deleted",
+  "lang" : "it",
+  "text" : "Record eliminato |||| %{smart_count} records eliminati"
+}, {
+  "id" : "it.ra.notification.http_error",
+  "code" : "ra.notification.http_error",
+  "lang" : "it",
+  "text" : "Errore di comunicazione con il server dati"
+}, {
+  "id" : "it.ra.notification.i18n_error",
+  "code" : "ra.notification.i18n_error",
+  "lang" : "it",
+  "text" : "Traduzioni non trovate per il linguaggio specificato"
+}, {
+  "id" : "it.ra.notification.item_doesnt_exist",
+  "code" : "ra.notification.item_doesnt_exist",
+  "lang" : "it",
+  "text" : "Record inesistente"
+}, {
+  "id" : "it.ra.notification.logged_out",
+  "code" : "ra.notification.logged_out",
+  "lang" : "it",
+  "text" : "La sessione Ã¨ stata terminata, si prega di ripetere l'autenticazione."
+}, {
+  "id" : "it.ra.notification.mark_as_readed",
+  "code" : "ra.notification.mark_as_readed",
+  "lang" : "it",
+  "text" : "Segna come letto"
+}, {
+  "id" : "it.ra.notification.mark_as_unreaded",
+  "code" : "ra.notification.mark_as_unreaded",
+  "lang" : "it",
+  "text" : "Segna come non letto"
+}, {
+  "id" : "it.ra.notification.readed",
+  "code" : "ra.notification.readed",
+  "lang" : "it",
+  "text" : "Letta in data %{readed}"
+}, {
+  "id" : "it.ra.notification.readed_error",
+  "code" : "ra.notification.readed_error",
+  "lang" : "it",
+  "text" : "Impossibile segnare le notifiche selezionate."
+}, {
+  "id" : "it.ra.notification.unreaded",
+  "code" : "ra.notification.unreaded",
+  "lang" : "it",
+  "text" : "Non lette"
+}, {
+  "id" : "it.ra.notification.updated",
+  "code" : "ra.notification.updated",
+  "lang" : "it",
+  "text" : "Record aggiornato |||| %{smart_count} records aggiornati"
+}, {
+  "id" : "it.ra.notification.view_all",
+  "code" : "ra.notification.view_all",
+  "lang" : "it",
+  "text" : "Visualizza Tutte"
+}, {
+  "id" : "it.ra.notifications.read_all",
+  "code" : "ra.notifications.read_all",
+  "lang" : "it",
+  "text" : "Segna tutte come lette"
+}, {
+  "id" : "it.ra.notifications.title",
+  "code" : "ra.notifications.title",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.ra.page.create",
+  "code" : "ra.page.create",
+  "lang" : "it",
+  "text" : "Aggiungi %{name}"
+}, {
+  "id" : "it.ra.page.dashboard",
+  "code" : "ra.page.dashboard",
+  "lang" : "it",
+  "text" : "Cruscotto"
+}, {
+  "id" : "it.ra.page.edit",
+  "code" : "ra.page.edit",
+  "lang" : "it",
+  "text" : "%{name} %{id}"
+}, {
+  "id" : "it.ra.page.empty",
+  "code" : "ra.page.empty",
+  "lang" : "it",
+  "text" : "Nessun dato caricato per %{name}"
+}, {
+  "id" : "it.ra.page.error",
+  "code" : "ra.page.error",
+  "lang" : "it",
+  "text" : "Qualcosa non ha funzionato"
+}, {
+  "id" : "it.ra.page.invite",
+  "code" : "ra.page.invite",
+  "lang" : "it",
+  "text" : "Vuoi aggiungerne uno?"
+}, {
+  "id" : "it.ra.page.list",
+  "code" : "ra.page.list",
+  "lang" : "it",
+  "text" : "%{name}"
+}, {
+  "id" : "it.ra.page.loading",
+  "code" : "ra.page.loading",
+  "lang" : "it",
+  "text" : "Caricamento in corso"
+}, {
+  "id" : "it.ra.page.not_found",
+  "code" : "ra.page.not_found",
+  "lang" : "it",
+  "text" : "Non trovato"
+}, {
+  "id" : "it.ra.page.profile.title",
+  "code" : "ra.page.profile.title",
+  "lang" : "it",
+  "text" : "Informazioni profilo"
+}, {
+  "id" : "it.ra.page.show",
+  "code" : "ra.page.show",
+  "lang" : "it",
+  "text" : "%{name} %{id}"
+}, {
+  "id" : "it.ra.password.toggle_hidden",
+  "code" : "ra.password.toggle_hidden",
+  "lang" : "it",
+  "text" : "Mostra la password"
+}, {
+  "id" : "it.ra.password.toggle_visible",
+  "code" : "ra.password.toggle_visible",
+  "lang" : "it",
+  "text" : "Nascondi la password"
+}, {
+  "id" : "it.ra.position.report.title",
+  "code" : "ra.position.report.title",
+  "lang" : "it",
+  "text" : "Statistiche Posizione"
+}, {
+  "id" : "it.ra.position.waiting-for-data",
+  "code" : "ra.position.waiting-for-data",
+  "lang" : "it",
+  "text" : "Seleziona un ufficio"
+}, {
+  "id" : "it.ra.reference_list.sorry",
+  "code" : "ra.reference_list.sorry",
+  "lang" : "it",
+  "text" : "Salva almeno una volta l'elemento corrente per poter aggiungere nuove informazioni in questa sezione."
+}, {
+  "id" : "it.ra.references.all_missing",
+  "code" : "ra.references.all_missing",
+  "lang" : "it",
+  "text" : "Impossibile trovare i riferimenti associati."
+}, {
+  "id" : "it.ra.references.many_missing",
+  "code" : "ra.references.many_missing",
+  "lang" : "it",
+  "text" : "Almeno uno dei riferimenti associati non sembra più disponibile."
+}, {
+  "id" : "it.ra.references.single_missing",
+  "code" : "ra.references.single_missing",
+  "lang" : "it",
+  "text" : "Il riferimento associato non sembra più disponibile."
+}, {
+  "id" : "it.ra.register.name",
+  "code" : "ra.register.name",
+  "lang" : "it",
+  "text" : "Nome Account"
+}, {
+  "id" : "it.ra.roles.admin",
+  "code" : "ra.roles.admin",
+  "lang" : "it",
+  "text" : "Amministratore"
+}, {
+  "id" : "it.ra.roles.customer",
+  "code" : "ra.roles.customer",
+  "lang" : "it",
+  "text" : "Cliente"
+}, {
+  "id" : "it.ra.roles.maintainer",
+  "code" : "ra.roles.maintainer",
+  "lang" : "it",
+  "text" : "Manutentore"
+}, {
+  "id" : "it.ra.roles.operator",
+  "code" : "ra.roles.operator",
+  "lang" : "it",
+  "text" : "Operatore"
+}, {
+  "id" : "it.ra.roles.user",
+  "code" : "ra.roles.user",
+  "lang" : "it",
+  "text" : "Utilizzatore"
+}, {
+  "id" : "it.ra.sort.ASC",
+  "code" : "ra.sort.ASC",
+  "lang" : "it",
+  "text" : "cresente"
+}, {
+  "id" : "it.ra.sort.DESC",
+  "code" : "ra.sort.DESC",
+  "lang" : "it",
+  "text" : "decrescente"
+}, {
+  "id" : "it.ra.sort.sort_by",
+  "code" : "ra.sort.sort_by",
+  "lang" : "it",
+  "text" : "Ordina per %{field} %{order}"
+}, {
+  "id" : "it.ra.status.closed",
+  "code" : "ra.status.closed",
+  "lang" : "it",
+  "text" : "Chiuso"
+}, {
+  "id" : "it.ra.status.open",
+  "code" : "ra.status.open",
+  "lang" : "it",
+  "text" : "Aperto"
+}, {
+  "id" : "it.ra.supply.card-status.RUNNING",
+  "code" : "ra.supply.card-status.RUNNING",
+  "lang" : "it",
+  "text" : "Stato: In uso"
+}, {
+  "id" : "it.ra.supply.card-status.STOPPED",
+  "code" : "ra.supply.card-status.STOPPED",
+  "lang" : "it",
+  "text" : "Stato: Sospeso"
+}, {
+  "id" : "it.ra.supply.status.RUNNING",
+  "code" : "ra.supply.status.RUNNING",
+  "lang" : "it",
+  "text" : "In Uso"
+}, {
+  "id" : "it.ra.supply.status.STOPPED",
+  "code" : "ra.supply.status.STOPPED",
+  "lang" : "it",
+  "text" : "Sospeso"
+}, {
+  "id" : "it.ra.time.days",
+  "code" : "ra.time.days",
+  "lang" : "it",
+  "text" : "Giorni"
+}, {
+  "id" : "it.ra.title.confirm_delete",
+  "code" : "ra.title.confirm_delete",
+  "lang" : "it",
+  "text" : "Conferma"
+}, {
+  "id" : "it.ra.user.roles",
+  "code" : "ra.user.roles",
+  "lang" : "it",
+  "text" : "Ruoli"
+}, {
+  "id" : "it.ra.validation.email",
+  "code" : "ra.validation.email",
+  "lang" : "it",
+  "text" : "Deve essere un valido indirizzo email"
+}, {
+  "id" : "it.ra.validation.future_date",
+  "code" : "ra.validation.future_date",
+  "lang" : "it",
+  "text" : "La data deve essere successiva ad oggi"
+}, {
+  "id" : "it.ra.validation.maxLength",
+  "code" : "ra.validation.maxLength",
+  "lang" : "it",
+  "text" : "Deve essere lungo %{max} caratteri al massimo"
+}, {
+  "id" : "it.ra.validation.maxValue",
+  "code" : "ra.validation.maxValue",
+  "lang" : "it",
+  "text" : "Deve essere al massimo %{max}"
+}, {
+  "id" : "it.ra.validation.minLength",
+  "code" : "ra.validation.minLength",
+  "lang" : "it",
+  "text" : "Deve essere lungo %{min} caratteri almeno"
+}, {
+  "id" : "it.ra.validation.minValue",
+  "code" : "ra.validation.minValue",
+  "lang" : "it",
+  "text" : "Deve essere almeno %{min}"
+}, {
+  "id" : "it.ra.validation.number",
+  "code" : "ra.validation.number",
+  "lang" : "it",
+  "text" : "Deve essere un numero"
+}, {
+  "id" : "it.ra.validation.oneOf",
+  "code" : "ra.validation.oneOf",
+  "lang" : "it",
+  "text" : "Deve essere uno di: %{options}"
+}, {
+  "id" : "it.ra.validation.regex",
+  "code" : "ra.validation.regex",
+  "lang" : "it",
+  "text" : "Deve rispettare il formato (espressione regolare): %{pattern}"
+}, {
+  "id" : "it.ra.validation.required",
+  "code" : "ra.validation.required",
+  "lang" : "it",
+  "text" : "Campo obbligatorio"
+}, {
+  "id" : "it.ra.validation_summary.message.success",
+  "code" : "ra.validation_summary.message.success",
+  "lang" : "it",
+  "text" : "Tutte le informazioni presenti nella sezione corrente sono state inserite correttamente."
+}, {
+  "id" : "it.ra.yes",
+  "code" : "ra.yes",
+  "lang" : "it",
+  "text" : "Si"
+}, {
+  "id" : "it.reporting.stats.breakPredictions.line",
+  "code" : "reporting.stats.breakPredictions.line",
+  "lang" : "it",
+  "text" : "Soglia"
+}, {
+  "id" : "it.reporting.stats.breakPredictions.usageHours",
+  "code" : "reporting.stats.breakPredictions.usageHours",
+  "lang" : "it",
+  "text" : "Ore di utilizzo accumulate"
+}, {
+  "id" : "it.reporting.stats.filters.office",
+  "code" : "reporting.stats.filters.office",
+  "lang" : "it",
+  "text" : "Ufficio"
+}, {
+  "id" : "it.reporting.stats.loading",
+  "code" : "reporting.stats.loading",
+  "lang" : "it",
+  "text" : "Caricamento dati in corso..."
+}, {
+  "id" : "it.reporting.stats.predictions.accumulated",
+  "code" : "reporting.stats.predictions.accumulated",
+  "lang" : "it",
+  "text" : "Ore cumulate"
+}, {
+  "id" : "it.reporting.stats.predictions.break",
+  "code" : "reporting.stats.predictions.break",
+  "lang" : "it",
+  "text" : "Ore di lavoro accumulate"
+}, {
+  "id" : "it.reporting.stats.predictions.broken",
+  "code" : "reporting.stats.predictions.broken",
+  "lang" : "it",
+  "text" : "Previsione rottura"
+}, {
+  "id" : "it.reporting.stats.predictions.future",
+  "code" : "reporting.stats.predictions.future",
+  "lang" : "it",
+  "text" : "Proiezione"
+}, {
+  "id" : "it.reporting.stats.predictions.history",
+  "code" : "reporting.stats.predictions.history",
+  "lang" : "it",
+  "text" : "Storico"
+}, {
+  "id" : "it.reporting.stats.predictions.usageHours",
+  "code" : "reporting.stats.predictions.usageHours",
+  "lang" : "it",
+  "text" : "Ore di utilizzo"
+}, {
+  "id" : "it.reporting.stats.supply.average-monthly-usage.title",
+  "code" : "reporting.stats.supply.average-monthly-usage.title",
+  "lang" : "it",
+  "text" : "Numero di utilizzi mese attuale:"
+}, {
+  "id" : "it.reporting.stats.supply.monthly-usage-percentage.title",
+  "code" : "reporting.stats.supply.monthly-usage-percentage.title",
+  "lang" : "it",
+  "text" : "Utilizzo medio in questo mese:"
+}, {
+  "id" : "it.reporting.stats.supply.monthly-usage.title",
+  "code" : "reporting.stats.supply.monthly-usage.title",
+  "lang" : "it",
+  "text" : "Tempo di utilizzo in questo mese:"
+}, {
+  "id" : "it.reporting.stats.supply.yearly-usage.title",
+  "code" : "reporting.stats.supply.yearly-usage.title",
+  "lang" : "it",
+  "text" : "Tempo di utilizzo quest anno:"
+}, {
+  "id" : "it.reporting.stats.track.monthly-movement.title",
+  "code" : "reporting.stats.track.monthly-movement.title",
+  "lang" : "it",
+  "text" : "Numero spostamenti in questo mese"
+}, {
+  "id" : "it.reporting.stats.track.monthly-total-tracked-percentage.title",
+  "code" : "reporting.stats.track.monthly-total-tracked-percentage.title",
+  "lang" : "it",
+  "text" : "% Macchinari movimentati questo mese"
+}, {
+  "id" : "it.reporting.stats.track.total-monthly-tracked.title",
+  "code" : "reporting.stats.track.total-monthly-tracked.title",
+  "lang" : "it",
+  "text" : "Numero macchinari movimentati questo mese"
+}, {
+  "id" : "it.reporting.stats.track.yearly-movement.title",
+  "code" : "reporting.stats.track.yearly-movement.title",
+  "lang" : "it",
+  "text" : "Numero spostamenti quest’anno"
+}, {
+  "id" : "it.reporting.stats.unit.days",
+  "code" : "reporting.stats.unit.days",
+  "lang" : "it",
+  "text" : "Giorni"
+}, {
+  "id" : "it.reporting.stats.unit.hours",
+  "code" : "reporting.stats.unit.hours",
+  "lang" : "it",
+  "text" : "Ore"
+}, {
+  "id" : "it.reporting.stats.usageDays",
+  "code" : "reporting.stats.usageDays",
+  "lang" : "it",
+  "text" : "Giorni di Utilizzo"
+}, {
+  "id" : "it.reporting.stats.usageHours",
+  "code" : "reporting.stats.usageHours",
+  "lang" : "it",
+  "text" : "Ore di utilizzo"
+}, {
+  "id" : "it.resources.entities.supply.notifications.started",
+  "code" : "resources.entities.supply.notifications.started",
+  "lang" : "it",
+  "text" : "Stato: In Uso"
+}, {
+  "id" : "it.resources.entities.supply.notifications.stopped",
+  "code" : "resources.entities.supply.notifications.stopped",
+  "lang" : "it",
+  "text" : "Stato: Sospeso"
+}, {
+  "id" : "it.resources.entities/activity-type.breadcrumbs.create",
+  "code" : "resources.entities/activity-type.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Tipo Attività"
+}, {
+  "id" : "it.resources.entities/activity-type.breadcrumbs.edit",
+  "code" : "resources.entities/activity-type.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Tipo Attività"
+}, {
+  "id" : "it.resources.entities/activity-type.fields.attachment",
+  "code" : "resources.entities/activity-type.fields.attachment",
+  "lang" : "it",
+  "text" : "Allegato"
+}, {
+  "id" : "it.resources.entities/activity-type.fields.created",
+  "code" : "resources.entities/activity-type.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/activity-type.fields.description",
+  "code" : "resources.entities/activity-type.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/activity-type.fields.path",
+  "code" : "resources.entities/activity-type.fields.path",
+  "lang" : "it",
+  "text" : "Percorso"
+}, {
+  "id" : "it.resources.entities/activity-type.fields.updated",
+  "code" : "resources.entities/activity-type.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/activity-type.name",
+  "code" : "resources.entities/activity-type.name",
+  "lang" : "it",
+  "text" : "Tipo Attività |||| Tipi Attività"
+}, {
+  "id" : "it.resources.entities/activity-type.title",
+  "code" : "resources.entities/activity-type.title",
+  "lang" : "it",
+  "text" : "Tipo Attività"
+}, {
+  "id" : "it.resources.entities/activity.actions.backToFather",
+  "code" : "resources.entities/activity.actions.backToFather",
+  "lang" : "it",
+  "text" : "Vai all'Approvvigionamento"
+}, {
+  "id" : "it.resources.entities/activity.breadcrumbs.create",
+  "code" : "resources.entities/activity.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Attività"
+}, {
+  "id" : "it.resources.entities/activity.breadcrumbs.edit",
+  "code" : "resources.entities/activity.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Attività"
+}, {
+  "id" : "it.resources.entities/activity.fields.activityType.description",
+  "code" : "resources.entities/activity.fields.activityType.description",
+  "lang" : "it",
+  "text" : "Tipo Attività"
+}, {
+  "id" : "it.resources.entities/activity.fields.activityTypeId",
+  "code" : "resources.entities/activity.fields.activityTypeId",
+  "lang" : "it",
+  "text" : "Tipo Attività"
+}, {
+  "id" : "it.resources.entities/activity.fields.created",
+  "code" : "resources.entities/activity.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/activity.fields.customer.id",
+  "code" : "resources.entities/activity.fields.customer.id",
+  "lang" : "it",
+  "text" : "Cliente"
+}, {
+  "id" : "it.resources.entities/activity.fields.customer.name",
+  "code" : "resources.entities/activity.fields.customer.name",
+  "lang" : "it",
+  "text" : "Cliente"
+}, {
+  "id" : "it.resources.entities/activity.fields.date",
+  "code" : "resources.entities/activity.fields.date",
+  "lang" : "it",
+  "text" : "Data"
+}, {
+  "id" : "it.resources.entities/activity.fields.description",
+  "code" : "resources.entities/activity.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/activity.fields.status",
+  "code" : "resources.entities/activity.fields.status",
+  "lang" : "it",
+  "text" : "Stato"
+}, {
+  "id" : "it.resources.entities/activity.fields.supply.equipmentId",
+  "code" : "resources.entities/activity.fields.supply.equipmentId",
+  "lang" : "it",
+  "text" : "Approvvigionamento"
+}, {
+  "id" : "it.resources.entities/activity.fields.supply.supplierId",
+  "code" : "resources.entities/activity.fields.supply.supplierId",
+  "lang" : "it",
+  "text" : "Fornitore"
+}, {
+  "id" : "it.resources.entities/activity.fields.supplyId",
+  "code" : "resources.entities/activity.fields.supplyId",
+  "lang" : "it",
+  "text" : "Approvvigionamento"
+}, {
+  "id" : "it.resources.entities/activity.fields.title",
+  "code" : "resources.entities/activity.fields.title",
+  "lang" : "it",
+  "text" : "Titolo"
+}, {
+  "id" : "it.resources.entities/activity.fields.updated",
+  "code" : "resources.entities/activity.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/activity.fields.user.name",
+  "code" : "resources.entities/activity.fields.user.name",
+  "lang" : "it",
+  "text" : "Creato da"
+}, {
+  "id" : "it.resources.entities/activity.info_message",
+  "code" : "resources.entities/activity.info_message",
+  "lang" : "it",
+  "text" : "Questa attività si riferisce all'approvvigionamento <b>%{supply}</b> del fornitore <b>%{supplier}</b>  con tag RFID associato <b>%{location}</b>"
+}, {
+  "id" : "it.resources.entities/activity.name",
+  "code" : "resources.entities/activity.name",
+  "lang" : "it",
+  "text" : "Attività"
+}, {
+  "id" : "it.resources.entities/activity.title",
+  "code" : "resources.entities/activity.title",
+  "lang" : "it",
+  "text" : "Attività:"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.created",
+  "code" : "resources.entities/audit-log.fields.created",
+  "lang" : "it",
+  "text" : "Data e ora"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.from",
+  "code" : "resources.entities/audit-log.fields.from",
+  "lang" : "it",
+  "text" : "Dalla data"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.message",
+  "code" : "resources.entities/audit-log.fields.message",
+  "lang" : "it",
+  "text" : "Messaggio"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.to",
+  "code" : "resources.entities/audit-log.fields.to",
+  "lang" : "it",
+  "text" : "Alla data"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.userId",
+  "code" : "resources.entities/audit-log.fields.userId",
+  "lang" : "it",
+  "text" : "Utente"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.description",
+  "code" : "resources.entities/customer-area.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.name",
+  "code" : "resources.entities/customer-area.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.office.address",
+  "code" : "resources.entities/customer-area.fields.office.address",
+  "lang" : "it",
+  "text" : "Indirizzo"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.office.cityId",
+  "code" : "resources.entities/customer-area.fields.office.cityId",
+  "lang" : "it",
+  "text" : "Città"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.officeId",
+  "code" : "resources.entities/customer-area.fields.officeId",
+  "lang" : "it",
+  "text" : "Ufficio"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.rfidDeviceId",
+  "code" : "resources.entities/customer-area.fields.rfidDeviceId",
+  "lang" : "it",
+  "text" : "Dispositivo RFID"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.updated",
+  "code" : "resources.entities/customer-area.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/customer-area.name",
+  "code" : "resources.entities/customer-area.name",
+  "lang" : "it",
+  "text" : "Area |||| Aree"
+}, {
+  "id" : "it.resources.entities/customer-area.title.create",
+  "code" : "resources.entities/customer-area.title.create",
+  "lang" : "it",
+  "text" : "Nuova Area"
+}, {
+  "id" : "it.resources.entities/customer-area.title.edit",
+  "code" : "resources.entities/customer-area.title.edit",
+  "lang" : "it",
+  "text" : "Modifica Area"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.address",
+  "code" : "resources.entities/customer-office.fields.address",
+  "lang" : "it",
+  "text" : "Indirizzo"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.city.name",
+  "code" : "resources.entities/customer-office.fields.city.name",
+  "lang" : "it",
+  "text" : "Città"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.cityId",
+  "code" : "resources.entities/customer-office.fields.cityId",
+  "lang" : "it",
+  "text" : "Città"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.headquarter",
+  "code" : "resources.entities/customer-office.fields.headquarter",
+  "lang" : "it",
+  "text" : "Sede Legale"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.nation.name",
+  "code" : "resources.entities/customer-office.fields.nation.name",
+  "lang" : "it",
+  "text" : "Nazione"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.nationId",
+  "code" : "resources.entities/customer-office.fields.nationId",
+  "lang" : "it",
+  "text" : "Nazione"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.province.name",
+  "code" : "resources.entities/customer-office.fields.province.name",
+  "lang" : "it",
+  "text" : "Provincia"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.provinceId",
+  "code" : "resources.entities/customer-office.fields.provinceId",
+  "lang" : "it",
+  "text" : "Provincia"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.region.name",
+  "code" : "resources.entities/customer-office.fields.region.name",
+  "lang" : "it",
+  "text" : "Regione"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.regionId",
+  "code" : "resources.entities/customer-office.fields.regionId",
+  "lang" : "it",
+  "text" : "Regione"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.updated",
+  "code" : "resources.entities/customer-office.fields.updated",
+  "lang" : "it",
+  "text" : "Ultima Modifica"
+}, {
+  "id" : "it.resources.entities/customer-office.name",
+  "code" : "resources.entities/customer-office.name",
+  "lang" : "it",
+  "text" : "Sede |||| Sedi"
+}, {
+  "id" : "it.resources.entities/customer-office.title.create",
+  "code" : "resources.entities/customer-office.title.create",
+  "lang" : "it",
+  "text" : "Nuova Sede"
+}, {
+  "id" : "it.resources.entities/customer-office.title.edit",
+  "code" : "resources.entities/customer-office.title.edit",
+  "lang" : "it",
+  "text" : "Modifica Sede"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.email",
+  "code" : "resources.entities/customer-referent.fields.email",
+  "lang" : "it",
+  "text" : "E-mail"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.mobile",
+  "code" : "resources.entities/customer-referent.fields.mobile",
+  "lang" : "it",
+  "text" : "Telefono"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.name",
+  "code" : "resources.entities/customer-referent.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.office.cityId",
+  "code" : "resources.entities/customer-referent.fields.office.cityId",
+  "lang" : "it",
+  "text" : "Città"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.officeId",
+  "code" : "resources.entities/customer-referent.fields.officeId",
+  "lang" : "it",
+  "text" : "Sede"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.surname",
+  "code" : "resources.entities/customer-referent.fields.surname",
+  "lang" : "it",
+  "text" : "Cognome"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.unit",
+  "code" : "resources.entities/customer-referent.fields.unit",
+  "lang" : "it",
+  "text" : "Reparto"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.updated",
+  "code" : "resources.entities/customer-referent.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/customer-referent.name",
+  "code" : "resources.entities/customer-referent.name",
+  "lang" : "it",
+  "text" : "Referente |||| Referenti"
+}, {
+  "id" : "it.resources.entities/customer-referent.title.create",
+  "code" : "resources.entities/customer-referent.title.create",
+  "lang" : "it",
+  "text" : "Nuovo Referente"
+}, {
+  "id" : "it.resources.entities/customer-referent.title.edit",
+  "code" : "resources.entities/customer-referent.title.edit",
+  "lang" : "it",
+  "text" : "Modifica Referente"
+}, {
+  "id" : "it.resources.entities/customer.actions.create",
+  "code" : "resources.entities/customer.actions.create",
+  "lang" : "it",
+  "text" : "Aggiungi"
+}, {
+  "id" : "it.resources.entities/customer.breadcrumbs.edit",
+  "code" : "resources.entities/customer.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Cliente"
+}, {
+  "id" : "it.resources.entities/customer.fields.active",
+  "code" : "resources.entities/customer.fields.active",
+  "lang" : "it",
+  "text" : "Attivo"
+}, {
+  "id" : "it.resources.entities/customer.fields.name",
+  "code" : "resources.entities/customer.fields.name",
+  "lang" : "it",
+  "text" : "Ragione Sociale"
+}, {
+  "id" : "it.resources.entities/customer.fields.updated",
+  "code" : "resources.entities/customer.fields.updated",
+  "lang" : "it",
+  "text" : "Ultima Modifica"
+}, {
+  "id" : "it.resources.entities/customer.fields.vatCode",
+  "code" : "resources.entities/customer.fields.vatCode",
+  "lang" : "it",
+  "text" : "P.IVA"
+}, {
+  "id" : "it.resources.entities/customer.name",
+  "code" : "resources.entities/customer.name",
+  "lang" : "it",
+  "text" : "Cliente |||| Clienti"
+}, {
+  "id" : "it.resources.entities/customer.sections.offices",
+  "code" : "resources.entities/customer.sections.offices",
+  "lang" : "it",
+  "text" : "Sedi"
+}, {
+  "id" : "it.resources.entities/customer.sections.referents",
+  "code" : "resources.entities/customer.sections.referents",
+  "lang" : "it",
+  "text" : "Referenti"
+}, {
+  "id" : "it.resources.entities/customer.tabs.areas",
+  "code" : "resources.entities/customer.tabs.areas",
+  "lang" : "it",
+  "text" : "Aree"
+}, {
+  "id" : "it.resources.entities/customer.tabs.details",
+  "code" : "resources.entities/customer.tabs.details",
+  "lang" : "it",
+  "text" : "Generali"
+}, {
+  "id" : "it.resources.entities/customer.tabs.offices",
+  "code" : "resources.entities/customer.tabs.offices",
+  "lang" : "it",
+  "text" : "Uffici"
+}, {
+  "id" : "it.resources.entities/customer.tabs.referents",
+  "code" : "resources.entities/customer.tabs.referents",
+  "lang" : "it",
+  "text" : "Referenti"
+}, {
+  "id" : "it.resources.entities/customer.title.create",
+  "code" : "resources.entities/customer.title.create",
+  "lang" : "it",
+  "text" : "Nuovo Cliente"
+}, {
+  "id" : "it.resources.entities/customer.title.edit",
+  "code" : "resources.entities/customer.title.edit",
+  "lang" : "it",
+  "text" : "Modifica cliente %{name}"
+}, {
+  "id" : "it.resources.entities/device.alert",
+  "code" : "resources.entities/device.alert",
+  "lang" : "it",
+  "text" : "Di seguito Ã¨ presente l'elenco dei dispositivi registrati per cui Ã¨ disponibile il login con pin."
+}, {
+  "id" : "it.resources.entities/device.breadcrumbs.create",
+  "code" : "resources.entities/device.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Nuovo Dispositivo"
+}, {
+  "id" : "it.resources.entities/device.breadcrumbs.edit",
+  "code" : "resources.entities/device.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Dettaglio Dispositivo"
+}, {
+  "id" : "it.resources.entities/device.fields.code",
+  "code" : "resources.entities/device.fields.code",
+  "lang" : "it",
+  "text" : "Codice Univoco"
+}, {
+  "id" : "it.resources.entities/device.fields.registrationDate",
+  "code" : "resources.entities/device.fields.registrationDate",
+  "lang" : "it",
+  "text" : "Data di registrazione"
+}, {
+  "id" : "it.resources.entities/device.fields.secret",
+  "code" : "resources.entities/device.fields.secret",
+  "lang" : "it",
+  "text" : "Segreto condiviso"
+}, {
+  "id" : "it.resources.entities/device.name",
+  "code" : "resources.entities/device.name",
+  "lang" : "it",
+  "text" : "Dispositivo |||| Dispositivi"
+}, {
+  "id" : "it.resources.entities/device.title",
+  "code" : "resources.entities/device.title",
+  "lang" : "it",
+  "text" : "Dettagli Dispositivo"
+}, {
+  "id" : "it.resources.entities/docx-template.breadcrumbs.create",
+  "code" : "resources.entities/docx-template.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea nuovo Modulo"
+}, {
+  "id" : "it.resources.entities/docx-template.breadcrumbs.edit",
+  "code" : "resources.entities/docx-template.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Modulo"
+}, {
+  "id" : "it.resources.entities/docx-template.edit",
+  "code" : "resources.entities/docx-template.edit",
+  "lang" : "it",
+  "text" : "Modifica Modulo"
+}, {
+  "id" : "it.resources.entities/docx-template.fields.attachment",
+  "code" : "resources.entities/docx-template.fields.attachment",
+  "lang" : "it",
+  "text" : "Template"
+}, {
+  "id" : "it.resources.entities/docx-template.fields.name",
+  "code" : "resources.entities/docx-template.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/docx-template.fields.updated",
+  "code" : "resources.entities/docx-template.fields.updated",
+  "lang" : "it",
+  "text" : "Ultima Modifica"
+}, {
+  "id" : "it.resources.entities/docx-template.name",
+  "code" : "resources.entities/docx-template.name",
+  "lang" : "it",
+  "text" : "Modulistica"
+}, {
+  "id" : "it.resources.entities/docx-template.title.create",
+  "code" : "resources.entities/docx-template.title.create",
+  "lang" : "it",
+  "text" : "Nuovo Modulo"
+}, {
+  "id" : "it.resources.entities/docx-template.title.edit",
+  "code" : "resources.entities/docx-template.title.edit",
+  "lang" : "it",
+  "text" : "Modifica Modulo"
+}, {
+  "id" : "it.resources.entities/docx-templates.fields.type.link",
+  "code" : "resources.entities/docx-templates.fields.type.link",
+  "lang" : "it",
+  "text" : "Per ottenere la documentazione aggiornata sulle variabili disponibili per la progettazione del template che hai selezionato <a href=\"%{url}\">clicca qui</a>"
+}, {
+  "id" : "it.resources.entities/equipment-attachment.fields.attachment",
+  "code" : "resources.entities/equipment-attachment.fields.attachment",
+  "lang" : "it",
+  "text" : "Allegato"
+}, {
+  "id" : "it.resources.entities/equipment-attachment.fields.created",
+  "code" : "resources.entities/equipment-attachment.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/equipment-attachment.fields.description",
+  "code" : "resources.entities/equipment-attachment.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/equipment-attachment.fields.updated",
+  "code" : "resources.entities/equipment-attachment.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/equipment-attachment.name",
+  "code" : "resources.entities/equipment-attachment.name",
+  "lang" : "it",
+  "text" : "Allegato Attrezzatura |||| Allegati Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-attachment.title",
+  "code" : "resources.entities/equipment-attachment.title",
+  "lang" : "it",
+  "text" : "Allegato Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.fields.attachment",
+  "code" : "resources.entities/equipment-type-attachment.fields.attachment",
+  "lang" : "it",
+  "text" : "Allegato"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.fields.created",
+  "code" : "resources.entities/equipment-type-attachment.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.fields.description",
+  "code" : "resources.entities/equipment-type-attachment.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.fields.updated",
+  "code" : "resources.entities/equipment-type-attachment.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.name",
+  "code" : "resources.entities/equipment-type-attachment.name",
+  "lang" : "it",
+  "text" : "Allegato Tipo Attrezzatura |||| Allegati Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.title",
+  "code" : "resources.entities/equipment-type-attachment.title",
+  "lang" : "it",
+  "text" : "Allegato Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.title.list",
+  "code" : "resources.entities/equipment-type-attachment.title.list",
+  "lang" : "it",
+  "text" : "Allegati Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.breadcrumbs.create",
+  "code" : "resources.entities/equipment-type.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.breadcrumbs.edit",
+  "code" : "resources.entities/equipment-type.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.accessory",
+  "code" : "resources.entities/equipment-type.fields.accessory",
+  "lang" : "it",
+  "text" : "Accessorio"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.attachment",
+  "code" : "resources.entities/equipment-type.fields.attachment",
+  "lang" : "it",
+  "text" : "Allegato"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.breakpointInHours.help",
+  "code" : "resources.entities/equipment-type.fields.breakpointInHours.help",
+  "lang" : "it",
+  "text" : "Indicare il numero di ore garantite di utilizzo oltre le quali l'apparecchiatura può essere soggetta a rottura."
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.created",
+  "code" : "resources.entities/equipment-type.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.name",
+  "code" : "resources.entities/equipment-type.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.updated",
+  "code" : "resources.entities/equipment-type.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/equipment-type.name",
+  "code" : "resources.entities/equipment-type.name",
+  "lang" : "it",
+  "text" : "Tipo Attrezzatura |||| Tipi Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.section",
+  "code" : "resources.entities/equipment-type.section",
+  "lang" : "it",
+  "text" : "Dati aggiuntivi"
+}, {
+  "id" : "it.resources.entities/equipment-type.title",
+  "code" : "resources.entities/equipment-type.title",
+  "lang" : "it",
+  "text" : "Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.title.list",
+  "code" : "resources.entities/equipment-type.title.list",
+  "lang" : "it",
+  "text" : "Sotto-Tipo Attrezzature"
+}, {
+  "id" : "it.resources.entities/equipment.actions.backToFather",
+  "code" : "resources.entities/equipment.actions.backToFather",
+  "lang" : "it",
+  "text" : "Vai al Fornitore"
+}, {
+  "id" : "it.resources.entities/equipment.breadcrumbs.create",
+  "code" : "resources.entities/equipment.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment.breadcrumbs.edit",
+  "code" : "resources.entities/equipment.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment.fields.created",
+  "code" : "resources.entities/equipment.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/equipment.fields.equipmentType.name",
+  "code" : "resources.entities/equipment.fields.equipmentType.name",
+  "lang" : "it",
+  "text" : "Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment.fields.equipmentTypeId",
+  "code" : "resources.entities/equipment.fields.equipmentTypeId",
+  "lang" : "it",
+  "text" : "Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment.fields.name",
+  "code" : "resources.entities/equipment.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/equipment.fields.supplier.businessName",
+  "code" : "resources.entities/equipment.fields.supplier.businessName",
+  "lang" : "it",
+  "text" : "Fornitore"
+}, {
+  "id" : "it.resources.entities/equipment.fields.supplierId",
+  "code" : "resources.entities/equipment.fields.supplierId",
+  "lang" : "it",
+  "text" : "Fornitore"
+}, {
+  "id" : "it.resources.entities/equipment.fields.updated",
+  "code" : "resources.entities/equipment.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/equipment.name",
+  "code" : "resources.entities/equipment.name",
+  "lang" : "it",
+  "text" : "Attrezzatura |||| Attrezzature"
+}, {
+  "id" : "it.resources.entities/equipment.title",
+  "code" : "resources.entities/equipment.title",
+  "lang" : "it",
+  "text" : "Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipmentType.actions.backToFather",
+  "code" : "resources.entities/equipmentType.actions.backToFather",
+  "lang" : "it",
+  "text" : "Torna Indietro"
+}, {
+  "id" : "it.resources.entities/i18n-message.breadcrumbs.create",
+  "code" : "resources.entities/i18n-message.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Nuovo Messaggio"
+}, {
+  "id" : "it.resources.entities/i18n-message.breadcrumbs.edit",
+  "code" : "resources.entities/i18n-message.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Messaggio"
+}, {
+  "id" : "it.resources.entities/i18n-message.fields.code",
+  "code" : "resources.entities/i18n-message.fields.code",
+  "lang" : "it",
+  "text" : "Codice"
+}, {
+  "id" : "it.resources.entities/i18n-message.fields.lang",
+  "code" : "resources.entities/i18n-message.fields.lang",
+  "lang" : "it",
+  "text" : "Lingua"
+}, {
+  "id" : "it.resources.entities/i18n-message.fields.text",
+  "code" : "resources.entities/i18n-message.fields.text",
+  "lang" : "it",
+  "text" : "Testo"
+}, {
+  "id" : "it.resources.entities/i18n-message.fields.translated",
+  "code" : "resources.entities/i18n-message.fields.translated",
+  "lang" : "it",
+  "text" : "Tradotto"
+}, {
+  "id" : "it.resources.entities/i18n-message.name",
+  "code" : "resources.entities/i18n-message.name",
+  "lang" : "it",
+  "text" : "Messaggi (i18n)"
+}, {
+  "id" : "it.resources.entities/i18n-message.title",
+  "code" : "resources.entities/i18n-message.title",
+  "lang" : "it",
+  "text" : "Messaggio Localizzato"
+}, {
+  "id" : "it.resources.entities/maintenance.actions.backToFather",
+  "code" : "resources.entities/maintenance.actions.backToFather",
+  "lang" : "it",
+  "text" : "Vai all'Attività"
+}, {
+  "id" : "it.resources.entities/maintenance.breadcrumbs.create",
+  "code" : "resources.entities/maintenance.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Manutenzione"
+}, {
+  "id" : "it.resources.entities/maintenance.breadcrumbs.edit",
+  "code" : "resources.entities/maintenance.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Manutenzione"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.activity.title",
+  "code" : "resources.entities/maintenance.fields.activity.title",
+  "lang" : "it",
+  "text" : "Attività"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.activityId",
+  "code" : "resources.entities/maintenance.fields.activityId",
+  "lang" : "it",
+  "text" : "Attività"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.created",
+  "code" : "resources.entities/maintenance.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.date",
+  "code" : "resources.entities/maintenance.fields.date",
+  "lang" : "it",
+  "text" : "Data"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.description",
+  "code" : "resources.entities/maintenance.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.equipmentTypeId",
+  "code" : "resources.entities/maintenance.fields.equipmentTypeId",
+  "lang" : "it",
+  "text" : "Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.interventionNumber",
+  "code" : "resources.entities/maintenance.fields.interventionNumber",
+  "lang" : "it",
+  "text" : "Numero Intervento"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.interventions",
+  "code" : "resources.entities/maintenance.fields.interventions",
+  "lang" : "it",
+  "text" : "Interventi"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.interventions.description",
+  "code" : "resources.entities/maintenance.fields.interventions.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.interventions.equipmentTypeId",
+  "code" : "resources.entities/maintenance.fields.interventions.equipmentTypeId",
+  "lang" : "it",
+  "text" : "Approvvigionamento"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.interventions.reorder",
+  "code" : "resources.entities/maintenance.fields.interventions.reorder",
+  "lang" : "it",
+  "text" : "Riordinato"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.interventions.substitution",
+  "code" : "resources.entities/maintenance.fields.interventions.substitution",
+  "lang" : "it",
+  "text" : "Sostituto"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.protocol.protocolNumber",
+  "code" : "resources.entities/maintenance.fields.protocol.protocolNumber",
+  "lang" : "it",
+  "text" : "Numero di Protocollo"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.protocolNumber",
+  "code" : "resources.entities/maintenance.fields.protocolNumber",
+  "lang" : "it",
+  "text" : "Numero di Protocollo"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.reorder",
+  "code" : "resources.entities/maintenance.fields.reorder",
+  "lang" : "it",
+  "text" : "Riordinato"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.substitution",
+  "code" : "resources.entities/maintenance.fields.substitution",
+  "lang" : "it",
+  "text" : "Sostituto"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.supplier.businessName",
+  "code" : "resources.entities/maintenance.fields.supplier.businessName",
+  "lang" : "it",
+  "text" : "Data"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.title",
+  "code" : "resources.entities/maintenance.fields.title",
+  "lang" : "it",
+  "text" : "Titolo"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.updated",
+  "code" : "resources.entities/maintenance.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/maintenance.fields.user.name",
+  "code" : "resources.entities/maintenance.fields.user.name",
+  "lang" : "it",
+  "text" : "Creato da"
+}, {
+  "id" : "it.resources.entities/maintenance.name",
+  "code" : "resources.entities/maintenance.name",
+  "lang" : "it",
+  "text" : "Manutenzione |||| Manutenzioni"
+}, {
+  "id" : "it.resources.entities/maintenance.title",
+  "code" : "resources.entities/maintenance.title",
+  "lang" : "it",
+  "text" : "Manutenzione"
+}, {
+  "id" : "it.resources.entities/notification.name",
+  "code" : "resources.entities/notification.name",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.resources.entities/price-list-item.fields.created",
+  "code" : "resources.entities/price-list-item.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/price-list-item.fields.equipment.equipmentTypeId",
+  "code" : "resources.entities/price-list-item.fields.equipment.equipmentTypeId",
+  "lang" : "it",
+  "text" : "Attrezzatura"
+}, {
+  "id" : "it.resources.entities/price-list-item.fields.equipmentId",
+  "code" : "resources.entities/price-list-item.fields.equipmentId",
+  "lang" : "it",
+  "text" : "Attrezzatura"
+}, {
+  "id" : "it.resources.entities/price-list-item.fields.equipmentType.name",
+  "code" : "resources.entities/price-list-item.fields.equipmentType.name",
+  "lang" : "it",
+  "text" : "Attrezzatura"
+}, {
+  "id" : "it.resources.entities/price-list-item.fields.price",
+  "code" : "resources.entities/price-list-item.fields.price",
+  "lang" : "it",
+  "text" : "Prezzo"
+}, {
+  "id" : "it.resources.entities/price-list-item.fields.updated",
+  "code" : "resources.entities/price-list-item.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/price-list-item.name",
+  "code" : "resources.entities/price-list-item.name",
+  "lang" : "it",
+  "text" : "Listino Prezzi |||| Listini Prezzi"
+}, {
+  "id" : "it.resources.entities/price-list-item.title",
+  "code" : "resources.entities/price-list-item.title",
+  "lang" : "it",
+  "text" : "Prezzo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/price-list-item.title.list",
+  "code" : "resources.entities/price-list-item.title.list",
+  "lang" : "it",
+  "text" : "Listino Prezzi"
+}, {
+  "id" : "it.resources.entities/price-list.actions.back-to-supplier",
+  "code" : "resources.entities/price-list.actions.back-to-supplier",
+  "lang" : "it",
+  "text" : "Vai al Fornitore"
+}, {
+  "id" : "it.resources.entities/price-list.fields.created",
+  "code" : "resources.entities/price-list.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/price-list.fields.end",
+  "code" : "resources.entities/price-list.fields.end",
+  "lang" : "it",
+  "text" : "Data Fine"
+}, {
+  "id" : "it.resources.entities/price-list.fields.name",
+  "code" : "resources.entities/price-list.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/price-list.fields.start",
+  "code" : "resources.entities/price-list.fields.start",
+  "lang" : "it",
+  "text" : "Data Inizio"
+}, {
+  "id" : "it.resources.entities/price-list.fields.updated",
+  "code" : "resources.entities/price-list.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/price-list.name",
+  "code" : "resources.entities/price-list.name",
+  "lang" : "it",
+  "text" : "Listino |||| Listini"
+}, {
+  "id" : "it.resources.entities/price-list.title",
+  "code" : "resources.entities/price-list.title",
+  "lang" : "it",
+  "text" : "Listino"
+}, {
+  "id" : "it.resources.entities/protocol-attachment.fields.attachment",
+  "code" : "resources.entities/protocol-attachment.fields.attachment",
+  "lang" : "it",
+  "text" : "Allegato"
+}, {
+  "id" : "it.resources.entities/protocol-attachment.fields.created",
+  "code" : "resources.entities/protocol-attachment.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/protocol-attachment.fields.description",
+  "code" : "resources.entities/protocol-attachment.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/protocol-attachment.fields.updated",
+  "code" : "resources.entities/protocol-attachment.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/protocol-attachment.name",
+  "code" : "resources.entities/protocol-attachment.name",
+  "lang" : "it",
+  "text" : "Allegato Protocollo |||| Allegati Protocollo"
+}, {
+  "id" : "it.resources.entities/protocol-attachment.title",
+  "code" : "resources.entities/protocol-attachment.title",
+  "lang" : "it",
+  "text" : "Allegati Protocollo"
+}, {
+  "id" : "it.resources.entities/protocol-service.title",
+  "code" : "resources.entities/protocol-service.title",
+  "lang" : "it",
+  "text" : "Servizi Offerti"
+}, {
+  "id" : "it.resources.entities/protocol.breadcrumbs.create",
+  "code" : "resources.entities/protocol.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Protocollo"
+}, {
+  "id" : "it.resources.entities/protocol.breadcrumbs.edit",
+  "code" : "resources.entities/protocol.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Protocollo"
+}, {
+  "id" : "it.resources.entities/protocol.fields.created",
+  "code" : "resources.entities/protocol.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/protocol.fields.customer.name",
+  "code" : "resources.entities/protocol.fields.customer.name",
+  "lang" : "it",
+  "text" : "Cliente"
+}, {
+  "id" : "it.resources.entities/protocol.fields.customerId",
+  "code" : "resources.entities/protocol.fields.customerId",
+  "lang" : "it",
+  "text" : "Cliente"
+}, {
+  "id" : "it.resources.entities/protocol.fields.description",
+  "code" : "resources.entities/protocol.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/protocol.fields.protocolDate",
+  "code" : "resources.entities/protocol.fields.protocolDate",
+  "lang" : "it",
+  "text" : "Data di Protocollo"
+}, {
+  "id" : "it.resources.entities/protocol.fields.protocolNumber",
+  "code" : "resources.entities/protocol.fields.protocolNumber",
+  "lang" : "it",
+  "text" : "Numero di Protocollo"
+}, {
+  "id" : "it.resources.entities/protocol.fields.title",
+  "code" : "resources.entities/protocol.fields.title",
+  "lang" : "it",
+  "text" : "Titolo"
+}, {
+  "id" : "it.resources.entities/protocol.fields.updated",
+  "code" : "resources.entities/protocol.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/protocol.name",
+  "code" : "resources.entities/protocol.name",
+  "lang" : "it",
+  "text" : "Protocollo |||| Protocolli"
+}, {
+  "id" : "it.resources.entities/protocol.services.end",
+  "code" : "resources.entities/protocol.services.end",
+  "lang" : "it",
+  "text" : "Scadenza"
+}, {
+  "id" : "it.resources.entities/protocol.services.frequency",
+  "code" : "resources.entities/protocol.services.frequency",
+  "lang" : "it",
+  "text" : "Frequenza"
+}, {
+  "id" : "it.resources.entities/protocol.services.fromActivityTypeId",
+  "code" : "resources.entities/protocol.services.fromActivityTypeId",
+  "lang" : "it",
+  "text" : "Tipo Attività Sorgente"
+}, {
+  "id" : "it.resources.entities/protocol.services.toActivityTypeId",
+  "code" : "resources.entities/protocol.services.toActivityTypeId",
+  "lang" : "it",
+  "text" : "Tipo Attività (Destinazione)"
+}, {
+  "id" : "it.resources.entities/protocol.tabs.details",
+  "code" : "resources.entities/protocol.tabs.details",
+  "lang" : "it",
+  "text" : "Info Generali"
+}, {
+  "id" : "it.resources.entities/protocol.title",
+  "code" : "resources.entities/protocol.title",
+  "lang" : "it",
+  "text" : "Protocollo"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.fields.additionalData",
+  "code" : "resources.entities/rfid-device-track.fields.additionalData",
+  "lang" : "it",
+  "text" : "Dati Aggiuntivi"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.fields.areaId",
+  "code" : "resources.entities/rfid-device-track.fields.areaId",
+  "lang" : "it",
+  "text" : "Area"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.fields.from",
+  "code" : "resources.entities/rfid-device-track.fields.from",
+  "lang" : "it",
+  "text" : "Dalla data"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.fields.gateway.name",
+  "code" : "resources.entities/rfid-device-track.fields.gateway.name",
+  "lang" : "it",
+  "text" : "Gateway"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.fields.supplyId",
+  "code" : "resources.entities/rfid-device-track.fields.supplyId",
+  "lang" : "it",
+  "text" : "Approvvigionamento"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.fields.tag.name",
+  "code" : "resources.entities/rfid-device-track.fields.tag.name",
+  "lang" : "it",
+  "text" : "Tag"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.fields.to",
+  "code" : "resources.entities/rfid-device-track.fields.to",
+  "lang" : "it",
+  "text" : "Alla data"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.fields.ts",
+  "code" : "resources.entities/rfid-device-track.fields.ts",
+  "lang" : "it",
+  "text" : "Registrato il"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.name",
+  "code" : "resources.entities/rfid-device-track.name",
+  "lang" : "it",
+  "text" : "Tracciamento RFID |||| Tracciamento RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.tabs.list",
+  "code" : "resources.entities/rfid-device-track.tabs.list",
+  "lang" : "it",
+  "text" : "Log Posizioni"
+}, {
+  "id" : "it.resources.entities/rfid-device-track.tutorial.description",
+  "code" : "resources.entities/rfid-device-track.tutorial.description",
+  "lang" : "it",
+  "text" : ""
+}, {
+  "id" : "it.resources.entities/rfid-device-track.tutorial.title",
+  "code" : "resources.entities/rfid-device-track.tutorial.title",
+  "lang" : "it",
+  "text" : "Tracciamento RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device.breadcrumbs.create",
+  "code" : "resources.entities/rfid-device.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device.breadcrumbs.edit",
+  "code" : "resources.entities/rfid-device.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device.fields.code",
+  "code" : "resources.entities/rfid-device.fields.code",
+  "lang" : "it",
+  "text" : "Codice Seriale"
+}, {
+  "id" : "it.resources.entities/rfid-device.fields.name",
+  "code" : "resources.entities/rfid-device.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/rfid-device.fields.type",
+  "code" : "resources.entities/rfid-device.fields.type",
+  "lang" : "it",
+  "text" : "Tipo"
+}, {
+  "id" : "it.resources.entities/rfid-device.fields.updated",
+  "code" : "resources.entities/rfid-device.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/rfid-device.name",
+  "code" : "resources.entities/rfid-device.name",
+  "lang" : "it",
+  "text" : "RFID |||| RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device.title.create",
+  "code" : "resources.entities/rfid-device.title.create",
+  "lang" : "it",
+  "text" : "Nuovo RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device.title.edit",
+  "code" : "resources.entities/rfid-device.title.edit",
+  "lang" : "it",
+  "text" : "Modifica RFID"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.created",
+  "code" : "resources.entities/supplier-referent.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.department",
+  "code" : "resources.entities/supplier-referent.fields.department",
+  "lang" : "it",
+  "text" : "Dipartimento"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.email",
+  "code" : "resources.entities/supplier-referent.fields.email",
+  "lang" : "it",
+  "text" : "Email"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.name",
+  "code" : "resources.entities/supplier-referent.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.phone",
+  "code" : "resources.entities/supplier-referent.fields.phone",
+  "lang" : "it",
+  "text" : "Numero di Telefono"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.surname",
+  "code" : "resources.entities/supplier-referent.fields.surname",
+  "lang" : "it",
+  "text" : "Cognome"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.updated",
+  "code" : "resources.entities/supplier-referent.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/supplier-referent.name",
+  "code" : "resources.entities/supplier-referent.name",
+  "lang" : "it",
+  "text" : "Referente Fornitore |||| Referenti Fornitore"
+}, {
+  "id" : "it.resources.entities/supplier-referent.title",
+  "code" : "resources.entities/supplier-referent.title",
+  "lang" : "it",
+  "text" : "Referenti Fornitore"
+}, {
+  "id" : "it.resources.entities/supplier.actions.create",
+  "code" : "resources.entities/supplier.actions.create",
+  "lang" : "it",
+  "text" : "Aggiungi"
+}, {
+  "id" : "it.resources.entities/supplier.breadcrumbs.create",
+  "code" : "resources.entities/supplier.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Fornitore"
+}, {
+  "id" : "it.resources.entities/supplier.breadcrumbs.edit",
+  "code" : "resources.entities/supplier.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Fornitore"
+}, {
+  "id" : "it.resources.entities/supplier.fields.address",
+  "code" : "resources.entities/supplier.fields.address",
+  "lang" : "it",
+  "text" : "Indirizzo"
+}, {
+  "id" : "it.resources.entities/supplier.fields.businessName",
+  "code" : "resources.entities/supplier.fields.businessName",
+  "lang" : "it",
+  "text" : "Ragione Sociale"
+}, {
+  "id" : "it.resources.entities/supplier.fields.city.name",
+  "code" : "resources.entities/supplier.fields.city.name",
+  "lang" : "it",
+  "text" : "Comune"
+}, {
+  "id" : "it.resources.entities/supplier.fields.cityId",
+  "code" : "resources.entities/supplier.fields.cityId",
+  "lang" : "it",
+  "text" : "Comune"
+}, {
+  "id" : "it.resources.entities/supplier.fields.created",
+  "code" : "resources.entities/supplier.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/supplier.fields.updated",
+  "code" : "resources.entities/supplier.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/supplier.fields.vatNumber",
+  "code" : "resources.entities/supplier.fields.vatNumber",
+  "lang" : "it",
+  "text" : "Partita IVA"
+}, {
+  "id" : "it.resources.entities/supplier.name",
+  "code" : "resources.entities/supplier.name",
+  "lang" : "it",
+  "text" : "Fornitore |||| Fornitori"
+}, {
+  "id" : "it.resources.entities/supplier.tabs.equipment",
+  "code" : "resources.entities/supplier.tabs.equipment",
+  "lang" : "it",
+  "text" : "Attrezzatura"
+}, {
+  "id" : "it.resources.entities/supplier.tabs.price-list",
+  "code" : "resources.entities/supplier.tabs.price-list",
+  "lang" : "it",
+  "text" : "Listino"
+}, {
+  "id" : "it.resources.entities/supplier.title",
+  "code" : "resources.entities/supplier.title",
+  "lang" : "it",
+  "text" : "Fornitore"
+}, {
+  "id" : "it.resources.entities/supply.actions.backToFather",
+  "code" : "resources.entities/supply.actions.backToFather",
+  "lang" : "it",
+  "text" : "Torna Indietro"
+}, {
+  "id" : "it.resources.entities/supply.alert",
+  "code" : "resources.entities/supply.alert",
+  "lang" : "it",
+  "text" : "Di seguito trovi le info relative all'approvvigionamento"
+}, {
+  "id" : "it.resources.entities/supply.breadcrumbs.create",
+  "code" : "resources.entities/supply.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Approvvigionamento"
+}, {
+  "id" : "it.resources.entities/supply.breadcrumbs.edit",
+  "code" : "resources.entities/supply.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Approvvigionamento"
+}, {
+  "id" : "it.resources.entities/supply.fields.area.name",
+  "code" : "resources.entities/supply.fields.area.name",
+  "lang" : "it",
+  "text" : "Area"
+}, {
+  "id" : "it.resources.entities/supply.fields.area.officeId",
+  "code" : "resources.entities/supply.fields.area.officeId",
+  "lang" : "it",
+  "text" : "Ufficio"
+}, {
+  "id" : "it.resources.entities/supply.fields.areaId",
+  "code" : "resources.entities/supply.fields.areaId",
+  "lang" : "it",
+  "text" : "Area"
+}, {
+  "id" : "it.resources.entities/supply.fields.breakpointInHours",
+  "code" : "resources.entities/supply.fields.breakpointInHours",
+  "lang" : "it",
+  "text" : "Ore di funzionamento"
+}, {
+  "id" : "it.resources.entities/supply.fields.breakpointInHours.help",
+  "code" : "resources.entities/supply.fields.breakpointInHours.help",
+  "lang" : "it",
+  "text" : "Indicare il numero di ore garantite di utilizzo oltre le quali l'apparecchiatura può essere soggetta a rottura."
+}, {
+  "id" : "it.resources.entities/supply.fields.created",
+  "code" : "resources.entities/supply.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/supply.fields.customer.name",
+  "code" : "resources.entities/supply.fields.customer.name",
+  "lang" : "it",
+  "text" : "Cliente"
+}, {
+  "id" : "it.resources.entities/supply.fields.customerName",
+  "code" : "resources.entities/supply.fields.customerName",
+  "lang" : "it",
+  "text" : "Cliente"
+}, {
+  "id" : "it.resources.entities/supply.fields.ddtNumber",
+  "code" : "resources.entities/supply.fields.ddtNumber",
+  "lang" : "it",
+  "text" : "Numero Documento di Consegna"
+}, {
+  "id" : "it.resources.entities/supply.fields.deliveryDate",
+  "code" : "resources.entities/supply.fields.deliveryDate",
+  "lang" : "it",
+  "text" : "Data Consegna"
+}, {
+  "id" : "it.resources.entities/supply.fields.duration",
+  "code" : "resources.entities/supply.fields.duration",
+  "lang" : "it",
+  "text" : "Tempo di utilizzo"
+}, {
+  "id" : "it.resources.entities/supply.fields.duration.running",
+  "code" : "resources.entities/supply.fields.duration.running",
+  "lang" : "it",
+  "text" : "Ancora in uso"
+}, {
+  "id" : "it.resources.entities/supply.fields.equipment.equipmentTypeId",
+  "code" : "resources.entities/supply.fields.equipment.equipmentTypeId",
+  "lang" : "it",
+  "text" : "Attrezzatura"
+}, {
+  "id" : "it.resources.entities/supply.fields.equipment.name",
+  "code" : "resources.entities/supply.fields.equipment.name",
+  "lang" : "it",
+  "text" : "Attrezzatura"
+}, {
+  "id" : "it.resources.entities/supply.fields.equipment.supplierId",
+  "code" : "resources.entities/supply.fields.equipment.supplierId",
+  "lang" : "it",
+  "text" : "Fornitore"
+}, {
+  "id" : "it.resources.entities/supply.fields.equipmentId",
+  "code" : "resources.entities/supply.fields.equipmentId",
+  "lang" : "it",
+  "text" : "Attrezzatura"
+}, {
+  "id" : "it.resources.entities/supply.fields.equipmentType.name",
+  "code" : "resources.entities/supply.fields.equipmentType.name",
+  "lang" : "it",
+  "text" : "Attrezzatura"
+}, {
+  "id" : "it.resources.entities/supply.fields.from",
+  "code" : "resources.entities/supply.fields.from",
+  "lang" : "it",
+  "text" : "Data Inizio"
+}, {
+  "id" : "it.resources.entities/supply.fields.orderDate",
+  "code" : "resources.entities/supply.fields.orderDate",
+  "lang" : "it",
+  "text" : "Data Ordine"
+}, {
+  "id" : "it.resources.entities/supply.fields.protocol.description",
+  "code" : "resources.entities/supply.fields.protocol.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/supply.fields.protocol.protocolDate",
+  "code" : "resources.entities/supply.fields.protocol.protocolDate",
+  "lang" : "it",
+  "text" : "Data di Protocollo"
+}, {
+  "id" : "it.resources.entities/supply.fields.protocol.protocolNumber",
+  "code" : "resources.entities/supply.fields.protocol.protocolNumber",
+  "lang" : "it",
+  "text" : "Numero di Protocollo"
+}, {
+  "id" : "it.resources.entities/supply.fields.protocol.title",
+  "code" : "resources.entities/supply.fields.protocol.title",
+  "lang" : "it",
+  "text" : "Titolo"
+}, {
+  "id" : "it.resources.entities/supply.fields.protocolId",
+  "code" : "resources.entities/supply.fields.protocolId",
+  "lang" : "it",
+  "text" : "Protocollo"
+}, {
+  "id" : "it.resources.entities/supply.fields.quantity",
+  "code" : "resources.entities/supply.fields.quantity",
+  "lang" : "it",
+  "text" : "Quantità"
+}, {
+  "id" : "it.resources.entities/supply.fields.rfidDevice",
+  "code" : "resources.entities/supply.fields.rfidDevice",
+  "lang" : "it",
+  "text" : "Tag RFID"
+}, {
+  "id" : "it.resources.entities/supply.fields.rfidDevice.name",
+  "code" : "resources.entities/supply.fields.rfidDevice.name",
+  "lang" : "it",
+  "text" : "Dispositivo RFID"
+}, {
+  "id" : "it.resources.entities/supply.fields.rfidDeviceId",
+  "code" : "resources.entities/supply.fields.rfidDeviceId",
+  "lang" : "it",
+  "text" : "Tag RFID"
+}, {
+  "id" : "it.resources.entities/supply.fields.serialNumber",
+  "code" : "resources.entities/supply.fields.serialNumber",
+  "lang" : "it",
+  "text" : "Numero Seriale"
+}, {
+  "id" : "it.resources.entities/supply.fields.supplier.businessName",
+  "code" : "resources.entities/supply.fields.supplier.businessName",
+  "lang" : "it",
+  "text" : "Fornitore"
+}, {
+  "id" : "it.resources.entities/supply.fields.supplierId",
+  "code" : "resources.entities/supply.fields.supplierId",
+  "lang" : "it",
+  "text" : "Fornitore"
+}, {
+  "id" : "it.resources.entities/supply.fields.to",
+  "code" : "resources.entities/supply.fields.to",
+  "lang" : "it",
+  "text" : "Data Fine"
+}, {
+  "id" : "it.resources.entities/supply.fields.updated",
+  "code" : "resources.entities/supply.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/supply.fields.usageStatus",
+  "code" : "resources.entities/supply.fields.usageStatus",
+  "lang" : "it",
+  "text" : "Stato"
+}, {
+  "id" : "it.resources.entities/supply.group.usage",
+  "code" : "resources.entities/supply.group.usage",
+  "lang" : "it",
+  "text" : "Utilizzi"
+}, {
+  "id" : "it.resources.entities/supply.info_message",
+  "code" : "resources.entities/supply.info_message",
+  "lang" : "it",
+  "text" : "Questo approvvigionamento appartiene al fornitore <b>%{supplier}</b> relativo all'attrezzatura <b>%{equipment}</b>  con il protocollo <b>%{protocol}</b>"
+}, {
+  "id" : "it.resources.entities/supply.name",
+  "code" : "resources.entities/supply.name",
+  "lang" : "it",
+  "text" : "Approvvigionamento |||| Approvvigionamenti"
+}, {
+  "id" : "it.resources.entities/supply.rfid-device-last-position",
+  "code" : "resources.entities/supply.rfid-device-last-position",
+  "lang" : "it",
+  "text" : "Area: %{area}, Data e ora: %{ts}"
+}, {
+  "id" : "it.resources.entities/supply.rfid-device-last-position.title",
+  "code" : "resources.entities/supply.rfid-device-last-position.title",
+  "lang" : "it",
+  "text" : "<b>Ultima posizione registrata</b>"
+}, {
+  "id" : "it.resources.entities/supply.show.activity",
+  "code" : "resources.entities/supply.show.activity",
+  "lang" : "it",
+  "text" : "Storico Attività"
+}, {
+  "id" : "it.resources.entities/supply.show.equipment-attachment",
+  "code" : "resources.entities/supply.show.equipment-attachment",
+  "lang" : "it",
+  "text" : "Allegati Attrezzatura"
+}, {
+  "id" : "it.resources.entities/supply.show.protocol-info",
+  "code" : "resources.entities/supply.show.protocol-info",
+  "lang" : "it",
+  "text" : "Info Protocollo"
+}, {
+  "id" : "it.resources.entities/supply.show.register-maintenance",
+  "code" : "resources.entities/supply.show.register-maintenance",
+  "lang" : "it",
+  "text" : "Registra una Manutenzione"
+}, {
+  "id" : "it.resources.entities/supply.show.request-assistance",
+  "code" : "resources.entities/supply.show.request-assistance",
+  "lang" : "it",
+  "text" : "Richiedi Assistenza"
+}, {
+  "id" : "it.resources.entities/supply.show.status-running",
+  "code" : "resources.entities/supply.show.status-running",
+  "lang" : "it",
+  "text" : "Sospendi"
+}, {
+  "id" : "it.resources.entities/supply.show.status-stopped",
+  "code" : "resources.entities/supply.show.status-stopped",
+  "lang" : "it",
+  "text" : "Avvia"
+}, {
+  "id" : "it.resources.entities/supply.show.supply-info",
+  "code" : "resources.entities/supply.show.supply-info",
+  "lang" : "it",
+  "text" : "Info Approvvigionamento"
+}, {
+  "id" : "it.resources.entities/supply.show.usage-info",
+  "code" : "resources.entities/supply.show.usage-info",
+  "lang" : "it",
+  "text" : "Registro Utilizzi"
+}, {
+  "id" : "it.resources.entities/supply.tabs.activity",
+  "code" : "resources.entities/supply.tabs.activity",
+  "lang" : "it",
+  "text" : "Storico Attività"
+}, {
+  "id" : "it.resources.entities/supply.tabs.details",
+  "code" : "resources.entities/supply.tabs.details",
+  "lang" : "it",
+  "text" : "Info Generali"
+}, {
+  "id" : "it.resources.entities/supply.tabs.equipment-attachment",
+  "code" : "resources.entities/supply.tabs.equipment-attachment",
+  "lang" : "it",
+  "text" : "Allegati Attrezzatura"
+}, {
+  "id" : "it.resources.entities/supply.tabs.protocol",
+  "code" : "resources.entities/supply.tabs.protocol",
+  "lang" : "it",
+  "text" : "Protocollo"
+}, {
+  "id" : "it.resources.entities/supply.tabs.rfid-device-track",
+  "code" : "resources.entities/supply.tabs.rfid-device-track",
+  "lang" : "it",
+  "text" : "Log Posizioni"
+}, {
+  "id" : "it.resources.entities/supply.tabs.supply",
+  "code" : "resources.entities/supply.tabs.supply",
+  "lang" : "it",
+  "text" : "Approvvigionamento "
+}, {
+  "id" : "it.resources.entities/supply.tabs.usage-chart",
+  "code" : "resources.entities/supply.tabs.usage-chart",
+  "lang" : "it",
+  "text" : "Grafico Utilizzi"
+}, {
+  "id" : "it.resources.entities/supply.title",
+  "code" : "resources.entities/supply.title",
+  "lang" : "it",
+  "text" : "Approvvigionamento"
+}, {
+  "id" : "it.resources.entities/usage.name",
+  "code" : "resources.entities/usage.name",
+  "lang" : "it",
+  "text" : "Utilizzo |||| Utilizzi"
+}, {
+  "id" : "it.resources.entities/usage.tabs.chart",
+  "code" : "resources.entities/usage.tabs.chart",
+  "lang" : "it",
+  "text" : "Statistica Tempo di Utilizzo"
+}, {
+  "id" : "it.resources.entities/usage.tabs.chart.breakPredictions",
+  "code" : "resources.entities/usage.tabs.chart.breakPredictions",
+  "lang" : "it",
+  "text" : "Previsione utilizzo e rottura"
+}, {
+  "id" : "it.resources.entities/usage.tabs.chart.no-data-description",
+  "code" : "resources.entities/usage.tabs.chart.no-data-description",
+  "lang" : "it",
+  "text" : "Nessun dato disponibile con i filtri selezionati"
+}, {
+  "id" : "it.resources.entities/usage.tabs.chart.predictions",
+  "code" : "resources.entities/usage.tabs.chart.predictions",
+  "lang" : "it",
+  "text" : "Predizione ore di funzionamento future"
+}, {
+  "id" : "it.resources.entities/usage.tabs.record",
+  "code" : "resources.entities/usage.tabs.record",
+  "lang" : "it",
+  "text" : "Registro Utilizzi"
+}, {
+  "id" : "it.resources.entities/user.breadcrumbs.create",
+  "code" : "resources.entities/user.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Nuovo Utente"
+}, {
+  "id" : "it.resources.entities/user.breadcrumbs.edit",
+  "code" : "resources.entities/user.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Utente"
+}, {
+  "id" : "it.resources.entities/user.fields.active",
+  "code" : "resources.entities/user.fields.active",
+  "lang" : "it",
+  "text" : "Attivo"
+}, {
+  "id" : "it.resources.entities/user.fields.customerId",
+  "code" : "resources.entities/user.fields.customerId",
+  "lang" : "it",
+  "text" : "Cliente"
+}, {
+  "id" : "it.resources.entities/user.fields.image",
+  "code" : "resources.entities/user.fields.image",
+  "lang" : "it",
+  "text" : "Immagine di profilo"
+}, {
+  "id" : "it.resources.entities/user.fields.name",
+  "code" : "resources.entities/user.fields.name",
+  "lang" : "it",
+  "text" : "Nome e Cognome"
+}, {
+  "id" : "it.resources.entities/user.fields.registrationDate",
+  "code" : "resources.entities/user.fields.registrationDate",
+  "lang" : "it",
+  "text" : "Data di registrazione"
+}, {
+  "id" : "it.resources.entities/user.fields.role",
+  "code" : "resources.entities/user.fields.role",
+  "lang" : "it",
+  "text" : "Ruolo"
+}, {
+  "id" : "it.resources.entities/user.name",
+  "code" : "resources.entities/user.name",
+  "lang" : "it",
+  "text" : "Utente |||| Utenti"
+}, {
+  "id" : "it.resources.entities/user.title.create",
+  "code" : "resources.entities/user.title.create",
+  "lang" : "it",
+  "text" : "Nuovo Utente"
+}, {
+  "id" : "it.resources.entities/user.title.edit",
+  "code" : "resources.entities/user.title.edit",
+  "lang" : "it",
+  "text" : "Modifica utente %{name}"
+}, {
+  "id" : "it.resources.notifications.empty",
+  "code" : "resources.notifications.empty",
+  "lang" : "it",
+  "text" : "Ancora nessuna notifica!"
+}, {
+  "id" : "it.resources.notifications.messages.readed.done",
+  "code" : "resources.notifications.messages.readed.done",
+  "lang" : "it",
+  "text" : "Notifiche segnate come lette."
+}, {
+  "id" : "it.resources.notifications.messages.unreaded.done",
+  "code" : "resources.notifications.messages.unreaded.done",
+  "lang" : "it",
+  "text" : "Notifiche segnate come non lette."
+}, {
+  "id" : "it.resources.transactions.fields.id",
+  "code" : "resources.transactions.fields.id",
+  "lang" : "it",
+  "text" : "#ID"
+}, {
+  "id" : "it.resources.transactions.fields.notes.show_less",
+  "code" : "resources.transactions.fields.notes.show_less",
+  "lang" : "it",
+  "text" : "Mostra meno"
+}, {
+  "id" : "it.resources.transactions.fields.state",
+  "code" : "resources.transactions.fields.state",
+  "lang" : "it",
+  "text" : "Stato"
+}, {
+  "id" : "it.resources.user/change-password.fields.newPassword",
+  "code" : "resources.user/change-password.fields.newPassword",
+  "lang" : "it",
+  "text" : "Nuova Password"
+}, {
+  "id" : "it.resources.user/change-password.fields.oldPassword",
+  "code" : "resources.user/change-password.fields.oldPassword",
+  "lang" : "it",
+  "text" : "Password precedente"
+}, {
+  "id" : "it.resources.user/change-password.title",
+  "code" : "resources.user/change-password.title",
+  "lang" : "it",
+  "text" : "Cambio Password"
+}, {
+  "id" : "it.test",
+  "code" : "test",
+  "lang" : "it",
+  "text" : "test"
+}, {
+  "id" : "it.tracking.stats.rfid.title",
+  "code" : "tracking.stats.rfid.title",
+  "lang" : "it",
+  "text" : "Ultime Posizioni Registrate"
+} ]
\ No newline at end of file
diff --git a/edera-api/api/src/test/java/applica/app/test/TestApplication.java b/edera-api/api/src/test/java/applica/app/test/TestApplication.java
new file mode 100644 (file)
index 0000000..ab388fa
--- /dev/null
@@ -0,0 +1,14 @@
+package applica.app.test;
+
+import applica.app.Application;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
+import org.springframework.context.annotation.ComponentScan;
+
+@DataMongoTest
+@ComponentScan({ "applica.app.test.fixture", "applica.app.test" })
+public class TestApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}
diff --git a/edera-api/api/src/test/java/applica/app/test/TestDataFactory.java b/edera-api/api/src/test/java/applica/app/test/TestDataFactory.java
new file mode 100644 (file)
index 0000000..1569b9a
--- /dev/null
@@ -0,0 +1,36 @@
+package applica.app.test;
+
+import applica.iam.domain.Profile;
+import applica.iam.domain.Role;
+import applica.iam.domain.User;
+import applica.iam.domain.UserId;
+import org.springframework.util.DigestUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class TestDataFactory {
+
+    public static User createUser(String password) {
+        var userId = UserId.create();
+        var pinCode = DigestUtils.md5DigestAsHex(UUID.randomUUID().toString().substring(0, 4).getBytes());
+        return new User(
+                userId,
+                "testuser" + System.currentTimeMillis() + "@gmail.com",
+                password,
+                true,
+                0,
+                null,
+                new Date(),
+                UUID.randomUUID().toString(),
+                false,
+                true,
+                List.of(Role.USER),
+                new Profile(Map.of("name", "Test user")),
+                pinCode
+        );
+    }
+
+}
diff --git a/edera-api/api/src/test/java/applica/app/test/configuration/TestConfiguration.java b/edera-api/api/src/test/java/applica/app/test/configuration/TestConfiguration.java
new file mode 100644 (file)
index 0000000..a85ea0b
--- /dev/null
@@ -0,0 +1,14 @@
+package applica.app.test.configuration;
+
+import org.mockito.Mockito;
+import org.springframework.context.annotation.Bean;
+import org.springframework.mail.MailSender;
+
+public class TestConfiguration {
+
+    @Bean
+    public MailSender mailSender() {
+        return Mockito.mock(MailSender.class);
+    }
+
+}
diff --git a/edera-api/api/src/test/java/applica/app/test/domain/docx/DocxBuilderTest.java b/edera-api/api/src/test/java/applica/app/test/domain/docx/DocxBuilderTest.java
new file mode 100644 (file)
index 0000000..53ed32c
--- /dev/null
@@ -0,0 +1,66 @@
+package applica.app.test.domain.docx;
+
+import applica.app.domain.docx.DocxBuilder;
+import applica.app.domain.docx.DocxDataSource;
+import applica.app.domain.docx.DocxException;
+import org.junit.jupiter.api.*;
+
+import java.io.FileOutputStream;
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class DocxBuilderTest {
+    @Test
+    public void testGenerate() {
+
+        var dataSource = new DocxDataSource();
+        var row = dataSource.createRow();
+        row.put("created", new Date().toString());
+        row.put("number", "1");
+        row.put("supply_name", "Test supply");
+        row.put("activity_type_name", "Test activity type");
+        row.put("description", "Test description");
+        row.put("article", "Test article");
+        row.put("reordered", "No");
+        row.put("replaced", "Yes");
+
+        row = dataSource.createRow();
+        row.put("created", new Date().toString());
+        row.put("number", "2");
+        row.put("supply_name", "Test supply 2");
+        row.put("activity_type_name", "Test activity type 2");
+        row.put("description", "Test description 2");
+        row.put("article", "Test article 2");
+        row.put("reordered", "Yes");
+        row.put("replaced", "No");
+
+        assertDoesNotThrow(() -> exec("sample-google-workspace", dataSource));
+        assertDoesNotThrow(() -> exec("sample-ms-office", dataSource));
+        assertDoesNotThrow(() -> exec("sample-open-office", dataSource));
+        assertThrows(DocxException.class, () -> exec("sample-ms-office-open-xml", dataSource));
+    }
+
+    private void exec(String template, DocxDataSource dataSource) throws DocxException {
+
+        var jarPath = DocxBuilder.class.getProtectionDomain().getCodeSource().getLocation().getPath();
+        var cleanPath = jarPath.replace("/target/classes/", "");
+        var rtfOut = String.format("%s/src/test/resources/out-%s.docx", cleanPath, template);
+        var rtfIn = String.format("%s/src/test/resources/%s.docx", cleanPath, template);
+
+        try {
+            var builder = new DocxBuilder();
+            var outputStream = builder.generate(rtfIn, dataSource);
+            assertNotNull(outputStream);
+
+            var rtfOutStream = new FileOutputStream(rtfOut);
+            rtfOutStream.write(outputStream.readAllBytes());
+            rtfOutStream.close();
+        }
+        catch (Exception e) {
+            throw new DocxException(e.getMessage());
+        }
+    }
+}
diff --git a/edera-api/api/src/test/java/applica/app/test/endtoend/IAMControllerTest.java b/edera-api/api/src/test/java/applica/app/test/endtoend/IAMControllerTest.java
new file mode 100644 (file)
index 0000000..93672ae
--- /dev/null
@@ -0,0 +1,245 @@
+package applica.app.test.endtoend;
+
+import applica.app.Application;
+import applica.app.test.TestDataFactory;
+import applica.app.test.configuration.TestConfiguration;
+import applica.iam.domain.UsersRepository;
+import applica.iam.sdk.ResponseCodes;
+import applica.iam.sdk.requests.PinLoginRequest;
+import applica.iam.sdk.requests.RegisterDeviceRequest;
+import applica.iam.sdk.responses.PinLoginResponse;
+import applica.iam.sdk.responses.RegisterDeviceResponse;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.RestDocumentationExtension;
+import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.util.StringUtils;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@SpringBootTest(classes = {
+        TestConfiguration.class,
+        Application.class
+})
+@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@ActiveProfiles("test")
+public class IAMControllerTest {
+    private ObjectMapper mapper = new ObjectMapper();
+    private MockMvc mvc;
+    @Autowired
+    private UsersRepository usersRepository;
+
+    @Value("${app.test}")
+    boolean test;
+
+    @BeforeAll
+    public void setUp(WebApplicationContext webApplicationContext) {
+        mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
+                .apply(SecurityMockMvcConfigurers.springSecurity())
+                .build();
+    }
+
+    @Test
+    @DisplayName("Test profile")
+    public void testProfile() {
+        assertTrue(test);
+    }
+
+    @Test
+    @DisplayName("Should correctly register")
+    public void register() throws Exception {
+        var name = "test" + System.currentTimeMillis();
+        mvc
+                .perform(
+                        post("/iam/register")
+                                .param("name", name)
+                                .param("email", name + "@test.com")
+                                .param("password", "testtest")
+                )
+                .andExpect(status().isOk());
+    }
+    @Test
+    @DisplayName("Should not login with incorrect credentials")
+    public void login() throws Exception {
+        mvc
+                .perform(
+                    post("/iam/login")
+                            .param("mail", "bimbobruno@gmail.com")
+                            .param("password", "testtest")
+                )
+                .andExpect(status().isUnauthorized());
+    }
+
+    @Test
+    @DisplayName("Should register and access to a protected url")
+    public void registerAndAccessProtectedUrl() throws Exception {
+        var name = "test" + String.valueOf(System.currentTimeMillis());
+        var email = name + "@test.com";
+        var password = "testtest";
+        mvc
+                .perform(
+                        post("/iam/register")
+                                .param("name", name)
+                                .param("email", email)
+                                .param("password", password)
+                )
+                .andExpect(status().isOk());
+
+        var user = usersRepository.findByUsername(email).orElseThrow();
+        mvc
+                .perform(
+                        post("/iam/activate")
+                                .param("activationCode", user.getActivationCode())
+                )
+                .andExpect(status().isOk());
+
+        var mapper = new ObjectMapper();
+        var loginResponse = mapper.readValue(mvc
+                .perform(
+                        post("/iam/login")
+                                .param("mail", email)
+                                .param("password", password)
+                )
+                .andExpect(status().isOk())
+                .andReturn()
+                .getResponse()
+                .getContentAsByteArray(), ObjectNode.class);
+
+        mvc
+                .perform(
+                        get("/tests/protected/user")
+                )
+                .andExpect(status().isForbidden());
+
+        mvc
+                .perform(
+                        get("/tests/protected/user")
+                                .header("Authorization", "Bearer " + loginResponse.get("token").asText())
+                )
+                .andExpect(status().isOk());
+
+        mvc
+                .perform(
+                        get("/tests/protected/admin")
+                                .header("Authorization", "Bearer " + loginResponse.get("token").asText())
+                )
+                .andExpect(status().isForbidden());
+    }
+
+    @Test
+    @DisplayName("Should login with token")
+    public void tokenLogin() throws Exception {
+        var name = "test" + String.valueOf(System.currentTimeMillis());
+        var email = name + "@test.com";
+        var password = "testtest";
+        mvc
+                .perform(
+                        post("/iam/register")
+                                .param("name", name)
+                                .param("email", email)
+                                .param("password", password)
+                )
+                .andExpect(status().isOk());
+
+        var user = usersRepository.findByUsername(email).orElseThrow();
+        mvc
+                .perform(
+                        post("/iam/activate")
+                                .param("activationCode", user.getActivationCode())
+                )
+                .andExpect(status().isOk());
+
+        var mapper = new ObjectMapper();
+        var loginResponse = mapper.readValue(mvc
+                .perform(
+                        post("/iam/login")
+                                .param("mail", email)
+                                .param("password", password)
+                )
+                .andExpect(status().isOk())
+                .andReturn()
+                .getResponse()
+                .getContentAsByteArray(), ObjectNode.class);
+
+        assertNotNull(loginResponse.get("token"));
+
+        mvc
+                .perform(
+                        post("/iam/token-login")
+                                .param("token", loginResponse.get("token").asText())
+                )
+                .andExpect(status().isOk());
+
+    }
+
+    @Test
+    @SneakyThrows
+    @DisplayName("Should register device")
+    public void testRegisterDevice() {
+        var deviceCode = UUID.randomUUID().toString();
+        var registerDeviceRequest = new RegisterDeviceRequest(deviceCode);
+        var registerDeviceResponse = mvc.perform(post("/iam/register-device")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(mapper.writeValueAsString(registerDeviceRequest)))
+                .andExpect(status().isOk())
+                .andReturn()
+                .getResponse()
+                .getContentAsString();
+
+        var registerDeviceResponseAsObject = mapper.readValue(registerDeviceResponse, RegisterDeviceResponse.class);
+        assertEquals(ResponseCodes.OK, registerDeviceResponseAsObject.getResponseCode());
+        assertNotNull(registerDeviceResponseAsObject.getDevice());
+        assertTrue(StringUtils.hasLength(registerDeviceResponseAsObject.getDevice().getSecret()));
+    }
+
+    @Test
+    @SneakyThrows
+    @DisplayName("Double device registration should fail")
+    public void testDoubleDeviceRegistration() {
+        var deviceCode = UUID.randomUUID().toString();
+        var device1Request = new RegisterDeviceRequest(deviceCode);
+        var device1Response = mvc.perform(post("/iam/register-device")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(mapper.writeValueAsString(device1Request)))
+                .andExpect(status().isOk())
+                .andReturn()
+                .getResponse()
+                .getContentAsString();
+
+        var device1 = mapper.readValue(device1Response, RegisterDeviceResponse.class);
+        assertEquals(ResponseCodes.OK, device1.getResponseCode());
+        assertNotNull(device1.getDevice());
+        assertTrue(StringUtils.hasLength(device1.getDevice().getSecret()));
+
+        var device2Response = mvc.perform(post("/iam/register-device")
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(mapper.writeValueAsString(device1Request)))
+                .andExpect(status().isOk())
+                .andReturn()
+                .getResponse()
+                .getContentAsString();
+
+        var device2 = mapper.readValue(device2Response, RegisterDeviceResponse.class);
+        assertEquals(device2.getDevice().getId(), device1.getDevice().getId());
+    }
+
+}
diff --git a/edera-api/api/src/test/java/applica/app/test/endtoend/RFIDDeviceTrackControllerTest.java b/edera-api/api/src/test/java/applica/app/test/endtoend/RFIDDeviceTrackControllerTest.java
new file mode 100644 (file)
index 0000000..e5c9410
--- /dev/null
@@ -0,0 +1,102 @@
+package applica.app.test.endtoend;
+
+import applica.app.Application;
+import applica.app.test.TestApplication;
+import applica.app.test.configuration.TestConfiguration;
+import applica.app.test.fixture.AreaFixture;
+import applica.app.test.fixture.RFIDDeviceFixture;
+import applica.app.test.fixture.SupplyFixture;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.requests.LoginRequest;
+import com.github.javafaker.Faker;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.restdocs.RestDocumentationExtension;
+import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.util.Base64;
+
+import static applica.iam.sdk.ResponseCodes.OK;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@SpringBootTest(classes = {
+        TestConfiguration.class,
+        TestApplication.class,
+        Application.class
+})
+@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+@ActiveProfiles("test")
+public class RFIDDeviceTrackControllerTest {
+
+    private MockMvc mvc;
+    @Autowired
+    private IAMService iamService;
+
+    @Autowired
+    private SupplyFixture supplyFixture;
+    @Autowired
+    private AreaFixture areaFixture;
+    @Autowired
+    private RFIDDeviceFixture rfidDeviceFixture;
+
+    @BeforeAll
+    public void setUp(WebApplicationContext webApplicationContext) {
+        mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
+                .apply(SecurityMockMvcConfigurers.springSecurity())
+                .build();
+        rfidDeviceFixture.setUp();
+        areaFixture.setUp();
+        supplyFixture.setUp();
+    }
+
+    @AfterAll
+    public void tearDown() {
+        rfidDeviceFixture.tearDown();
+        areaFixture.tearDown();
+        supplyFixture.tearDown();
+    }
+
+    @Test
+    @DisplayName("Should save a RFIDDeviceTrack")
+    public void testSaveARFIDDeviceTrack() throws Exception {
+        var fakeTag = rfidDeviceFixture.get(0);
+        var fakeGateway = rfidDeviceFixture.get(1);
+
+        mvc.perform(get(String.format("/track?tag=%s&timestamp=%d&gate=%s",
+                        fakeTag.getCode(),
+                        System.currentTimeMillis(),
+                        fakeGateway.getCode())))
+                .andExpect(status().isOk());
+
+    }
+
+    @Test
+    @DisplayName("Should save a RFIDDeviceTrack with params")
+    public void testSaveARFIDDeviceTrackWithParams() throws Exception {
+        var fakeTag = rfidDeviceFixture.get(0);
+        var fakeGateway = rfidDeviceFixture.get(1);
+        var faker = new Faker();
+
+        mvc.perform(get(String.format("/track?tag=%s&timestamp=%d&gate=%s&imei=%s&dragonball_character=%s",
+                        fakeTag.getCode(),
+                        System.currentTimeMillis(),
+                        fakeGateway.getCode(),
+                        faker.code().imei(),
+                        faker.dragonBall().character())))
+                .andExpect(status().isOk());
+
+    }
+}
diff --git a/edera-api/api/src/test/java/applica/app/test/fixture/AreaFixture.java b/edera-api/api/src/test/java/applica/app/test/fixture/AreaFixture.java
new file mode 100644 (file)
index 0000000..2e00d8d
--- /dev/null
@@ -0,0 +1,39 @@
+package applica.app.test.fixture;
+
+import applica.app.domain.customer.Area;
+import applica.crud.factory.CrudFactory;
+import com.github.javafaker.Faker;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class AreaFixture extends Fixture<Area>{
+
+    final RFIDDeviceFixture rfidDeviceFixture;
+    public AreaFixture(CrudFactory crudFactory, RFIDDeviceFixture rfidDeviceFixture) {
+        super(crudFactory.createDataLayer(Area.class));
+        this.rfidDeviceFixture = rfidDeviceFixture;
+    }
+
+    @Override
+    public List<Area> prepareData() {
+        var list = new ArrayList<Area>();
+        var faker = new Faker();
+        var area = new Area();
+        area.setName(faker.name().name());
+        area.setDescription(faker.lorem().sentence());
+        area.setCustomerId(faker.idNumber().validSvSeSsn());
+        area.setOfficeId(faker.idNumber().validSvSeSsn());
+        area.setRfidDeviceId(rfidDeviceFixture.get(1).getId());
+        list.add(area);
+
+        return list;
+    }
+
+    @Override
+    public Object getId(Area entity) {
+        return entity.getId();
+    }
+}
diff --git a/edera-api/api/src/test/java/applica/app/test/fixture/Fixture.java b/edera-api/api/src/test/java/applica/app/test/fixture/Fixture.java
new file mode 100644 (file)
index 0000000..a8afdf8
--- /dev/null
@@ -0,0 +1,59 @@
+package applica.app.test.fixture;
+
+import applica.crud.datalayer.DataLayer;
+import lombok.Getter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Assert;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public abstract class Fixture<T> extends ArrayList<T> {
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Getter
+    private DataLayer<T> dataLayer;
+
+    private boolean initialized = false;
+
+    @Override
+    public T get(int index) {
+        if (!initialized) {
+            setUp();
+        }
+        return super.get(index);
+    }
+
+    public Fixture(DataLayer<T> dataLayer) {
+        this.dataLayer = dataLayer;
+    }
+
+    public abstract List<T> prepareData();
+
+    public abstract Object getId(T entity);
+
+    public void setUp() {
+        if (initialized) {
+            return;
+        }
+        logger.debug("Setting up {}", getClass().getName());
+        var entitiesToCreate = prepareData();
+        for (var entity : entitiesToCreate) {
+            dataLayer.save(entity);
+            add(entity);
+        }
+        Assert.isTrue(entitiesToCreate.size() == size(), "Entities created are not the same of entities added to fixture");
+        initialized = true;
+    }
+
+    public void tearDown() {
+        logger.debug("Tearing down {}", getClass().getName());
+        for (var entity : this) {
+            dataLayer.delete(getId(entity));
+        }
+        clear();
+        initialized = false;
+    }
+}
\ No newline at end of file
diff --git a/edera-api/api/src/test/java/applica/app/test/fixture/RFIDDeviceFixture.java b/edera-api/api/src/test/java/applica/app/test/fixture/RFIDDeviceFixture.java
new file mode 100644 (file)
index 0000000..454dd38
--- /dev/null
@@ -0,0 +1,39 @@
+package applica.app.test.fixture;
+
+import applica.app.domain.rfid.RFIDDevice;
+import applica.app.domain.rfid.RFIDDeviceType;
+import applica.crud.factory.CrudFactory;
+import com.github.javafaker.Faker;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class RFIDDeviceFixture extends Fixture<RFIDDevice>{
+    public RFIDDeviceFixture(CrudFactory crudFactory) {
+        super(crudFactory.createDataLayer(RFIDDevice.class));
+    }
+
+    @Override
+    public List<RFIDDevice> prepareData() {
+        var faker = new Faker();
+        var list = new ArrayList<RFIDDevice>();
+        var tag = new RFIDDevice();
+        tag.setCode("123456789");
+        tag.setType(RFIDDeviceType.TAG);
+        tag.setName(faker.name().name());
+        list.add(tag);
+        var gateway = new RFIDDevice();
+        gateway.setCode("987654321");
+        gateway.setType(RFIDDeviceType.GATEWAY);
+        gateway.setName(faker.name().name());
+        list.add(gateway);
+        return list;
+    }
+
+    @Override
+    public Object getId(RFIDDevice entity) {
+        return entity.getId();
+    }
+}
diff --git a/edera-api/api/src/test/java/applica/app/test/fixture/SupplyFixture.java b/edera-api/api/src/test/java/applica/app/test/fixture/SupplyFixture.java
new file mode 100644 (file)
index 0000000..1d4d670
--- /dev/null
@@ -0,0 +1,63 @@
+package applica.app.test.fixture;
+
+import applica.app.domain.supply.Supply;
+import applica.app.domain.supply.UsageStatus;
+import applica.crud.factory.CrudFactory;
+import com.github.javafaker.Faker;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class SupplyFixture extends Fixture<Supply>{
+
+    final RFIDDeviceFixture rfidDeviceFixture;
+    final AreaFixture areaFixture;
+    public SupplyFixture(CrudFactory crudFactory, RFIDDeviceFixture rfidDeviceFixture, AreaFixture areaFixture) {
+        super(crudFactory.createDataLayer(Supply.class));
+        this.rfidDeviceFixture = rfidDeviceFixture;
+        this.areaFixture = areaFixture;
+    }
+
+    @Override
+    public List<Supply> prepareData() {
+        var list = new ArrayList<Supply>();
+        var faker = new Faker();
+        var supplyTag = new Supply();
+        supplyTag.setRfidDeviceId(rfidDeviceFixture.get(0).getId());
+        supplyTag.setUsageId(faker.idNumber().validSvSeSsn());
+        supplyTag.setDdtNumber(faker.numerify("DDT-###"));
+        supplyTag.setUsageStatus(UsageStatus.STOPPED);
+        supplyTag.setDeliveryDate(LocalDate.now());
+        supplyTag.setEquipmentId(faker.idNumber().validSvSeSsn());
+        supplyTag.setSupplierId(faker.idNumber().validSvSeSsn());
+        supplyTag.setQuantity(faker.number().randomDigit());
+        supplyTag.setProtocolId(faker.idNumber().validSvSeSsn());
+        supplyTag.setOrderDate(LocalDate.now());
+        supplyTag.setSerialNumber(faker.numerify("SN-###"));
+        list.add(supplyTag);
+
+        var supplyArea = new Supply();
+        supplyArea.setUsageId(faker.idNumber().validSvSeSsn());
+        supplyArea.setDdtNumber(faker.numerify("DDT-###"));
+        supplyArea.setUsageStatus(UsageStatus.STOPPED);
+        supplyArea.setDeliveryDate(LocalDate.now());
+        supplyArea.setEquipmentId(faker.idNumber().validSvSeSsn());
+        supplyArea.setSupplierId(faker.idNumber().validSvSeSsn());
+        supplyArea.setQuantity(faker.number().randomDigit());
+        supplyArea.setAreaId(areaFixture.get(0).getId());
+        supplyArea.setProtocolId(faker.idNumber().validSvSeSsn());
+        supplyArea.setOrderDate(LocalDate.now());
+        supplyArea.setSerialNumber(faker.numerify("SN-###"));
+        list.add(supplyArea);
+
+        return list;
+    }
+
+    @Override
+    public Object getId(Supply entity) {
+        return entity.getId();
+    }
+}
diff --git a/edera-api/api/src/test/resources/i18n.json b/edera-api/api/src/test/resources/i18n.json
new file mode 100644 (file)
index 0000000..c82094e
--- /dev/null
@@ -0,0 +1,1721 @@
+[ {
+  "id" : "it.Forbidden",
+  "code" : "Forbidden",
+  "lang" : "it",
+  "text" : "Accesso non consentito"
+}, {
+  "id" : "it.app.date_format.long",
+  "code" : "app.date_format.long",
+  "lang" : "it",
+  "text" : "DD/MM/YYYY, HH:mm:ss"
+}, {
+  "id" : "it.app.input.max_length_info",
+  "code" : "app.input.max_length_info",
+  "lang" : "it",
+  "text" : "Usati %{count} su %{max} caratteri disponibili (spazi inclusi)"
+}, {
+  "id" : "it.app.label.day",
+  "code" : "app.label.day",
+  "lang" : "it",
+  "text" : "Data"
+}, {
+  "id" : "it.app.label.end_at",
+  "code" : "app.label.end_at",
+  "lang" : "it",
+  "text" : "Alla data"
+}, {
+  "id" : "it.app.welcome",
+  "code" : "app.welcome",
+  "lang" : "it",
+  "text" : "Benvenuto %{full_name}"
+}, {
+  "id" : "it.crud.error.serialization",
+  "code" : "crud.error.serialization",
+  "lang" : "it",
+  "text" : "Errore nella serializzazione dei dati"
+}, {
+  "id" : "it.error.validation",
+  "code" : "error.validation",
+  "lang" : "it",
+  "text" : "Errore di validazione"
+}, {
+  "id" : "it.iam.error.email-address-already-exists",
+  "code" : "iam.error.email-address-already-exists",
+  "lang" : "it",
+  "text" : "Indirizzo e-mail già utilizzato"
+}, {
+  "id" : "it.menu.groups.admin",
+  "code" : "menu.groups.admin",
+  "lang" : "it",
+  "text" : "Amministrazione"
+}, {
+  "id" : "it.menu.items.command-logs",
+  "code" : "menu.items.command-logs",
+  "lang" : "it",
+  "text" : "Comandi (Logs)"
+}, {
+  "id" : "it.menu.items.entities/i18n-message",
+  "code" : "menu.items.entities/i18n-message",
+  "lang" : "it",
+  "text" : "Messaggi (i18n)"
+}, {
+  "id" : "it.menu.items.entities/notification",
+  "code" : "menu.items.entities/notification",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.menu.items.entities/user",
+  "code" : "menu.items.entities/user",
+  "lang" : "it",
+  "text" : "Utenti"
+}, {
+  "id" : "it.menu.items.notifications",
+  "code" : "menu.items.notifications",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.menu.items.users",
+  "code" : "menu.items.users",
+  "lang" : "it",
+  "text" : "Utenti"
+}, {
+  "id" : "it.ra.action.add",
+  "code" : "ra.action.add",
+  "lang" : "it",
+  "text" : "Aggiungi"
+}, {
+  "id" : "it.ra.action.add_filter",
+  "code" : "ra.action.add_filter",
+  "lang" : "it",
+  "text" : "Filtri"
+}, {
+  "id" : "it.ra.action.all",
+  "code" : "ra.action.all",
+  "lang" : "it",
+  "text" : "(Tutti)"
+}, {
+  "id" : "it.ra.action.back",
+  "code" : "ra.action.back",
+  "lang" : "it",
+  "text" : "Indietro"
+}, {
+  "id" : "it.ra.action.bulk_actions",
+  "code" : "ra.action.bulk_actions",
+  "lang" : "it",
+  "text" : "%{smart_count} selezionati"
+}, {
+  "id" : "it.ra.action.cancel",
+  "code" : "ra.action.cancel",
+  "lang" : "it",
+  "text" : "Annulla"
+}, {
+  "id" : "it.ra.action.clear_array_input",
+  "code" : "ra.action.clear_array_input",
+  "lang" : "it",
+  "text" : "Rimuovi tutti gli elementi"
+}, {
+  "id" : "it.ra.action.clear_input_value",
+  "code" : "ra.action.clear_input_value",
+  "lang" : "it",
+  "text" : "Svuota il modulo"
+}, {
+  "id" : "it.ra.action.clone",
+  "code" : "ra.action.clone",
+  "lang" : "it",
+  "text" : "Duplica"
+}, {
+  "id" : "it.ra.action.close",
+  "code" : "ra.action.close",
+  "lang" : "it",
+  "text" : "Chiudi"
+}, {
+  "id" : "it.ra.action.close_menu",
+  "code" : "ra.action.close_menu",
+  "lang" : "it",
+  "text" : "Chiudi il menu"
+}, {
+  "id" : "it.ra.action.confirm",
+  "code" : "ra.action.confirm",
+  "lang" : "it",
+  "text" : "Conferma"
+}, {
+  "id" : "it.ra.action.create",
+  "code" : "ra.action.create",
+  "lang" : "it",
+  "text" : "Crea"
+}, {
+  "id" : "it.ra.action.delete",
+  "code" : "ra.action.delete",
+  "lang" : "it",
+  "text" : "Cancella"
+}, {
+  "id" : "it.ra.action.edit",
+  "code" : "ra.action.edit",
+  "lang" : "it",
+  "text" : "Modifica"
+}, {
+  "id" : "it.ra.action.expand",
+  "code" : "ra.action.expand",
+  "lang" : "it",
+  "text" : "Espandi"
+}, {
+  "id" : "it.ra.action.export",
+  "code" : "ra.action.export",
+  "lang" : "it",
+  "text" : "Esporta"
+}, {
+  "id" : "it.ra.action.list",
+  "code" : "ra.action.list",
+  "lang" : "it",
+  "text" : "Elenco"
+}, {
+  "id" : "it.ra.action.next",
+  "code" : "ra.action.next",
+  "lang" : "it",
+  "text" : "Prosegui"
+}, {
+  "id" : "it.ra.action.open_menu",
+  "code" : "ra.action.open_menu",
+  "lang" : "it",
+  "text" : "Apri il menu"
+}, {
+  "id" : "it.ra.action.refresh",
+  "code" : "ra.action.refresh",
+  "lang" : "it",
+  "text" : "Aggiorna"
+}, {
+  "id" : "it.ra.action.remove",
+  "code" : "ra.action.remove",
+  "lang" : "it",
+  "text" : "Rimuovi"
+}, {
+  "id" : "it.ra.action.remove_filter",
+  "code" : "ra.action.remove_filter",
+  "lang" : "it",
+  "text" : "Rimuovi questo filtro"
+}, {
+  "id" : "it.ra.action.save",
+  "code" : "ra.action.save",
+  "lang" : "it",
+  "text" : "Salva"
+}, {
+  "id" : "it.ra.action.search",
+  "code" : "ra.action.search",
+  "lang" : "it",
+  "text" : "Ricerca"
+}, {
+  "id" : "it.ra.action.show",
+  "code" : "ra.action.show",
+  "lang" : "it",
+  "text" : "Mostra"
+}, {
+  "id" : "it.ra.action.sort",
+  "code" : "ra.action.sort",
+  "lang" : "it",
+  "text" : "Ordina"
+}, {
+  "id" : "it.ra.action.undo",
+  "code" : "ra.action.undo",
+  "lang" : "it",
+  "text" : "Annulla"
+}, {
+  "id" : "it.ra.action.unselect",
+  "code" : "ra.action.unselect",
+  "lang" : "it",
+  "text" : "Annulla selezione"
+}, {
+  "id" : "it.ra.action.view_all",
+  "code" : "ra.action.view_all",
+  "lang" : "it",
+  "text" : "Visualizza Tutto"
+}, {
+  "id" : "it.ra.active.false",
+  "code" : "ra.active.false",
+  "lang" : "it",
+  "text" : "Disattivata"
+}, {
+  "id" : "it.ra.active.true",
+  "code" : "ra.active.true",
+  "lang" : "it",
+  "text" : "Attiva"
+}, {
+  "id" : "it.ra.attachment.info",
+  "code" : "ra.attachment.info",
+  "lang" : "it",
+  "text" : "Creato da %{user} %{created}"
+}, {
+  "id" : "it.ra.audit-log.entities/create",
+  "code" : "ra.audit-log.entities/create",
+  "lang" : "it",
+  "text" : "Creata entità di tipo %{entity}"
+}, {
+  "id" : "it.ra.audit-log.entities/edit",
+  "code" : "ra.audit-log.entities/edit",
+  "lang" : "it",
+  "text" : "Modificata entità di tipo %{entity} con id %{id}"
+}, {
+  "id" : "it.ra.audit-log.entities/find",
+  "code" : "ra.audit-log.entities/find",
+  "lang" : "it",
+  "text" : "Ricerca per %{entity}"
+}, {
+  "id" : "it.ra.audit-log.entities/get",
+  "code" : "ra.audit-log.entities/get",
+  "lang" : "it",
+  "text" : "Visualizzata entità %{entity} con id %{id}"
+}, {
+  "id" : "it.ra.audit-log.entities/post",
+  "code" : "ra.audit-log.entities/post",
+  "lang" : "it",
+  "text" : "Creata nuova entità di tipo %{entity}"
+}, {
+  "id" : "it.ra.audit_log.default_explanation",
+  "code" : "ra.audit_log.default_explanation",
+  "lang" : "it",
+  "text" : "Operazione non identificata"
+}, {
+  "id" : "it.ra.audit_log.default_message",
+  "code" : "ra.audit_log.default_message",
+  "lang" : "it",
+  "text" : "Eseguita chiamata %{method} su %{url}"
+}, {
+  "id" : "it.ra.auth.account",
+  "code" : "ra.auth.account",
+  "lang" : "it",
+  "text" : "Username o email"
+}, {
+  "id" : "it.ra.auth.activate",
+  "code" : "ra.auth.activate",
+  "lang" : "it",
+  "text" : "Attivazione Account"
+}, {
+  "id" : "it.ra.auth.activate_pending",
+  "code" : "ra.auth.activate_pending",
+  "lang" : "it",
+  "text" : "Verifica dell'account in corso..."
+}, {
+  "id" : "it.ra.auth.activate_success",
+  "code" : "ra.auth.activate_success",
+  "lang" : "it",
+  "text" : "Account attivato correttamente"
+}, {
+  "id" : "it.ra.auth.already_have_account",
+  "code" : "ra.auth.already_have_account",
+  "lang" : "it",
+  "text" : "Hai già un account?"
+}, {
+  "id" : "it.ra.auth.auth_check_error",
+  "code" : "ra.auth.auth_check_error",
+  "lang" : "it",
+  "text" : "E' necessario accedere per continuare"
+}, {
+  "id" : "it.ra.auth.back_to_login",
+  "code" : "ra.auth.back_to_login",
+  "lang" : "it",
+  "text" : "Torna al login"
+}, {
+  "id" : "it.ra.auth.change_password",
+  "code" : "ra.auth.change_password",
+  "lang" : "it",
+  "text" : "Cambia Password"
+}, {
+  "id" : "it.ra.auth.create_account",
+  "code" : "ra.auth.create_account",
+  "lang" : "it",
+  "text" : "Registrati"
+}, {
+  "id" : "it.ra.auth.forgot_password",
+  "code" : "ra.auth.forgot_password",
+  "lang" : "it",
+  "text" : "Password dimenticata?"
+}, {
+  "id" : "it.ra.auth.impersonating.undo",
+  "code" : "ra.auth.impersonating.undo",
+  "lang" : "it",
+  "text" : "Smetti di impersonate %{name}"
+}, {
+  "id" : "it.ra.auth.impersonating.undo.short",
+  "code" : "ra.auth.impersonating.undo.short",
+  "lang" : "it",
+  "text" : "Esci da %{name}"
+}, {
+  "id" : "it.ra.auth.login.spid",
+  "code" : "ra.auth.login.spid",
+  "lang" : "it",
+  "text" : "Entra con SPID/CIE/CNS"
+}, {
+  "id" : "it.ra.auth.logout",
+  "code" : "ra.auth.logout",
+  "lang" : "it",
+  "text" : "Disconnessione"
+}, {
+  "id" : "it.ra.auth.password",
+  "code" : "ra.auth.password",
+  "lang" : "it",
+  "text" : "Password"
+}, {
+  "id" : "it.ra.auth.password_changed",
+  "code" : "ra.auth.password_changed",
+  "lang" : "it",
+  "text" : "Password aggiornata correttamente"
+}, {
+  "id" : "it.ra.auth.password_placeholder",
+  "code" : "ra.auth.password_placeholder",
+  "lang" : "it",
+  "text" : "La tua password"
+}, {
+  "id" : "it.ra.auth.password_reset.button",
+  "code" : "ra.auth.password_reset.button",
+  "lang" : "it",
+  "text" : "Reimposta"
+}, {
+  "id" : "it.ra.auth.password_reset.title",
+  "code" : "ra.auth.password_reset.title",
+  "lang" : "it",
+  "text" : "Reimposta password"
+}, {
+  "id" : "it.ra.auth.recover",
+  "code" : "ra.auth.recover",
+  "lang" : "it",
+  "text" : "Invia E-mail conferma reset password"
+}, {
+  "id" : "it.ra.auth.recover.inbox_help",
+  "code" : "ra.auth.recover.inbox_help",
+  "lang" : "it",
+  "text" : "Ricorda di controllare la cartella SPAM"
+}, {
+  "id" : "it.ra.auth.recover.title",
+  "code" : "ra.auth.recover.title",
+  "lang" : "it",
+  "text" : "Recupera Password"
+}, {
+  "id" : "it.ra.auth.recover_ok",
+  "code" : "ra.auth.recover_ok",
+  "lang" : "it",
+  "text" : "Recupero password eseguito, contorlla la tua e-mail"
+}, {
+  "id" : "it.ra.auth.register",
+  "code" : "ra.auth.register",
+  "lang" : "it",
+  "text" : "Non hai ancora un account?"
+}, {
+  "id" : "it.ra.auth.register.title",
+  "code" : "ra.auth.register.title",
+  "lang" : "it",
+  "text" : "Registrati"
+}, {
+  "id" : "it.ra.auth.register_iam.error.email-address-already-exists",
+  "code" : "ra.auth.register_iam.error.email-address-already-exists",
+  "lang" : "it",
+  "text" : "Non Ã¨ possibile utilizzare questo indirizzo e-mail"
+}, {
+  "id" : "it.ra.auth.register_iam.error.email-not-valid",
+  "code" : "ra.auth.register_iam.error.email-not-valid",
+  "lang" : "it",
+  "text" : "L'indirizzo e-mail inserito non Ã¨ valido"
+}, {
+  "id" : "it.ra.auth.register_ok",
+  "code" : "ra.auth.register_ok",
+  "lang" : "it",
+  "text" : "Registrazione avvenuta correttamente, controlla la tua e-mail per confermare i dati inseriti"
+}, {
+  "id" : "it.ra.auth.reset_password",
+  "code" : "ra.auth.reset_password",
+  "lang" : "it",
+  "text" : "Recupera Password"
+}, {
+  "id" : "it.ra.auth.sign_in",
+  "code" : "ra.auth.sign_in",
+  "lang" : "it",
+  "text" : "Login"
+}, {
+  "id" : "it.ra.auth.sign_in_error",
+  "code" : "ra.auth.sign_in_error",
+  "lang" : "it",
+  "text" : "Autenticazione fallita, riprovare."
+}, {
+  "id" : "it.ra.auth.sign_in_success",
+  "code" : "ra.auth.sign_in_success",
+  "lang" : "it",
+  "text" : "Utente impersona con successo, ricaricamento pagina in corso..."
+}, {
+  "id" : "it.ra.auth.sign_out_success",
+  "code" : "ra.auth.sign_out_success",
+  "lang" : "it",
+  "text" : "Logout da altro utente eseguito con successo, ricaricamento pagina in corso..."
+}, {
+  "id" : "it.ra.auth.stop_impersonate",
+  "code" : "ra.auth.stop_impersonate",
+  "lang" : "it",
+  "text" : "Smetti di impersonare"
+}, {
+  "id" : "it.ra.auth.user_menu",
+  "code" : "ra.auth.user_menu",
+  "lang" : "it",
+  "text" : "Profilo"
+}, {
+  "id" : "it.ra.auth.username",
+  "code" : "ra.auth.username",
+  "lang" : "it",
+  "text" : "Nome utente"
+}, {
+  "id" : "it.ra.boolean.false",
+  "code" : "ra.boolean.false",
+  "lang" : "it",
+  "text" : "No"
+}, {
+  "id" : "it.ra.boolean.null",
+  "code" : "ra.boolean.null",
+  "lang" : "it",
+  "text" : "(Tutti)"
+}, {
+  "id" : "it.ra.boolean.true",
+  "code" : "ra.boolean.true",
+  "lang" : "it",
+  "text" : "Si"
+}, {
+  "id" : "it.ra.custom_pages.welcome.subtitle",
+  "code" : "ra.custom_pages.welcome.subtitle",
+  "lang" : "it",
+  "text" : "Utilizza le voci di menu a sinistra per navigare ed usufruire delle funzioni applicative."
+}, {
+  "id" : "it.ra.custom_pages.welcome.title",
+  "code" : "ra.custom_pages.welcome.title",
+  "lang" : "it",
+  "text" : "Ciao!"
+}, {
+  "id" : "it.ra.error.equipment-type.delete",
+  "code" : "ra.error.equipment-type.delete",
+  "lang" : "it",
+  "text" : "Impossibile eliminare il Tipo di Attrezzatura"
+}, {
+  "id" : "it.ra.error.supplier.delete",
+  "code" : "ra.error.supplier.delete",
+  "lang" : "it",
+  "text" : "Impossibile eliminare il Fornitore"
+}, {
+  "id" : "it.ra.image.upload_several",
+  "code" : "ra.image.upload_several",
+  "lang" : "it",
+  "text" : "Trascina le immagini da caricare, oppure clicca per selezionarle."
+}, {
+  "id" : "it.ra.image.upload_single",
+  "code" : "ra.image.upload_single",
+  "lang" : "it",
+  "text" : "Trascina l'immagine da caricare, oppure clicca per selezionarla."
+}, {
+  "id" : "it.ra.input.file.upload_several",
+  "code" : "ra.input.file.upload_several",
+  "lang" : "it",
+  "text" : "Trascina i files da caricare, oppure clicca per selezionare."
+}, {
+  "id" : "it.ra.input.file.upload_single",
+  "code" : "ra.input.file.upload_single",
+  "lang" : "it",
+  "text" : "Trascina il file da caricare, oppure clicca per selezionarlo."
+}, {
+  "id" : "it.ra.input.image.upload_single",
+  "code" : "ra.input.image.upload_single",
+  "lang" : "it",
+  "text" : "Seleziona o sposta qui il file da caricare"
+}, {
+  "id" : "it.ra.input.password.toggle_hidden",
+  "code" : "ra.input.password.toggle_hidden",
+  "lang" : "it",
+  "text" : "Nascondi Password"
+}, {
+  "id" : "it.ra.input.password.toggle_visible",
+  "code" : "ra.input.password.toggle_visible",
+  "lang" : "it",
+  "text" : "Mostra Password"
+}, {
+  "id" : "it.ra.menu.control-panel",
+  "code" : "ra.menu.control-panel",
+  "lang" : "it",
+  "text" : "Impostazioni"
+}, {
+  "id" : "it.ra.menu.custom_pages",
+  "code" : "ra.menu.custom_pages",
+  "lang" : "it",
+  "text" : "Dashboard"
+}, {
+  "id" : "it.ra.menu.dashboard",
+  "code" : "ra.menu.dashboard",
+  "lang" : "it",
+  "text" : "Dashboard"
+}, {
+  "id" : "it.ra.menu.item.custom_page",
+  "code" : "ra.menu.item.custom_page",
+  "lang" : "it",
+  "text" : "Home"
+}, {
+  "id" : "it.ra.menu.item.entities/audit-log",
+  "code" : "ra.menu.item.entities/audit-log",
+  "lang" : "it",
+  "text" : "Audit Log"
+}, {
+  "id" : "it.ra.menu.item.entities/customer",
+  "code" : "ra.menu.item.entities/customer",
+  "lang" : "it",
+  "text" : "Clienti"
+}, {
+  "id" : "it.ra.menu.item.entities/device",
+  "code" : "ra.menu.item.entities/device",
+  "lang" : "it",
+  "text" : "Dispositivi"
+}, {
+  "id" : "it.ra.menu.item.entities/equipment-type",
+  "code" : "ra.menu.item.entities/equipment-type",
+  "lang" : "it",
+  "text" : "Tipi Attrezzatura"
+}, {
+  "id" : "it.ra.menu.item.entities/i18n-message",
+  "code" : "ra.menu.item.entities/i18n-message",
+  "lang" : "it",
+  "text" : "Messaggi (i18n)"
+}, {
+  "id" : "it.ra.menu.item.entities/intervention-type",
+  "code" : "ra.menu.item.entities/intervention-type",
+  "lang" : "it",
+  "text" : "Tipi Intervento"
+}, {
+  "id" : "it.ra.menu.item.entities/notification",
+  "code" : "ra.menu.item.entities/notification",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.ra.menu.item.entities/rfid-device",
+  "code" : "ra.menu.item.entities/rfid-device",
+  "lang" : "it",
+  "text" : "Dispositivi RFID"
+}, {
+  "id" : "it.ra.menu.item.entities/user",
+  "code" : "ra.menu.item.entities/user",
+  "lang" : "it",
+  "text" : "Utenti"
+}, {
+  "id" : "it.ra.menu.item.notification",
+  "code" : "ra.menu.item.notification",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.ra.menu.item.supplier",
+  "code" : "ra.menu.item.supplier",
+  "lang" : "it",
+  "text" : "Fornitori"
+}, {
+  "id" : "it.ra.menu.maintenance",
+  "code" : "ra.menu.maintenance",
+  "lang" : "it",
+  "text" : "Manutenzione"
+}, {
+  "id" : "it.ra.menu.security",
+  "code" : "ra.menu.security",
+  "lang" : "it",
+  "text" : "Sicurezza"
+}, {
+  "id" : "it.ra.message.about",
+  "code" : "ra.message.about",
+  "lang" : "it",
+  "text" : "Informazioni"
+}, {
+  "id" : "it.ra.message.are_you_sure",
+  "code" : "ra.message.are_you_sure",
+  "lang" : "it",
+  "text" : "Sei sicuro?"
+}, {
+  "id" : "it.ra.message.bulk_delete_content",
+  "code" : "ra.message.bulk_delete_content",
+  "lang" : "it",
+  "text" : "Sei sicuro di voler cancellare questo %{name}? |||| Sei sicuro di voler eliminare questi %{smart_count}?"
+}, {
+  "id" : "it.ra.message.bulk_delete_title",
+  "code" : "ra.message.bulk_delete_title",
+  "lang" : "it",
+  "text" : "Delete %{name} |||| Delete %{smart_count} %{name} items"
+}, {
+  "id" : "it.ra.message.clear_array_input",
+  "code" : "ra.message.clear_array_input",
+  "lang" : "it",
+  "text" : "Sei sicuro di voler rimuovere tutti gli elementi?"
+}, {
+  "id" : "it.ra.message.confirm_delete",
+  "code" : "ra.message.confirm_delete",
+  "lang" : "it",
+  "text" : "Sei sicuro di voler cancellare questo elemento?"
+}, {
+  "id" : "it.ra.message.delete_content",
+  "code" : "ra.message.delete_content",
+  "lang" : "it",
+  "text" : "Sicuro di voler cancellare questo elemento?"
+}, {
+  "id" : "it.ra.message.delete_title",
+  "code" : "ra.message.delete_title",
+  "lang" : "it",
+  "text" : "Conferma cancellazione"
+}, {
+  "id" : "it.ra.message.details",
+  "code" : "ra.message.details",
+  "lang" : "it",
+  "text" : "Dettagli"
+}, {
+  "id" : "it.ra.message.error",
+  "code" : "ra.message.error",
+  "lang" : "it",
+  "text" : "Si Ã¨ verificato un errore locale per cui non Ã¨ stato possibile completare l'operazione. "
+}, {
+  "id" : "it.ra.message.invalid_form",
+  "code" : "ra.message.invalid_form",
+  "lang" : "it",
+  "text" : "Il modulo non Ã¨ valido. Si prega di verificare la presenza di errori."
+}, {
+  "id" : "it.ra.message.loading",
+  "code" : "ra.message.loading",
+  "lang" : "it",
+  "text" : "La pagina si sta caricando, solo un momento per favore"
+}, {
+  "id" : "it.ra.message.no",
+  "code" : "ra.message.no",
+  "lang" : "it",
+  "text" : "No"
+}, {
+  "id" : "it.ra.message.not_found",
+  "code" : "ra.message.not_found",
+  "lang" : "it",
+  "text" : "Hai inserito un URL errato, oppure hai cliccato un link errato"
+}, {
+  "id" : "it.ra.message.profile_updated",
+  "code" : "ra.message.profile_updated",
+  "lang" : "it",
+  "text" : "Profilo aggiornato correttamente"
+}, {
+  "id" : "it.ra.message.unsaved_changes",
+  "code" : "ra.message.unsaved_changes",
+  "lang" : "it",
+  "text" : "Alcune modifiche non sono state salvate. Sei sicuro di volerle ignorare?"
+}, {
+  "id" : "it.ra.message.yes",
+  "code" : "ra.message.yes",
+  "lang" : "it",
+  "text" : "Si"
+}, {
+  "id" : "it.ra.navigation.next",
+  "code" : "ra.navigation.next",
+  "lang" : "it",
+  "text" : "Successivo"
+}, {
+  "id" : "it.ra.navigation.no_more_results",
+  "code" : "ra.navigation.no_more_results",
+  "lang" : "it",
+  "text" : "La pagina numero %{page} Ã¨ fuori dall'intervallo. Prova la pagina precedente."
+}, {
+  "id" : "it.ra.navigation.no_results",
+  "code" : "ra.navigation.no_results",
+  "lang" : "it",
+  "text" : "Nessun risultato trovato"
+}, {
+  "id" : "it.ra.navigation.page_out_from_begin",
+  "code" : "ra.navigation.page_out_from_begin",
+  "lang" : "it",
+  "text" : "Il numero di pagina deve essere maggiore di 1"
+}, {
+  "id" : "it.ra.navigation.page_out_from_end",
+  "code" : "ra.navigation.page_out_from_end",
+  "lang" : "it",
+  "text" : "Fine della paginazione"
+}, {
+  "id" : "it.ra.navigation.page_out_of_boundaries",
+  "code" : "ra.navigation.page_out_of_boundaries",
+  "lang" : "it",
+  "text" : "Il numero di pagina %{page} Ã¨ fuori dei limiti"
+}, {
+  "id" : "it.ra.navigation.page_range_info",
+  "code" : "ra.navigation.page_range_info",
+  "lang" : "it",
+  "text" : "%{offsetBegin}-%{offsetEnd} di %{total}"
+}, {
+  "id" : "it.ra.navigation.page_rows_per_page",
+  "code" : "ra.navigation.page_rows_per_page",
+  "lang" : "it",
+  "text" : "Righe per pagina"
+}, {
+  "id" : "it.ra.navigation.prev",
+  "code" : "ra.navigation.prev",
+  "lang" : "it",
+  "text" : "Precedente"
+}, {
+  "id" : "it.ra.navigation.skip_nav",
+  "code" : "ra.navigation.skip_nav",
+  "lang" : "it",
+  "text" : "Vai al contenuto"
+}, {
+  "id" : "it.ra.no",
+  "code" : "ra.no",
+  "lang" : "it",
+  "text" : "No"
+}, {
+  "id" : "it.ra.no_results",
+  "code" : "ra.no_results",
+  "lang" : "it",
+  "text" : "Nessun elemento registrato."
+}, {
+  "id" : "it.ra.notification",
+  "code" : "ra.notification",
+  "lang" : "it",
+  "text" : "Notifica"
+}, {
+  "id" : "it.ra.notification.bad_item",
+  "code" : "ra.notification.bad_item",
+  "lang" : "it",
+  "text" : "Record errato"
+}, {
+  "id" : "it.ra.notification.canceled",
+  "code" : "ra.notification.canceled",
+  "lang" : "it",
+  "text" : "Azione annullata"
+}, {
+  "id" : "it.ra.notification.created",
+  "code" : "ra.notification.created",
+  "lang" : "it",
+  "text" : "Salvataggio eseguito"
+}, {
+  "id" : "it.ra.notification.data_provider_error",
+  "code" : "ra.notification.data_provider_error",
+  "lang" : "it",
+  "text" : "Errore del data provider. Controlla la console per i dettagli."
+}, {
+  "id" : "it.ra.notification.deleted",
+  "code" : "ra.notification.deleted",
+  "lang" : "it",
+  "text" : "Record eliminato |||| %{smart_count} records eliminati"
+}, {
+  "id" : "it.ra.notification.http_error",
+  "code" : "ra.notification.http_error",
+  "lang" : "it",
+  "text" : "Errore di comunicazione con il server dati"
+}, {
+  "id" : "it.ra.notification.i18n_error",
+  "code" : "ra.notification.i18n_error",
+  "lang" : "it",
+  "text" : "Traduzioni non trovate per il linguaggio specificato"
+}, {
+  "id" : "it.ra.notification.item_doesnt_exist",
+  "code" : "ra.notification.item_doesnt_exist",
+  "lang" : "it",
+  "text" : "Record inesistente"
+}, {
+  "id" : "it.ra.notification.logged_out",
+  "code" : "ra.notification.logged_out",
+  "lang" : "it",
+  "text" : "La sessione Ã¨ stata terminata, si prega di ripetere l'autenticazione."
+}, {
+  "id" : "it.ra.notification.mark_as_readed",
+  "code" : "ra.notification.mark_as_readed",
+  "lang" : "it",
+  "text" : "Segna come letto"
+}, {
+  "id" : "it.ra.notification.mark_as_unreaded",
+  "code" : "ra.notification.mark_as_unreaded",
+  "lang" : "it",
+  "text" : "Segna come non letto"
+}, {
+  "id" : "it.ra.notification.readed",
+  "code" : "ra.notification.readed",
+  "lang" : "it",
+  "text" : "Letta in data %{readed}"
+}, {
+  "id" : "it.ra.notification.readed_error",
+  "code" : "ra.notification.readed_error",
+  "lang" : "it",
+  "text" : "Impossibile segnare le notifiche selezionate."
+}, {
+  "id" : "it.ra.notification.unreaded",
+  "code" : "ra.notification.unreaded",
+  "lang" : "it",
+  "text" : "Non lette"
+}, {
+  "id" : "it.ra.notification.updated",
+  "code" : "ra.notification.updated",
+  "lang" : "it",
+  "text" : "Record aggiornato |||| %{smart_count} records aggiornati"
+}, {
+  "id" : "it.ra.notifications.read_all",
+  "code" : "ra.notifications.read_all",
+  "lang" : "it",
+  "text" : "Segna tutte come lette"
+}, {
+  "id" : "it.ra.notifications.title",
+  "code" : "ra.notifications.title",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.ra.page.create",
+  "code" : "ra.page.create",
+  "lang" : "it",
+  "text" : "Aggiungi %{name}"
+}, {
+  "id" : "it.ra.page.dashboard",
+  "code" : "ra.page.dashboard",
+  "lang" : "it",
+  "text" : "Cruscotto"
+}, {
+  "id" : "it.ra.page.edit",
+  "code" : "ra.page.edit",
+  "lang" : "it",
+  "text" : "%{name} %{id}"
+}, {
+  "id" : "it.ra.page.empty",
+  "code" : "ra.page.empty",
+  "lang" : "it",
+  "text" : "Nessun dato caricato per %{name}"
+}, {
+  "id" : "it.ra.page.error",
+  "code" : "ra.page.error",
+  "lang" : "it",
+  "text" : "Qualcosa non ha funzionato"
+}, {
+  "id" : "it.ra.page.invite",
+  "code" : "ra.page.invite",
+  "lang" : "it",
+  "text" : "Vuoi aggiungerne uno?"
+}, {
+  "id" : "it.ra.page.list",
+  "code" : "ra.page.list",
+  "lang" : "it",
+  "text" : "%{name}"
+}, {
+  "id" : "it.ra.page.loading",
+  "code" : "ra.page.loading",
+  "lang" : "it",
+  "text" : "Caricamento in corso"
+}, {
+  "id" : "it.ra.page.not_found",
+  "code" : "ra.page.not_found",
+  "lang" : "it",
+  "text" : "Non trovato"
+}, {
+  "id" : "it.ra.page.profile.title",
+  "code" : "ra.page.profile.title",
+  "lang" : "it",
+  "text" : "Informazioni profilo"
+}, {
+  "id" : "it.ra.page.show",
+  "code" : "ra.page.show",
+  "lang" : "it",
+  "text" : "%{name} %{id}"
+}, {
+  "id" : "it.ra.password.toggle_hidden",
+  "code" : "ra.password.toggle_hidden",
+  "lang" : "it",
+  "text" : "Mostra la password"
+}, {
+  "id" : "it.ra.password.toggle_visible",
+  "code" : "ra.password.toggle_visible",
+  "lang" : "it",
+  "text" : "Nascondi la password"
+}, {
+  "id" : "it.ra.reference_list.sorry",
+  "code" : "ra.reference_list.sorry",
+  "lang" : "it",
+  "text" : "Salva almeno una volta l'elemento corrente per poter aggiungere nuove informazioni in questa sezione."
+}, {
+  "id" : "it.ra.references.all_missing",
+  "code" : "ra.references.all_missing",
+  "lang" : "it",
+  "text" : "Impossibile trovare i riferimenti associati."
+}, {
+  "id" : "it.ra.references.many_missing",
+  "code" : "ra.references.many_missing",
+  "lang" : "it",
+  "text" : "Almeno uno dei riferimenti associati non sembra più disponibile."
+}, {
+  "id" : "it.ra.references.single_missing",
+  "code" : "ra.references.single_missing",
+  "lang" : "it",
+  "text" : "Il riferimento associato non sembra più disponibile."
+}, {
+  "id" : "it.ra.register.name",
+  "code" : "ra.register.name",
+  "lang" : "it",
+  "text" : "Nome Account"
+}, {
+  "id" : "it.ra.roles.admin",
+  "code" : "ra.roles.admin",
+  "lang" : "it",
+  "text" : "Amministratore"
+}, {
+  "id" : "it.ra.roles.mantainer",
+  "code" : "ra.roles.mantainer",
+  "lang" : "it",
+  "text" : "Manutentore"
+}, {
+  "id" : "it.ra.roles.operator",
+  "code" : "ra.roles.operator",
+  "lang" : "it",
+  "text" : "Operatore"
+}, {
+  "id" : "it.ra.roles.user",
+  "code" : "ra.roles.user",
+  "lang" : "it",
+  "text" : "Utilizzatore"
+}, {
+  "id" : "it.ra.sort.ASC",
+  "code" : "ra.sort.ASC",
+  "lang" : "it",
+  "text" : "cresente"
+}, {
+  "id" : "it.ra.sort.DESC",
+  "code" : "ra.sort.DESC",
+  "lang" : "it",
+  "text" : "decrescente"
+}, {
+  "id" : "it.ra.sort.sort_by",
+  "code" : "ra.sort.sort_by",
+  "lang" : "it",
+  "text" : "Ordina per %{field} %{order}"
+}, {
+  "id" : "it.ra.title.confirm_delete",
+  "code" : "ra.title.confirm_delete",
+  "lang" : "it",
+  "text" : "Conferma"
+}, {
+  "id" : "it.ra.user.roles",
+  "code" : "ra.user.roles",
+  "lang" : "it",
+  "text" : "Ruoli"
+}, {
+  "id" : "it.ra.validation.email",
+  "code" : "ra.validation.email",
+  "lang" : "it",
+  "text" : "Deve essere un valido indirizzo email"
+}, {
+  "id" : "it.ra.validation.future_date",
+  "code" : "ra.validation.future_date",
+  "lang" : "it",
+  "text" : "La data deve essere successiva ad oggi"
+}, {
+  "id" : "it.ra.validation.maxLength",
+  "code" : "ra.validation.maxLength",
+  "lang" : "it",
+  "text" : "Deve essere lungo %{max} caratteri al massimo"
+}, {
+  "id" : "it.ra.validation.maxValue",
+  "code" : "ra.validation.maxValue",
+  "lang" : "it",
+  "text" : "Deve essere al massimo %{max}"
+}, {
+  "id" : "it.ra.validation.minLength",
+  "code" : "ra.validation.minLength",
+  "lang" : "it",
+  "text" : "Deve essere lungo %{min} caratteri almeno"
+}, {
+  "id" : "it.ra.validation.minValue",
+  "code" : "ra.validation.minValue",
+  "lang" : "it",
+  "text" : "Deve essere almeno %{min}"
+}, {
+  "id" : "it.ra.validation.number",
+  "code" : "ra.validation.number",
+  "lang" : "it",
+  "text" : "Deve essere un numero"
+}, {
+  "id" : "it.ra.validation.oneOf",
+  "code" : "ra.validation.oneOf",
+  "lang" : "it",
+  "text" : "Deve essere uno di: %{options}"
+}, {
+  "id" : "it.ra.validation.regex",
+  "code" : "ra.validation.regex",
+  "lang" : "it",
+  "text" : "Deve rispettare il formato (espressione regolare): %{pattern}"
+}, {
+  "id" : "it.ra.validation.required",
+  "code" : "ra.validation.required",
+  "lang" : "it",
+  "text" : "Campo obbligatorio"
+}, {
+  "id" : "it.ra.validation_summary.message.success",
+  "code" : "ra.validation_summary.message.success",
+  "lang" : "it",
+  "text" : "Tutte le informazioni presenti nella sezione corrente sono state inserite correttamente."
+}, {
+  "id" : "it.ra.yes",
+  "code" : "ra.yes",
+  "lang" : "it",
+  "text" : "Si"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.created",
+  "code" : "resources.entities/audit-log.fields.created",
+  "lang" : "it",
+  "text" : "Data e ora"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.from",
+  "code" : "resources.entities/audit-log.fields.from",
+  "lang" : "it",
+  "text" : "Dalla data"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.message",
+  "code" : "resources.entities/audit-log.fields.message",
+  "lang" : "it",
+  "text" : "Messaggio"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.to",
+  "code" : "resources.entities/audit-log.fields.to",
+  "lang" : "it",
+  "text" : "Alla data"
+}, {
+  "id" : "it.resources.entities/audit-log.fields.userId",
+  "code" : "resources.entities/audit-log.fields.userId",
+  "lang" : "it",
+  "text" : "Utente"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.description",
+  "code" : "resources.entities/customer-area.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.name",
+  "code" : "resources.entities/customer-area.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.office.address",
+  "code" : "resources.entities/customer-area.fields.office.address",
+  "lang" : "it",
+  "text" : "Indirizzo"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.office.cityId",
+  "code" : "resources.entities/customer-area.fields.office.cityId",
+  "lang" : "it",
+  "text" : "Città"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.officeId",
+  "code" : "resources.entities/customer-area.fields.officeId",
+  "lang" : "it",
+  "text" : "Ufficio"
+}, {
+  "id" : "it.resources.entities/customer-area.fields.updated",
+  "code" : "resources.entities/customer-area.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/customer-area.name",
+  "code" : "resources.entities/customer-area.name",
+  "lang" : "it",
+  "text" : "Area |||| Aree"
+}, {
+  "id" : "it.resources.entities/customer-area.title.create",
+  "code" : "resources.entities/customer-area.title.create",
+  "lang" : "it",
+  "text" : "Nuova Area"
+}, {
+  "id" : "it.resources.entities/customer-area.title.edit",
+  "code" : "resources.entities/customer-area.title.edit",
+  "lang" : "it",
+  "text" : "Modifica Area"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.address",
+  "code" : "resources.entities/customer-office.fields.address",
+  "lang" : "it",
+  "text" : "Indirizzo"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.cityId",
+  "code" : "resources.entities/customer-office.fields.cityId",
+  "lang" : "it",
+  "text" : "Città"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.headquarter",
+  "code" : "resources.entities/customer-office.fields.headquarter",
+  "lang" : "it",
+  "text" : "Sede Legale"
+}, {
+  "id" : "it.resources.entities/customer-office.fields.updated",
+  "code" : "resources.entities/customer-office.fields.updated",
+  "lang" : "it",
+  "text" : "Ultima Modifica"
+}, {
+  "id" : "it.resources.entities/customer-office.name",
+  "code" : "resources.entities/customer-office.name",
+  "lang" : "it",
+  "text" : "Sede |||| Sedi"
+}, {
+  "id" : "it.resources.entities/customer-office.title.create",
+  "code" : "resources.entities/customer-office.title.create",
+  "lang" : "it",
+  "text" : "Nuova Sede"
+}, {
+  "id" : "it.resources.entities/customer-office.title.edit",
+  "code" : "resources.entities/customer-office.title.edit",
+  "lang" : "it",
+  "text" : "Modifica Sede"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.email",
+  "code" : "resources.entities/customer-referent.fields.email",
+  "lang" : "it",
+  "text" : "E-mail"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.mobile",
+  "code" : "resources.entities/customer-referent.fields.mobile",
+  "lang" : "it",
+  "text" : "Telefono"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.name",
+  "code" : "resources.entities/customer-referent.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.office.cityId",
+  "code" : "resources.entities/customer-referent.fields.office.cityId",
+  "lang" : "it",
+  "text" : "Città"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.officeId",
+  "code" : "resources.entities/customer-referent.fields.officeId",
+  "lang" : "it",
+  "text" : "Sede"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.surname",
+  "code" : "resources.entities/customer-referent.fields.surname",
+  "lang" : "it",
+  "text" : "Cognome"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.unit",
+  "code" : "resources.entities/customer-referent.fields.unit",
+  "lang" : "it",
+  "text" : "Reparto"
+}, {
+  "id" : "it.resources.entities/customer-referent.fields.updated",
+  "code" : "resources.entities/customer-referent.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/customer-referent.name",
+  "code" : "resources.entities/customer-referent.name",
+  "lang" : "it",
+  "text" : "Referente |||| Referenti"
+}, {
+  "id" : "it.resources.entities/customer-referent.title.create",
+  "code" : "resources.entities/customer-referent.title.create",
+  "lang" : "it",
+  "text" : "Nuovo Referente"
+}, {
+  "id" : "it.resources.entities/customer-referent.title.edit",
+  "code" : "resources.entities/customer-referent.title.edit",
+  "lang" : "it",
+  "text" : "Modifica Referente"
+}, {
+  "id" : "it.resources.entities/customer.actions.create",
+  "code" : "resources.entities/customer.actions.create",
+  "lang" : "it",
+  "text" : "Aggiungi"
+}, {
+  "id" : "it.resources.entities/customer.breadcrumbs.edit",
+  "code" : "resources.entities/customer.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Cliente"
+}, {
+  "id" : "it.resources.entities/customer.fields.active",
+  "code" : "resources.entities/customer.fields.active",
+  "lang" : "it",
+  "text" : "Attivo"
+}, {
+  "id" : "it.resources.entities/customer.fields.name",
+  "code" : "resources.entities/customer.fields.name",
+  "lang" : "it",
+  "text" : "Ragione Sociale"
+}, {
+  "id" : "it.resources.entities/customer.fields.updated",
+  "code" : "resources.entities/customer.fields.updated",
+  "lang" : "it",
+  "text" : "Ultima Modifica"
+}, {
+  "id" : "it.resources.entities/customer.fields.vatCode",
+  "code" : "resources.entities/customer.fields.vatCode",
+  "lang" : "it",
+  "text" : "P.IVA"
+}, {
+  "id" : "it.resources.entities/customer.name",
+  "code" : "resources.entities/customer.name",
+  "lang" : "it",
+  "text" : "Cliente |||| Clienti"
+}, {
+  "id" : "it.resources.entities/customer.sections.offices",
+  "code" : "resources.entities/customer.sections.offices",
+  "lang" : "it",
+  "text" : "Sedi"
+}, {
+  "id" : "it.resources.entities/customer.sections.referents",
+  "code" : "resources.entities/customer.sections.referents",
+  "lang" : "it",
+  "text" : "Referenti"
+}, {
+  "id" : "it.resources.entities/customer.tabs.areas",
+  "code" : "resources.entities/customer.tabs.areas",
+  "lang" : "it",
+  "text" : "Aree"
+}, {
+  "id" : "it.resources.entities/customer.tabs.details",
+  "code" : "resources.entities/customer.tabs.details",
+  "lang" : "it",
+  "text" : "Generali"
+}, {
+  "id" : "it.resources.entities/customer.tabs.offices",
+  "code" : "resources.entities/customer.tabs.offices",
+  "lang" : "it",
+  "text" : "Uffici"
+}, {
+  "id" : "it.resources.entities/customer.tabs.referents",
+  "code" : "resources.entities/customer.tabs.referents",
+  "lang" : "it",
+  "text" : "Referenti"
+}, {
+  "id" : "it.resources.entities/customer.title.create",
+  "code" : "resources.entities/customer.title.create",
+  "lang" : "it",
+  "text" : "Nuovo Cliente"
+}, {
+  "id" : "it.resources.entities/customer.title.edit",
+  "code" : "resources.entities/customer.title.edit",
+  "lang" : "it",
+  "text" : "Modifica cliente %{name}"
+}, {
+  "id" : "it.resources.entities/device.alert",
+  "code" : "resources.entities/device.alert",
+  "lang" : "it",
+  "text" : "Di seguito Ã¨ presente l'elenco dei dispositivi registrati per cui Ã¨ disponibile il login con pin."
+}, {
+  "id" : "it.resources.entities/device.breadcrumbs.create",
+  "code" : "resources.entities/device.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Nuovo Dispositivo"
+}, {
+  "id" : "it.resources.entities/device.breadcrumbs.edit",
+  "code" : "resources.entities/device.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Dettaglio Dispositivo"
+}, {
+  "id" : "it.resources.entities/device.fields.code",
+  "code" : "resources.entities/device.fields.code",
+  "lang" : "it",
+  "text" : "Codice Univoco"
+}, {
+  "id" : "it.resources.entities/device.fields.registrationDate",
+  "code" : "resources.entities/device.fields.registrationDate",
+  "lang" : "it",
+  "text" : "Data di registrazione"
+}, {
+  "id" : "it.resources.entities/device.fields.secret",
+  "code" : "resources.entities/device.fields.secret",
+  "lang" : "it",
+  "text" : "Segreto condiviso"
+}, {
+  "id" : "it.resources.entities/device.name",
+  "code" : "resources.entities/device.name",
+  "lang" : "it",
+  "text" : "Dispositivo |||| Dispositivi"
+}, {
+  "id" : "it.resources.entities/device.title",
+  "code" : "resources.entities/device.title",
+  "lang" : "it",
+  "text" : "Dettagli Dispositivo"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.fields.attachment",
+  "code" : "resources.entities/equipment-type-attachment.fields.attachment",
+  "lang" : "it",
+  "text" : "Allegato"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.fields.description",
+  "code" : "resources.entities/equipment-type-attachment.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.name",
+  "code" : "resources.entities/equipment-type-attachment.name",
+  "lang" : "it",
+  "text" : "Allegato Tipo Attrezzatura |||| Allegati Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.title",
+  "code" : "resources.entities/equipment-type-attachment.title",
+  "lang" : "it",
+  "text" : "Allegato Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type-attachment.title.list",
+  "code" : "resources.entities/equipment-type-attachment.title.list",
+  "lang" : "it",
+  "text" : "Allegati Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.breadcrumbs.create",
+  "code" : "resources.entities/equipment-type.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.breadcrumbs.edit",
+  "code" : "resources.entities/equipment-type.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.accessory",
+  "code" : "resources.entities/equipment-type.fields.accessory",
+  "lang" : "it",
+  "text" : "Accessorio"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.attachment",
+  "code" : "resources.entities/equipment-type.fields.attachment",
+  "lang" : "it",
+  "text" : "Allegato"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.created",
+  "code" : "resources.entities/equipment-type.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.name",
+  "code" : "resources.entities/equipment-type.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/equipment-type.fields.updated",
+  "code" : "resources.entities/equipment-type.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/equipment-type.name",
+  "code" : "resources.entities/equipment-type.name",
+  "lang" : "it",
+  "text" : "Tipo Attrezzatura |||| Tipi Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.title",
+  "code" : "resources.entities/equipment-type.title",
+  "lang" : "it",
+  "text" : "Tipo Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipment-type.title.list",
+  "code" : "resources.entities/equipment-type.title.list",
+  "lang" : "it",
+  "text" : "Tipi Attrezzatura"
+}, {
+  "id" : "it.resources.entities/equipmentType.actions.backToFather",
+  "code" : "resources.entities/equipmentType.actions.backToFather",
+  "lang" : "it",
+  "text" : "Torna Indietro"
+}, {
+  "id" : "it.resources.entities/i18n-message.breadcrumbs.create",
+  "code" : "resources.entities/i18n-message.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Nuovo Messaggio"
+}, {
+  "id" : "it.resources.entities/i18n-message.breadcrumbs.edit",
+  "code" : "resources.entities/i18n-message.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Messaggio"
+}, {
+  "id" : "it.resources.entities/i18n-message.fields.code",
+  "code" : "resources.entities/i18n-message.fields.code",
+  "lang" : "it",
+  "text" : "Codice"
+}, {
+  "id" : "it.resources.entities/i18n-message.fields.lang",
+  "code" : "resources.entities/i18n-message.fields.lang",
+  "lang" : "it",
+  "text" : "Lingua"
+}, {
+  "id" : "it.resources.entities/i18n-message.fields.text",
+  "code" : "resources.entities/i18n-message.fields.text",
+  "lang" : "it",
+  "text" : "Testo"
+}, {
+  "id" : "it.resources.entities/i18n-message.fields.translated",
+  "code" : "resources.entities/i18n-message.fields.translated",
+  "lang" : "it",
+  "text" : "Tradotto"
+}, {
+  "id" : "it.resources.entities/i18n-message.name",
+  "code" : "resources.entities/i18n-message.name",
+  "lang" : "it",
+  "text" : "Messaggi (i18n)"
+}, {
+  "id" : "it.resources.entities/i18n-message.title",
+  "code" : "resources.entities/i18n-message.title",
+  "lang" : "it",
+  "text" : "Messaggio Localizzato"
+}, {
+  "id" : "it.resources.entities/intervention-type.breadcrumbs.create",
+  "code" : "resources.entities/intervention-type.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Tipo Intervento"
+}, {
+  "id" : "it.resources.entities/intervention-type.breadcrumbs.edit",
+  "code" : "resources.entities/intervention-type.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Tipo Intervento"
+}, {
+  "id" : "it.resources.entities/intervention-type.fields.created",
+  "code" : "resources.entities/intervention-type.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/intervention-type.fields.description",
+  "code" : "resources.entities/intervention-type.fields.description",
+  "lang" : "it",
+  "text" : "Descrizione"
+}, {
+  "id" : "it.resources.entities/intervention-type.fields.path",
+  "code" : "resources.entities/intervention-type.fields.path",
+  "lang" : "it",
+  "text" : "Percorso"
+}, {
+  "id" : "it.resources.entities/intervention-type.fields.updated",
+  "code" : "resources.entities/intervention-type.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/intervention-type.name",
+  "code" : "resources.entities/intervention-type.name",
+  "lang" : "it",
+  "text" : "Tipo Intervento |||| Tipi Intervento"
+}, {
+  "id" : "it.resources.entities/intervention-type.title",
+  "code" : "resources.entities/intervention-type.title",
+  "lang" : "it",
+  "text" : "Tipo Intervento"
+}, {
+  "id" : "it.resources.entities/notification.name",
+  "code" : "resources.entities/notification.name",
+  "lang" : "it",
+  "text" : "Notifiche"
+}, {
+  "id" : "it.resources.entities/rfid-device.breadcrumbs.create",
+  "code" : "resources.entities/rfid-device.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Dispositivo RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device.breadcrumbs.edit",
+  "code" : "resources.entities/rfid-device.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Dispositivo RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device.fields.code",
+  "code" : "resources.entities/rfid-device.fields.code",
+  "lang" : "it",
+  "text" : "Codice Seriale"
+}, {
+  "id" : "it.resources.entities/rfid-device.fields.name",
+  "code" : "resources.entities/rfid-device.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/rfid-device.fields.updated",
+  "code" : "resources.entities/rfid-device.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/rfid-device.name",
+  "code" : "resources.entities/rfid-device.name",
+  "lang" : "it",
+  "text" : "Dispositivo RFID |||| Dispositivi RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device.title.create",
+  "code" : "resources.entities/rfid-device.title.create",
+  "lang" : "it",
+  "text" : "Nuovo Dispositivo RFID"
+}, {
+  "id" : "it.resources.entities/rfid-device.title.edit",
+  "code" : "resources.entities/rfid-device.title.edit",
+  "lang" : "it",
+  "text" : "Modifica Dispositivo RFID"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.created",
+  "code" : "resources.entities/supplier-referent.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.department",
+  "code" : "resources.entities/supplier-referent.fields.department",
+  "lang" : "it",
+  "text" : "Dipartimento"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.email",
+  "code" : "resources.entities/supplier-referent.fields.email",
+  "lang" : "it",
+  "text" : "Email"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.name",
+  "code" : "resources.entities/supplier-referent.fields.name",
+  "lang" : "it",
+  "text" : "Nome"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.phone",
+  "code" : "resources.entities/supplier-referent.fields.phone",
+  "lang" : "it",
+  "text" : "Numero di Telefono"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.surname",
+  "code" : "resources.entities/supplier-referent.fields.surname",
+  "lang" : "it",
+  "text" : "Cognome"
+}, {
+  "id" : "it.resources.entities/supplier-referent.fields.updated",
+  "code" : "resources.entities/supplier-referent.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/supplier-referent.name",
+  "code" : "resources.entities/supplier-referent.name",
+  "lang" : "it",
+  "text" : "Referente Fornitore |||| Referenti Fornitore"
+}, {
+  "id" : "it.resources.entities/supplier-referent.title",
+  "code" : "resources.entities/supplier-referent.title",
+  "lang" : "it",
+  "text" : "Referenti Fornitore"
+}, {
+  "id" : "it.resources.entities/supplier.breadcrumbs.create",
+  "code" : "resources.entities/supplier.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Crea Fornitore"
+}, {
+  "id" : "it.resources.entities/supplier.breadcrumbs.edit",
+  "code" : "resources.entities/supplier.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Fornitore"
+}, {
+  "id" : "it.resources.entities/supplier.fields.address",
+  "code" : "resources.entities/supplier.fields.address",
+  "lang" : "it",
+  "text" : "Indirizzo"
+}, {
+  "id" : "it.resources.entities/supplier.fields.businessName",
+  "code" : "resources.entities/supplier.fields.businessName",
+  "lang" : "it",
+  "text" : "Ragione Sociale"
+}, {
+  "id" : "it.resources.entities/supplier.fields.cityId",
+  "code" : "resources.entities/supplier.fields.cityId",
+  "lang" : "it",
+  "text" : "Comune"
+}, {
+  "id" : "it.resources.entities/supplier.fields.created",
+  "code" : "resources.entities/supplier.fields.created",
+  "lang" : "it",
+  "text" : "Creato il"
+}, {
+  "id" : "it.resources.entities/supplier.fields.updated",
+  "code" : "resources.entities/supplier.fields.updated",
+  "lang" : "it",
+  "text" : "Ultimo Aggiornamento"
+}, {
+  "id" : "it.resources.entities/supplier.fields.vatNumber",
+  "code" : "resources.entities/supplier.fields.vatNumber",
+  "lang" : "it",
+  "text" : "Partita IVA"
+}, {
+  "id" : "it.resources.entities/supplier.name",
+  "code" : "resources.entities/supplier.name",
+  "lang" : "it",
+  "text" : "Fornitore |||| Fornitori"
+}, {
+  "id" : "it.resources.entities/supplier.title",
+  "code" : "resources.entities/supplier.title",
+  "lang" : "it",
+  "text" : "Fornitore"
+}, {
+  "id" : "it.resources.entities/user.breadcrumbs.create",
+  "code" : "resources.entities/user.breadcrumbs.create",
+  "lang" : "it",
+  "text" : "Nuovo Utente"
+}, {
+  "id" : "it.resources.entities/user.breadcrumbs.edit",
+  "code" : "resources.entities/user.breadcrumbs.edit",
+  "lang" : "it",
+  "text" : "Modifica Utente"
+}, {
+  "id" : "it.resources.entities/user.fields.active",
+  "code" : "resources.entities/user.fields.active",
+  "lang" : "it",
+  "text" : "Attivo"
+}, {
+  "id" : "it.resources.entities/user.fields.image",
+  "code" : "resources.entities/user.fields.image",
+  "lang" : "it",
+  "text" : "Immagine di profilo"
+}, {
+  "id" : "it.resources.entities/user.fields.name",
+  "code" : "resources.entities/user.fields.name",
+  "lang" : "it",
+  "text" : "Nome e Cognome"
+}, {
+  "id" : "it.resources.entities/user.fields.registrationDate",
+  "code" : "resources.entities/user.fields.registrationDate",
+  "lang" : "it",
+  "text" : "Data di registrazione"
+}, {
+  "id" : "it.resources.entities/user.fields.role",
+  "code" : "resources.entities/user.fields.role",
+  "lang" : "it",
+  "text" : "Ruolo"
+}, {
+  "id" : "it.resources.entities/user.name",
+  "code" : "resources.entities/user.name",
+  "lang" : "it",
+  "text" : "Utente |||| Utenti"
+}, {
+  "id" : "it.resources.entities/user.title.create",
+  "code" : "resources.entities/user.title.create",
+  "lang" : "it",
+  "text" : "Nuovo Utente"
+}, {
+  "id" : "it.resources.entities/user.title.edit",
+  "code" : "resources.entities/user.title.edit",
+  "lang" : "it",
+  "text" : "Modifica utente %{name}"
+}, {
+  "id" : "it.resources.notifications.empty",
+  "code" : "resources.notifications.empty",
+  "lang" : "it",
+  "text" : "Ancora nessuna notifica!"
+}, {
+  "id" : "it.resources.notifications.messages.readed.done",
+  "code" : "resources.notifications.messages.readed.done",
+  "lang" : "it",
+  "text" : "Notifiche segnate come lette."
+}, {
+  "id" : "it.resources.notifications.messages.unreaded.done",
+  "code" : "resources.notifications.messages.unreaded.done",
+  "lang" : "it",
+  "text" : "Notifiche segnate come non lette."
+}, {
+  "id" : "it.resources.transactions.fields.id",
+  "code" : "resources.transactions.fields.id",
+  "lang" : "it",
+  "text" : "#ID"
+}, {
+  "id" : "it.resources.transactions.fields.notes.show_less",
+  "code" : "resources.transactions.fields.notes.show_less",
+  "lang" : "it",
+  "text" : "Mostra meno"
+}, {
+  "id" : "it.resources.transactions.fields.state",
+  "code" : "resources.transactions.fields.state",
+  "lang" : "it",
+  "text" : "Stato"
+}, {
+  "id" : "it.resources.user/change-password.fields.newPassword",
+  "code" : "resources.user/change-password.fields.newPassword",
+  "lang" : "it",
+  "text" : "Nuova Password"
+}, {
+  "id" : "it.resources.user/change-password.fields.oldPassword",
+  "code" : "resources.user/change-password.fields.oldPassword",
+  "lang" : "it",
+  "text" : "Password precedente"
+}, {
+  "id" : "it.resources.user/change-password.title",
+  "code" : "resources.user/change-password.title",
+  "lang" : "it",
+  "text" : "Cambio Password"
+}, {
+  "id" : "it.test",
+  "code" : "test",
+  "lang" : "it",
+  "text" : "test"
+} ]
\ No newline at end of file
diff --git a/edera-api/api/src/test/resources/sample-google-workspace.docx b/edera-api/api/src/test/resources/sample-google-workspace.docx
new file mode 100644 (file)
index 0000000..5402977
Binary files /dev/null and b/edera-api/api/src/test/resources/sample-google-workspace.docx differ
diff --git a/edera-api/api/src/test/resources/sample-ms-office-open-xml.docx b/edera-api/api/src/test/resources/sample-ms-office-open-xml.docx
new file mode 100644 (file)
index 0000000..7061e52
Binary files /dev/null and b/edera-api/api/src/test/resources/sample-ms-office-open-xml.docx differ
diff --git a/edera-api/api/src/test/resources/sample-ms-office.docx b/edera-api/api/src/test/resources/sample-ms-office.docx
new file mode 100644 (file)
index 0000000..1fde152
Binary files /dev/null and b/edera-api/api/src/test/resources/sample-ms-office.docx differ
diff --git a/edera-api/api/src/test/resources/sample-open-office.docx b/edera-api/api/src/test/resources/sample-open-office.docx
new file mode 100644 (file)
index 0000000..e8f487c
Binary files /dev/null and b/edera-api/api/src/test/resources/sample-open-office.docx differ
diff --git a/edera-api/bitbucket-pipelines.yml b/edera-api/bitbucket-pipelines.yml
new file mode 100644 (file)
index 0000000..92a70a2
--- /dev/null
@@ -0,0 +1,101 @@
+image: google/cloud-sdk:412.0.0-slim
+definitions:
+  steps:
+    - step: &use-api-module
+        name: "Use api module"
+        script:
+          - export MODULE="api"
+          - mkdir ./build
+          - echo $MODULE > ./build/MODULE
+        artifacts:
+          - build/**
+    - step: &build
+        name: "Build"
+        caches:
+          - maven
+          - pip
+        script:
+          - export MODULE=$(cat ./build/MODULE)
+          - source scripts/set-env.sh
+          - apt update
+          - apt install -y openjdk-17-jdk maven
+          - cat scripts/tf/service-account.json.base64 | base64 --decode >> build/service-account.json
+          - gcloud auth activate-service-account --key-file build/service-account.json
+          - export REVISION=$(mvn help:evaluate -Dexpression=revision -q -DforceStdout)
+          - export VERSION=${REVISION}.${BITBUCKET_BUILD_NUMBER}
+          - echo $VERSION > ${MODULE}/src/main/resources/build.txt
+          - mvn -Drevision=${VERSION} -Dchangelist= package -DskipTests=true
+          - export EXECUTABLE="${MODULE}-${VERSION}.jar"
+          - echo cp "${MODULE}/target/${EXECUTABLE}" ./build
+          - cp "${MODULE}/target/${EXECUTABLE}" ./build
+          - echo $VERSION > ./build/VERSION
+          - echo $EXECUTABLE > ./build/EXECUTABLE
+        artifacts:
+          - build/**
+    - step: &publish
+        name: "Publish"
+        trigger: automatic
+        services:
+          - docker
+        script:
+          - source scripts/set-env.sh
+          - export EXECUTABLE=$(cat ./build/EXECUTABLE)
+          - export MODULE=$(cat ./build/MODULE)
+          - export VERSION=$(cat ./build/VERSION)
+          - export IMAGE_BASE="eu.gcr.io/${PROJECT_ID}/${NAMESPACE}-${MODULE}"
+          - export IMAGE="${IMAGE_BASE}:${VERSION}"
+          - export JAVA_ARGS=""
+          - export PROGRAM_ARGS=""
+          - gcloud auth activate-service-account --key-file build/service-account.json
+          - gcloud config set project ${PROJECT_ID}
+          - gcloud config set compute/zone europe-west1-b
+          - gcloud auth configure-docker
+          - >-
+            docker build -t ${IMAGE} \
+                --build-arg PROFILE=${PROFILE} \
+                --build-arg EXECUTABLE=${EXECUTABLE} \
+                --build-arg JAVA_ARGS=${JAVA_ARGS} \
+                --build-arg PROGRAM_ARGS=${PROGRAM_ARGS} \
+                --build-arg MAIN=${MAIN} \
+                -f ./scripts/docker/Dockerfile .
+          - docker push ${IMAGE}
+          - docker tag $IMAGE "${IMAGE_BASE}:latest"
+          - docker push "${IMAGE_BASE}:latest"
+          - git tag -a "${VERSION}" -m "version ${VERSION}"
+          - git push origin "${VERSION}"
+    - step: &deploy
+        name: Deploy
+        script:
+          - source scripts/set-env.sh
+          - export VERSION=$(cat ./build/VERSION)
+          - export EXECUTABLE=$(cat ./build/EXECUTABLE)
+          - export MODULE=$(cat ./build/MODULE)
+          - export IMAGE="eu.gcr.io/${PROJECT_ID}/${NAMESPACE}-${MODULE}:${VERSION}"
+          - gcloud auth activate-service-account --key-file build/service-account.json
+          - gcloud config set project ${PROJECT_ID}
+          - gcloud config set compute/zone europe-west1-b
+          - apt install kubectl
+          - apt install google-cloud-sdk-gke-gcloud-auth-plugin
+          - export USE_GKE_GCLOUD_AUTH_PLUGIN=True
+          - gcloud container clusters get-credentials applica
+          - kubectl set image deployment/${MODULE} ${MODULE}=${IMAGE} -n ${NAMESPACE}
+    - step: &test
+        image: maven:3.8.3-openjdk-17
+        name: Build & Test
+        caches:
+          - maven
+        script:
+          - mvn test -Dspring.profiles.active=test
+pipelines:
+  branches:
+    develop:
+      - step: *use-api-module
+      - step: *build
+      - step: *publish
+      - step: *deploy
+
+    feature/*:
+      - step: *test
+
+    hotfix/*:
+      - step: *test
diff --git a/edera-api/mvnw b/edera-api/mvnw
new file mode 100644 (file)
index 0000000..8a8fb22
--- /dev/null
@@ -0,0 +1,316 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /usr/local/etc/mavenrc ] ; then
+    . /usr/local/etc/mavenrc
+  fi
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`\\unset -f command; \\command -v java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    else
+      jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        else
+            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        fi
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl -o "$wrapperJarPath" "$jarUrl" -f
+        else
+            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+        fi
+
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  $MAVEN_DEBUG_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" \
+  "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/edera-api/mvnw.cmd b/edera-api/mvnw.cmd
new file mode 100644 (file)
index 0000000..1d8ab01
--- /dev/null
@@ -0,0 +1,188 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %DOWNLOAD_URL%
+    )
+
+    powershell -Command "&{"^
+               "$webclient = new-object System.Net.WebClient;"^
+               "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+               "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+               "}"^
+               "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+               "}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+  %JVM_CONFIG_MAVEN_PROPS% ^
+  %MAVEN_OPTS% ^
+  %MAVEN_DEBUG_OPTS% ^
+  -classpath %WRAPPER_JAR% ^
+  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/edera-api/notifications/pom.xml b/edera-api/notifications/pom.xml
new file mode 100644 (file)
index 0000000..a71945f
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>applica.app</groupId>
+        <artifactId>root</artifactId>
+        <version>${revision}${sha1}${changelist}</version>
+    </parent>
+
+    <artifactId>notifications</artifactId>
+    <version>${revision}${sha1}${changelist}</version>
+    <name>notifications</name>
+    <description>Manages user notifications and communications</description>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>applica.modules</groupId>
+            <artifactId>iam-sdk</artifactId>
+            <version>${applica-iam.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>${commons-lang3.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>${freemarker.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-tx</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/edera-api/notifications/src/main/java/applica/notifications/application/NotificationService.java b/edera-api/notifications/src/main/java/applica/notifications/application/NotificationService.java
new file mode 100644 (file)
index 0000000..e39803a
--- /dev/null
@@ -0,0 +1,5 @@
+package applica.notifications.application;
+
+public interface NotificationService {
+
+}
diff --git a/edera-api/notifications/src/main/java/applica/notifications/application/UserActivatedEventHandler.java b/edera-api/notifications/src/main/java/applica/notifications/application/UserActivatedEventHandler.java
new file mode 100644 (file)
index 0000000..e67e984
--- /dev/null
@@ -0,0 +1,44 @@
+package applica.notifications.application;
+
+import applica.integration.events.DomainEventHandler;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.events.UserActivatedEvent;
+import applica.iam.sdk.requests.GetUserByIdRequest;
+import applica.notifications.integration.MailMessage;
+import applica.notifications.integration.MailMessageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static applica.iam.sdk.ResponseCodes.OK;
+
+public class UserActivatedEventHandler extends DomainEventHandler<UserActivatedEvent> {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+    private final MailMessageService mailMessageService;
+    private final IAMService iamService;
+
+    public UserActivatedEventHandler(MailMessageService mailMessageService, IAMService iamService) {
+        this.mailMessageService = mailMessageService;
+        this.iamService = iamService;
+    }
+
+    @Override
+    public void handle(UserActivatedEvent event) {
+        logger.info("Handling user activated event: {}", event);
+
+        var response = iamService.getUserById(new GetUserByIdRequest(event.getUserId()));
+        if (!OK.equals(response.getResponseCode())) {
+            return;
+        }
+        var mailMessage = new MailMessage("activation", response.getUser());
+        mailMessage.setTo(response.getUser().getEmail());
+        mailMessage.setSubject("Activation completed successfully");
+        mailMessageService.send(mailMessage);
+
+        logger.info("Activation email sent to user: {}", response.getUser().getEmail());
+    }
+
+    @Override
+    public Class<?> getEventClass() {
+        return UserActivatedEvent.class;
+    }
+}
diff --git a/edera-api/notifications/src/main/java/applica/notifications/application/UserPasswordChangedEventHandler.java b/edera-api/notifications/src/main/java/applica/notifications/application/UserPasswordChangedEventHandler.java
new file mode 100644 (file)
index 0000000..b9fecc5
--- /dev/null
@@ -0,0 +1,45 @@
+package applica.notifications.application;
+
+import applica.integration.events.DomainEventHandler;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.events.UserPasswordChangedEvent;
+import applica.iam.sdk.requests.GetUserByIdRequest;
+import applica.notifications.integration.MailMessage;
+import applica.notifications.integration.MailMessageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static applica.iam.sdk.ResponseCodes.OK;
+
+public class UserPasswordChangedEventHandler extends DomainEventHandler<UserPasswordChangedEvent> {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+    private final MailMessageService mailMessageService;
+    private final IAMService iamService;
+
+    public UserPasswordChangedEventHandler(MailMessageService mailMessageService, IAMService iamService) {
+        this.mailMessageService = mailMessageService;
+        this.iamService = iamService;
+    }
+
+    @Override
+    public void handle(UserPasswordChangedEvent event) {
+        logger.info("Handling user password changed event: {}", event);
+
+        var response = iamService.getUserById(new GetUserByIdRequest(event.getUserId()));
+        if (!OK.equals(response.getResponseCode())) {
+            return;
+        }
+
+        var mailMessage = new MailMessage("passwordChanged", response.getUser());
+        mailMessage.setTo(response.getUser().getEmail());
+        mailMessage.setSubject("Password changed");
+        mailMessageService.send(mailMessage);
+
+        logger.info("Password changed email sent to user: {}", response.getUser().getEmail());
+    }
+
+    @Override
+    public Class<?> getEventClass() {
+        return UserPasswordChangedEvent.class;
+    }
+}
diff --git a/edera-api/notifications/src/main/java/applica/notifications/application/UserPasswordRecoveredEventHandler.java b/edera-api/notifications/src/main/java/applica/notifications/application/UserPasswordRecoveredEventHandler.java
new file mode 100644 (file)
index 0000000..f91ffd0
--- /dev/null
@@ -0,0 +1,49 @@
+package applica.notifications.application;
+
+import applica.integration.events.DomainEventHandler;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.events.UserPasswordRecoveredEvent;
+import applica.iam.sdk.requests.GetUserByIdRequest;
+import applica.notifications.integration.MailMessage;
+import applica.notifications.integration.MailMessageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+
+import static applica.iam.sdk.ResponseCodes.OK;
+
+public class UserPasswordRecoveredEventHandler extends DomainEventHandler<UserPasswordRecoveredEvent> {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+    private final MailMessageService mailMessageService;
+    private final IAMService iamService;
+
+    public UserPasswordRecoveredEventHandler(MailMessageService mailMessageService, IAMService iamService) {
+        this.mailMessageService = mailMessageService;
+        this.iamService = iamService;
+    }
+
+    @Override
+    public void handle(UserPasswordRecoveredEvent event) {
+        var response = iamService.getUserById(new GetUserByIdRequest(event.getUserId()));
+        if (!OK.equals(response.getResponseCode())) {
+            return;
+        }
+
+        var dataModel = new HashMap<String, Object>();
+        dataModel.put("user", response.getUser());
+        dataModel.put("newPassword", event.getPassword());
+
+        var mailMessage = new MailMessage("recover", dataModel);
+        mailMessage.setTo(response.getUser().getEmail());
+        mailMessage.setSubject("Password recovery completed successfully");
+        mailMessageService.send(mailMessage);
+
+        logger.info("Password recovery email sent to user: {}", response.getUser().getEmail());
+    }
+
+    @Override
+    public Class<?> getEventClass() {
+        return UserPasswordRecoveredEvent.class;
+    }
+}
diff --git a/edera-api/notifications/src/main/java/applica/notifications/application/UserRegisteredEventHandler.java b/edera-api/notifications/src/main/java/applica/notifications/application/UserRegisteredEventHandler.java
new file mode 100644 (file)
index 0000000..10a924d
--- /dev/null
@@ -0,0 +1,50 @@
+package applica.notifications.application;
+
+import applica.integration.events.DomainEventHandler;
+import applica.iam.sdk.IAMService;
+import applica.iam.sdk.events.UserRegisteredEvent;
+import applica.iam.sdk.requests.GetUserByIdRequest;
+import applica.notifications.integration.MailMessage;
+import applica.notifications.integration.MailMessageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+
+import static applica.iam.sdk.ResponseCodes.OK;
+
+public class UserRegisteredEventHandler extends DomainEventHandler<UserRegisteredEvent> {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+    private final MailMessageService mailMessageService;
+    private final IAMService iamService;
+
+    public UserRegisteredEventHandler(MailMessageService mailMessageService, IAMService iamService) {
+        this.mailMessageService = mailMessageService;
+        this.iamService = iamService;
+    }
+
+    @Override
+    public void handle(UserRegisteredEvent event) {
+        logger.info("Handling user registered event: {}", event);
+        var response = iamService.getUserById(new GetUserByIdRequest(event.getUserId()));
+        if (!OK.equals(response.getResponseCode())) {
+            return;
+        }
+
+        var dataModel = new HashMap<String, Object>();
+        dataModel.put("user", response.getUser());
+        dataModel.put("activationCode", event.getActivationCode());
+        dataModel.put("domainName", getDomainName());
+        dataModel.put("password", event.getGeneratedPassword());
+
+        var mailMessage = new MailMessage("registration", dataModel);
+        mailMessage.setTo(response.getUser().getEmail());
+        mailMessage.setSubject("Registration completed successfully");
+        mailMessageService.send(mailMessage);
+    }
+
+    @Override
+    public Class<?> getEventClass() {
+        return UserRegisteredEvent.class;
+    }
+}
diff --git a/edera-api/notifications/src/main/java/applica/notifications/integration/DefaultMailMessageService.java b/edera-api/notifications/src/main/java/applica/notifications/integration/DefaultMailMessageService.java
new file mode 100644 (file)
index 0000000..79f62f1
--- /dev/null
@@ -0,0 +1,55 @@
+package applica.notifications.integration;
+
+import freemarker.template.TemplateException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.MailSender;
+import org.springframework.util.StringUtils;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+@SuppressWarnings("unused")
+public class DefaultMailMessageService implements MailMessageService {
+    final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Autowired
+    private FreemarkerHelper freemarker;
+
+    @Autowired
+    private MailSender mailSender;
+
+    @Value("${mail.from}")
+    private String defaultForm;
+
+    @Override
+    public void send(MailMessage mailMessage) {
+
+        if (!StringUtils.hasLength(mailMessage.getFrom())) {
+            mailMessage.setFrom(defaultForm);
+        }
+        mailMessage.setText(generateBody(mailMessage));
+        mailSender.send(mailMessage);
+    }
+
+    @Override
+    public void send(MailMessage... messages) {
+        for (MailMessage message : messages) {
+            send(message);
+        }
+    }
+
+    private String generateBody(MailMessage mailMessage) {
+        var writer = new StringWriter();
+        try {
+            var template = freemarker.getTemplate(mailMessage.getTemplate());
+            template.process(mailMessage.getDataModel(), writer);
+        } catch (TemplateException | IOException e) {
+            logger.error("Unable to process mail registration template", e);
+            return null;
+        }
+        return writer.toString();
+    }
+}
diff --git a/edera-api/notifications/src/main/java/applica/notifications/integration/FreemarkerHelper.java b/edera-api/notifications/src/main/java/applica/notifications/integration/FreemarkerHelper.java
new file mode 100644 (file)
index 0000000..0451b61
--- /dev/null
@@ -0,0 +1,19 @@
+package applica.notifications.integration;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+
+import java.io.IOException;
+
+public class FreemarkerHelper {
+    final Configuration configuration;
+
+    public FreemarkerHelper(String templatePath) {
+        configuration = new Configuration(Configuration.VERSION_2_3_31);
+        configuration.setClassLoaderForTemplateLoading(getClass().getClassLoader(), templatePath);
+    }
+
+    public Template getTemplate(String name) throws IOException {
+        return configuration.getTemplate(name);
+    }
+}
diff --git a/edera-api/notifications/src/main/java/applica/notifications/integration/MailMessage.java b/edera-api/notifications/src/main/java/applica/notifications/integration/MailMessage.java
new file mode 100644 (file)
index 0000000..8b2788d
--- /dev/null
@@ -0,0 +1,23 @@
+package applica.notifications.integration;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.mail.SimpleMailMessage;
+
+public class MailMessage extends SimpleMailMessage {
+    @Getter
+    @Setter
+    private String template;
+
+    @Getter
+    @Setter
+    private Object dataModel;
+
+    public MailMessage(String template, Object dataModel) {
+        if (!template.endsWith(".ftlh")) {
+            template += ".ftlh";
+        }
+        this.template = template;
+        this.dataModel = dataModel;
+    }
+}
diff --git a/edera-api/notifications/src/main/java/applica/notifications/integration/MailMessageService.java b/edera-api/notifications/src/main/java/applica/notifications/integration/MailMessageService.java
new file mode 100644 (file)
index 0000000..9e70fd5
--- /dev/null
@@ -0,0 +1,6 @@
+package applica.notifications.integration;
+
+public interface MailMessageService {
+    void send(MailMessage message);
+    void send(MailMessage ... messages);
+}
diff --git a/edera-api/notifications/src/main/resources/templates/activation.ftlh b/edera-api/notifications/src/main/resources/templates/activation.ftlh
new file mode 100644 (file)
index 0000000..12e176d
--- /dev/null
@@ -0,0 +1,3 @@
+Buongiorno ${profile.name}
+
+Il tuo account Ã¨ stato attivato con successo
\ No newline at end of file
diff --git a/edera-api/notifications/src/main/resources/templates/passwordChanged.ftlh b/edera-api/notifications/src/main/resources/templates/passwordChanged.ftlh
new file mode 100644 (file)
index 0000000..da69c12
--- /dev/null
@@ -0,0 +1,3 @@
+Buongiorno ${profile.name}
+
+La tua password Ã¨ stata cambiata correttamente
\ No newline at end of file
diff --git a/edera-api/notifications/src/main/resources/templates/recover.ftlh b/edera-api/notifications/src/main/resources/templates/recover.ftlh
new file mode 100644 (file)
index 0000000..96a0f82
--- /dev/null
@@ -0,0 +1,3 @@
+Buongiorno ${user.profile.name}
+
+La tua password Ã¨ stata cambiata con la seguente: ${newPassword}
\ No newline at end of file
diff --git a/edera-api/notifications/src/main/resources/templates/registration.ftlh b/edera-api/notifications/src/main/resources/templates/registration.ftlh
new file mode 100644 (file)
index 0000000..8b227ea
--- /dev/null
@@ -0,0 +1,7 @@
+Gentile ${user.profile.name},
+di seguito i tuoi dati di accesso:
+
+Username: ${user.email}
+Password: ${password}
+
+Puoi accedere alla piattaforma <a href="${domainName}/" rel="link">cliccando qui</a>.
\ No newline at end of file
diff --git a/edera-api/pom.xml b/edera-api/pom.xml
new file mode 100644 (file)
index 0000000..cdf13c5
--- /dev/null
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.0.7</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>applica.app</groupId>
+    <artifactId>root</artifactId>
+    <version>${revision}${sha1}${changelist}</version>
+    <packaging>pom</packaging>
+
+    <repositories>
+        <repository>
+            <id>central</id>
+            <name>Default Repository</name>
+            <layout>default</layout>
+            <url>https://repo1.maven.org/maven2</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+        </repository>
+        <repository>
+            <id>artifact-registry</id>
+            <url>artifactregistry://europe-west1-maven.pkg.dev/applica-general/applica-maven</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>true</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
+    <properties>
+        <revision>1.0</revision>
+        <changelist>-SNAPSHOT</changelist>
+        <sha1/>
+        <java.version>17</java.version>
+        <maven.flatten.plugin>1.3.0</maven.flatten.plugin>
+        <jjwt.version>0.9.1</jjwt.version>
+        <jaxb-api.version>2.3.1</jaxb-api.version>
+        <commons-validator.version>1.7</commons-validator.version>
+        <commons-beanutils.version>1.9.4</commons-beanutils.version>
+        <commons-io.version>2.11.0</commons-io.version>
+        <jmimemagic.version>0.1.2</jmimemagic.version>
+        <imgscalr.version>4.2</imgscalr.version>
+        <apache-commons-imaging.version>1.0-alpha2</apache-commons-imaging.version>
+        <google-cloud-storage.version>2.16.0</google-cloud-storage.version>
+        <httpcomponents-httpclient.version>4.5.14</httpcomponents-httpclient.version>
+        <springdoc-openapi.version>2.0.2</springdoc-openapi.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <applica-fs.version>1.0.3</applica-fs.version>
+        <applica-iam.version>1.0.13</applica-iam.version>
+        <applica-crud.version>1.0.24</applica-crud.version>
+    </properties>
+
+    <modules>
+        <module>api</module>
+        <module>notifications</module>
+    </modules>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>flatten-maven-plugin</artifactId>
+                <version>${maven.flatten.plugin}</version>
+                <configuration>
+                    <updatePomFile>true</updatePomFile>
+                    <flattenMode>resolveCiFriendliesOnly</flattenMode>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>flatten</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>flatten</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>flatten.clean</id>
+                        <phase>clean</phase>
+                        <goals>
+                            <goal>clean</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M7</version>
+            </plugin>
+        </plugins>
+
+        <extensions>
+            <extension>
+                <groupId>com.google.cloud.artifactregistry</groupId>
+                <artifactId>artifactregistry-maven-wagon</artifactId>
+                <version>2.1.0</version>
+            </extension>
+        </extensions>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/edera-api/scripts/create-infrastructure.sh b/edera-api/scripts/create-infrastructure.sh
new file mode 100644 (file)
index 0000000..4c7fad0
--- /dev/null
@@ -0,0 +1,4 @@
+source set-env.sh
+cd tf
+terraform init
+terraform apply -var="project_id=${PROJECT_ID}" -var="project_name=${PROJECT_NAME}"
\ No newline at end of file
diff --git a/edera-api/scripts/docker/Dockerfile b/edera-api/scripts/docker/Dockerfile
new file mode 100644 (file)
index 0000000..38e20d0
--- /dev/null
@@ -0,0 +1,16 @@
+FROM openjdk:17
+LABEL maintainer Bruno Fortunato (www.applica.guru)
+
+VOLUME /fileserver
+
+ARG PROFILE=production
+ARG EXECUTABLE=applica.api.Application
+ARG PORT=8080
+ARG JAVA_ARGS=""
+ARG PROGRAM_ARGS=""
+EXPOSE $PORT
+
+COPY build/* /app/
+
+RUN echo java $JAVA_ARGS -jar "/app/$EXECUTABLE" --spring.profiles.active=$PROFILE $PROGRAM_ARGS >> /app/entrypoint.sh
+ENTRYPOINT ["/bin/bash", "/app/entrypoint.sh"]
diff --git a/edera-api/scripts/init-gcloud.sh b/edera-api/scripts/init-gcloud.sh
new file mode 100644 (file)
index 0000000..e35f720
--- /dev/null
@@ -0,0 +1,7 @@
+source set-env.sh
+gcloud config configurations create $PROJECT_ID
+gcloud config set project $PROJECT_ID
+gcloud auth login
+gcloud config set compute/region europe-west1
+gcloud config set compute/zone europe-west1-b
+gcloud config configurations list
\ No newline at end of file
diff --git a/edera-api/scripts/init-k8s.sh b/edera-api/scripts/init-k8s.sh
new file mode 100644 (file)
index 0000000..cf0ed0f
--- /dev/null
@@ -0,0 +1,8 @@
+source set-env.sh
+gcloud config configurations activate $PROJECT_ID
+gcloud container clusters get-credentials default
+kubectl get deployments
+envsubst < mongodb.yml | kubectl apply -f -
+envsubst < api.yml | kubectl apply -f -
+envsubst < web.yml | kubectl apply -f -
+envsubst < ingress.yml | kubectl apply -f -
diff --git a/edera-api/scripts/kube/api.yml b/edera-api/scripts/kube/api.yml
new file mode 100644 (file)
index 0000000..2b97013
--- /dev/null
@@ -0,0 +1,53 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: api
+  labels:
+    app: api
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: api
+  template:
+    metadata:
+      labels:
+        app: api
+    spec:
+      containers:
+        - image: eu.gcr.io/applica-general/edera-api:latest
+          name: api
+          ports:
+            - containerPort: 8080
+              name: api
+          env:
+            - name: DOMAIN_NAME
+              value: https://edera.applica.guru
+          livenessProbe:
+            httpGet:
+              path: /api/actuator/health/liveness
+              port: api
+            initialDelaySeconds: 30
+            periodSeconds: 30
+          readinessProbe:
+            httpGet:
+              path: /api/actuator/health/readiness
+              port: api
+            initialDelaySeconds: 30
+            periodSeconds: 30
+          imagePullPolicy: Always
+---
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    app: api
+  name: api
+spec:
+  type: NodePort
+  ports:
+    - port: 8080
+      targetPort: 8080
+      protocol: TCP
+  selector:
+    app: api
diff --git a/edera-api/scripts/kube/ingress.yml b/edera-api/scripts/kube/ingress.yml
new file mode 100644 (file)
index 0000000..16d5a71
--- /dev/null
@@ -0,0 +1,28 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: app-ingress
+  annotations:
+    cert-manager.io/cluster-issuer: letsencrypt-production
+    ingress.kubernetes.io/add-base-url: "false"
+    ingress.kubernetes.io/rewrite-target: /
+    kubernetes.io/ingress.class: "nginx"
+    kubernetes.io/ingress.allow-http: "true"
+    nginx.ingress.kubernetes.io/ssl-redirect: "false"
+spec:
+  ingressClassName: nginx
+  tls:
+    - hosts:
+        - edera.applica.guru
+      secretName: edera-secret
+  rules:
+    - host: edera.applica.guru
+      http:
+        paths:
+          - pathType: Prefix
+            backend:
+              service:
+                name: api
+                port:
+                  number: 8080
+            path: /api
\ No newline at end of file
diff --git a/edera-api/scripts/set-env.sh b/edera-api/scripts/set-env.sh
new file mode 100644 (file)
index 0000000..4255e5a
--- /dev/null
@@ -0,0 +1,4 @@
+export PROJECT_ID=applica-general
+export PROJECT_NAME=applica-general
+export NAMESPACE=edera
+export PROFILE=production
\ No newline at end of file
diff --git a/edera-api/scripts/tf/main.tf b/edera-api/scripts/tf/main.tf
new file mode 100644 (file)
index 0000000..4ddc2c4
--- /dev/null
@@ -0,0 +1,75 @@
+data "google_billing_account" "billing_account" {
+  billing_account = var.billing_account
+  open = true
+}
+
+resource "google_project" "project" {
+  name       = var.project_name
+  project_id = var.project_id
+
+  billing_account = data.google_billing_account.billing_account.billing_account
+}
+
+resource "google_service_account" "service_account" {
+  account_id = "automation"
+
+  depends_on = [google_project.project]
+}
+
+resource "google_project_iam_member" "gcr_publisher_storage_admin" {
+  project = var.project_id
+  role   = "roles/owner"
+  member = "serviceAccount:${google_service_account.service_account.email}"
+}
+
+resource "google_service_account_key" "service_account_key" {
+  service_account_id = google_service_account.service_account.account_id
+}
+
+resource "local_file" "service_account_key_file" {
+  content  = google_service_account_key.service_account_key.private_key
+  filename = "${path.module}/service-account.json.base64"
+}
+
+resource "google_project_service" "container_registry_service" {
+  project = var.project_id
+  service = "containerregistry.googleapis.com"
+
+  depends_on = [google_project.project]
+}
+/*
+resource "google_project_service" "container_service" {
+  project = var.project_id
+  service = "container.googleapis.com"
+
+  depends_on = [google_project.project]
+}
+*/
+resource "google_container_registry" "container_registry" {
+  location = "EU"
+
+  depends_on = [google_project_service.container_registry_service]
+}
+
+/*
+resource "google_container_cluster" "primary" {
+  name = "default"
+  location = var.zone
+  initial_node_count = 1
+  node_config {
+    service_account = google_service_account.service_account.email
+    machine_type = "e2-micro"
+    oauth_scopes = [
+      "https://www.googleapis.com/auth/cloud-platform"
+    ]
+  }
+  depends_on = [google_project.container_service]
+}
+
+resource "google_compute_address" "ip_address" {
+  name = "external-ip"
+
+  depends_on = [google_project.project]
+}
+
+*/
\ No newline at end of file
diff --git a/edera-api/scripts/tf/mongo-atlas.tf b/edera-api/scripts/tf/mongo-atlas.tf
new file mode 100644 (file)
index 0000000..5d8916d
--- /dev/null
@@ -0,0 +1,60 @@
+# resource "random_password" "password" {
+#   length           = 16
+#   special          = true
+#   override_special = "!#$%&*()-_=+[]{}<>:?"
+# }
+#
+# locals {
+#   mongodb_atlas_api_pub_key = var.mongodb_atlas_public_key
+#   mongodb_atlas_api_pri_key = var.mongodb_atlas_private_key
+#   mongodb_atlas_org_id  = var.mongodb_atlas_org_id
+#
+#   mongodb_atlas_database_username = var.project_id
+#   mongodb_atlas_database_user_password = random_password.password.result
+# }
+#
+#
+# resource "mongodbatlas_project" "project" {
+#   name   = var.project_name
+#   org_id = var.mongodb_atlas_org_id
+# }
+#
+# resource "mongodbatlas_cluster" "cluster" {
+#   project_id              = mongodbatlas_project.project.id
+#   name                    = "default-cluster"
+#
+#   provider_name = "TENANT"
+#   backing_provider_name = "GCP"
+#   provider_region_name = "WESTERN_EUROPE"
+#   provider_instance_size_name = "M0"
+# }
+#
+# resource "mongodbatlas_database_user" "user" {
+#   username           = local.mongodb_atlas_database_username
+#   password           = local.mongodb_atlas_database_user_password
+#   project_id         = mongodbatlas_project.project.id
+#   auth_database_name = "admin"
+#
+#   roles {
+#     role_name     = "readWriteAnyDatabase"
+#     database_name = "admin"
+#   }
+# }
+#
+# resource "mongodbatlas_project_ip_access_list" "my_ipaddress" {
+#       project_id = mongodbatlas_project.project.id
+#       cidr_block = "0.0.0.0/0"
+#       comment    = "Everyone (dev only)"
+# }
+#
+# output "mongo_connection_string" {
+#   value = mongodbatlas_cluster.cluster.connection_strings.0.standard_srv
+# }
+#
+# output "mongo_credentials" {
+#   value = {
+#     user = local.mongodb_atlas_database_username
+#     password = local.mongodb_atlas_database_user_password
+#   }
+#   sensitive = true
+# }
\ No newline at end of file
diff --git a/edera-api/scripts/tf/provider.tf b/edera-api/scripts/tf/provider.tf
new file mode 100644 (file)
index 0000000..93b069f
--- /dev/null
@@ -0,0 +1,5 @@
+provider "google" {
+  project = var.project_id
+  region = var.region
+  zone = var.zone
+}
\ No newline at end of file
diff --git a/edera-api/scripts/tf/service-account.json.base64 b/edera-api/scripts/tf/service-account.json.base64
new file mode 100644 (file)
index 0000000..6841642
--- /dev/null
@@ -0,0 +1,42 @@
+ewogICJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIsCiAgInByb2plY3RfaWQiOiAiYXBwbGljYS1n
+ZW5lcmFsIiwKICAicHJpdmF0ZV9rZXlfaWQiOiAiNDk3YzZmNTU5N2Y0NTI3NWY3ZTlmZTQwMzFj
+NGI4MjllMjY4NzEyMSIsCiAgInByaXZhdGVfa2V5IjogIi0tLS0tQkVHSU4gUFJJVkFURSBLRVkt
+LS0tLVxuTUlJRXZnSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS2d3Z2dTa0FnRUFBb0lCQVFD
+dDRrY1B3UVJ6bUQ3b1xucHlVQVBlTnBnS01TWGxTM25CYmVtSUFlcmk3dm5GeHJIMFV4RlJndmRN
+VTlzRTJ6L0h5WXNTMGNnR1R3Y01Fd1xuQlVVYVF3UWpUMFVxdlBrN3ByY2lOWURSTXVNQXY1MW5Y
+WkpRZ0RYeitMSG15VldhaE1VRTlMczM4YzJxeUFpalxuVTBjakhqTmxsMmp3S2J5OWJVNUVUcWlT
+eEtxdE1FRVlwSUFzV2kvWG1iekx6NlVtSnladm56ZCtkRFNRQTU3aFxuY0NsOEFTdlBpQTdIeXVz
+b1lzVWJ2S2xRS0tmcndtV3NBUllEVjdRc0t6bEpsR0ViWE1sWlozdzB1VCszVkxXOVxuTWMyS1Fo
+SGpvaWpoTW9yRjdVdjVWMjJrTWkyL2V4RkI3L3ErbllCejU5K1MxUXZlRmpCd3E5L0gwQTRyTXpU
+a1xubWZDYzhjMWZBZ01CQUFFQ2dnRUFOR1dsdlRrUUl0Y2pTYzhvSnJEL2lLaXpPeE5DMnd0Rmx2
+RUVWbnB0ZVZXNFxuUWExc0Y3VEFFM2pRQU4xU0pPVDJGTHI3R1lZVkpLRU5qZTlnbWQvTTdPanpz
+a084cEwyQm5PVGJldTZuR2ZBalxudWVTbjlPc1ZsdjEvZWtoOEs3Skxma2xTNnpKSm8rZGdOdnNl
+eWhYTkxoVllrVm82WGlpRWQ2L3VPei9aSUpPVVxuTFlHN1hPVHExNWhKcmZaM2trdDBBRldJc2NK
+SXVFbFBlUTJOaDVWY3VSeVJwUVQwaFA0NUlRUTZFdElBWXp4OVxuUnRGUWFhd2tpWVFqZ0RkelFO
+Qm1tUHN2ZWc4Y2JkQXlWZVlxcitaVkFiNk1pWjJ2N01RR0hyeEFBcnhSeXRMZlxuMmd2VUl0K3pv
+dmJBemo3Q3h0YjUvS3JDdHUwMC9aWTcwNzRxTExnR1NRS0JnUUR2NXRkNmxWbUg5cXl3TThTc1xu
+NnA5NE5lTVFrbU9vME41SjBUUisxTGFITnBkdjBaY3B0bEJDS0lCTXorVTBBc0p0cG11dlpGTGdH
+cmp6U3E2cFxuVFlGUis0Mlc1NjJGMkoxWmlKZmdnWDdubWp3TmhUNzdrdWUwbjVWbE1iTHBUN0Iz
+ajB6V3Ftc0hqZ09lZWhDblxueEIwOXpKcXJEYm96MkV3UGVvRkhDVjB5dHdLQmdRQzVqVmpJd21F
+d01PU2g5UHF3QzJ5SVBYeS81UkpGTG4yL1xuNWxzUmhRbkkrTkxTZUJjWnRIV1BiOWcyNVF6SFVV
+dWRRNGhiYWpHaDM1R2t5SGtIcjJ5MGVjdTFUd2pVTitGMFxubzdhejRzekk4a3kzRk9sRUpvdXM4
+enFNQWsrYnhFR1hrUUNkWmFiL0ltS0svR1dRK21RcXlDV0RtUkFvR3NVcVxuTEJNdUhKOXltUUtC
+Z0YrRnJBRGNYT1RkWEk5Z1hZeDRjM3piQUFtR01IWjBqRDRhTmV2V2FNTllBbDU4dHRMZVxuREFE
+N3ZYSllTU3czZVJGTjlZekZ4cFlETGVkNXNpZ3BlemVZa1Iwb0xKaWgwcTFtelFxUXBXWTBySHE1
+dG9WWFxuVGpsR1hhY0liZk9tVGw2Y3lYeWtLSysrWlVTQjJBWGsrYnUwcjFVeXh4U0RxRzExV3Vw
+ZEdTWHJBb0dCQUo2YVxucm9CMGZvU2wxbGlGd2Q3RzlRK0RsMldqMWJraTQwUXNFRDNxZlJHM2R1
+V0cxeUFXdThKT3RQOC9UR3YzRm00blxuc3ArSkowR1ppN0hSMW5wMlBiSUt4ZENGN1NNUlhQckpr
+YnN6cXg0ODFzeEw2SlJqYWxMOFdWZ2lCWkE4OG1BdlxuQnRxRGNIcDNGc3A4c2doNXJ6Tk9mNXA4
+Tkc1RGE3TC9sNmw3dCtOSkFvR0JBT2d6MHM1WGErRzZLTmlVN0IySFxuc0l2MmRNMzZvNlZ3a21P
+T3FjdCtkVS8ycG12ZGM4aFpDbFZGYXBQeWZ4djFqVnNjSDUrZG5FaFJIaTEzYXdIU1xuVzc3MnQ5
+WUdBNWpGb2dnakVPRU5QbVR2QUwxd01NL0ExRCtmanVaWVArOEVDaEU0QmFmbVN3WWZGVzJRQmxE
+eFxuZlhoOHNmT0FnMjhuMEc1QWNGcXZVSFJNXG4tLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tXG4i
+LAogICJjbGllbnRfZW1haWwiOiAiYml0YnVja2V0LXBpcGVsaW5lc0BhcHBsaWNhLWdlbmVyYWwu
+aWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICJjbGllbnRfaWQiOiAiMTA4Mzk0ODE1Njc3NDgx
+NDI2MjYwIiwKICAiYXV0aF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vb2F1
+dGgyL2F1dGgiLAogICJ0b2tlbl91cmkiOiAiaHR0cHM6Ly9vYXV0aDIuZ29vZ2xlYXBpcy5jb20v
+dG9rZW4iLAogICJhdXRoX3Byb3ZpZGVyX3g1MDlfY2VydF91cmwiOiAiaHR0cHM6Ly93d3cuZ29v
+Z2xlYXBpcy5jb20vb2F1dGgyL3YxL2NlcnRzIiwKICAiY2xpZW50X3g1MDlfY2VydF91cmwiOiAi
+aHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vcm9ib3QvdjEvbWV0YWRhdGEveDUwOS9iaXRidWNr
+ZXQtcGlwZWxpbmVzJTQwYXBwbGljYS1nZW5lcmFsLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwK
+ICAidW5pdmVyc2VfZG9tYWluIjogImdvb2dsZWFwaXMuY29tIgp9Cg==
\ No newline at end of file
diff --git a/edera-api/scripts/tf/terraform.tfstate b/edera-api/scripts/tf/terraform.tfstate
new file mode 100644 (file)
index 0000000..93fa8c2
--- /dev/null
@@ -0,0 +1,238 @@
+{
+  "version": 4,
+  "terraform_version": "1.3.6",
+  "serial": 92,
+  "lineage": "55ea8741-2812-cc86-b261-6b858399bdf0",
+  "outputs": {},
+  "resources": [
+    {
+      "mode": "data",
+      "type": "google_billing_account",
+      "name": "billing_account",
+      "provider": "provider[\"registry.terraform.io/hashicorp/google\"]",
+      "instances": [
+        {
+          "schema_version": 0,
+          "attributes": {
+            "billing_account": "011783-8751C0-5C335B",
+            "display_name": "My Billing Account 1",
+            "id": "011783-8751C0-5C335B",
+            "name": "billingAccounts/011783-8751C0-5C335B",
+            "open": true,
+            "project_ids": [
+              "archetype-api-1",
+              "archetype-api-2",
+              "commodo-onepunch",
+              "commodo-spaziorelax",
+              "compost-community",
+              "inponzio-257109",
+              "medical-viewer"
+            ]
+          },
+          "sensitive_attributes": []
+        }
+      ]
+    },
+    {
+      "mode": "managed",
+      "type": "google_container_registry",
+      "name": "container_registry",
+      "provider": "provider[\"registry.terraform.io/hashicorp/google\"]",
+      "instances": [
+        {
+          "schema_version": 0,
+          "attributes": {
+            "bucket_self_link": "https://www.googleapis.com/storage/v1/b/eu.artifacts.archetype-api-2.appspot.com",
+            "id": "eu.artifacts.archetype-api-2.appspot.com",
+            "location": "EU",
+            "project": null
+          },
+          "sensitive_attributes": [],
+          "private": "bnVsbA==",
+          "dependencies": [
+            "data.google_billing_account.billing_account",
+            "google_project.project",
+            "google_project_service.container_registry_service"
+          ]
+        }
+      ]
+    },
+    {
+      "mode": "managed",
+      "type": "google_project",
+      "name": "project",
+      "provider": "provider[\"registry.terraform.io/hashicorp/google\"]",
+      "instances": [
+        {
+          "schema_version": 1,
+          "attributes": {
+            "auto_create_network": true,
+            "billing_account": "011783-8751C0-5C335B",
+            "folder_id": null,
+            "id": "projects/archetype-api-2",
+            "labels": {},
+            "name": "archetype-api",
+            "number": "825727680893",
+            "org_id": null,
+            "project_id": "archetype-api-2",
+            "skip_delete": null,
+            "timeouts": null
+          },
+          "sensitive_attributes": [],
+          "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6NjAwMDAwMDAwMDAwLCJyZWFkIjo2MDAwMDAwMDAwMDAsInVwZGF0ZSI6NjAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=",
+          "dependencies": [
+            "data.google_billing_account.billing_account"
+          ]
+        }
+      ]
+    },
+    {
+      "mode": "managed",
+      "type": "google_project_iam_member",
+      "name": "gcr_publisher_storage_admin",
+      "provider": "provider[\"registry.terraform.io/hashicorp/google\"]",
+      "instances": [
+        {
+          "schema_version": 0,
+          "attributes": {
+            "condition": [],
+            "etag": "BwXxh/eZ+/0=",
+            "id": "archetype-api-2/roles/owner/serviceAccount:automation@archetype-api-2.iam.gserviceaccount.com",
+            "member": "serviceAccount:automation@archetype-api-2.iam.gserviceaccount.com",
+            "project": "archetype-api-2",
+            "role": "roles/owner"
+          },
+          "sensitive_attributes": [],
+          "private": "bnVsbA==",
+          "dependencies": [
+            "google_service_account.service_account"
+          ]
+        }
+      ]
+    },
+    {
+      "mode": "managed",
+      "type": "google_project_service",
+      "name": "container_registry_service",
+      "provider": "provider[\"registry.terraform.io/hashicorp/google\"]",
+      "instances": [
+        {
+          "schema_version": 0,
+          "attributes": {
+            "disable_dependent_services": null,
+            "disable_on_destroy": true,
+            "id": "archetype-api-2/containerregistry.googleapis.com",
+            "project": "archetype-api-2",
+            "service": "containerregistry.googleapis.com",
+            "timeouts": null
+          },
+          "sensitive_attributes": [],
+          "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInJlYWQiOjYwMDAwMDAwMDAwMCwidXBkYXRlIjoxMjAwMDAwMDAwMDAwfX0=",
+          "dependencies": [
+            "data.google_billing_account.billing_account",
+            "google_project.project"
+          ]
+        }
+      ]
+    },
+    {
+      "mode": "managed",
+      "type": "google_service_account",
+      "name": "service_account",
+      "provider": "provider[\"registry.terraform.io/hashicorp/google\"]",
+      "instances": [
+        {
+          "schema_version": 0,
+          "attributes": {
+            "account_id": "automation",
+            "description": "",
+            "disabled": false,
+            "display_name": "",
+            "email": "automation@archetype-api-2.iam.gserviceaccount.com",
+            "id": "projects/archetype-api-2/serviceAccounts/automation@archetype-api-2.iam.gserviceaccount.com",
+            "member": "serviceAccount:automation@archetype-api-2.iam.gserviceaccount.com",
+            "name": "projects/archetype-api-2/serviceAccounts/automation@archetype-api-2.iam.gserviceaccount.com",
+            "project": "archetype-api-2",
+            "timeouts": null,
+            "unique_id": "110113293688327852504"
+          },
+          "sensitive_attributes": [],
+          "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDB9fQ==",
+          "dependencies": [
+            "data.google_billing_account.billing_account",
+            "google_project.project"
+          ]
+        }
+      ]
+    },
+    {
+      "mode": "managed",
+      "type": "google_service_account_key",
+      "name": "service_account_key",
+      "provider": "provider[\"registry.terraform.io/hashicorp/google\"]",
+      "instances": [
+        {
+          "schema_version": 0,
+          "attributes": {
+            "id": "projects/archetype-api-2/serviceAccounts/automation@archetype-api-2.iam.gserviceaccount.com/keys/b46d12f9f90569f6a6ffb813b8033dfbce52f491",
+            "keepers": null,
+            "key_algorithm": "KEY_ALG_RSA_2048",
+            "name": "projects/archetype-api-2/serviceAccounts/automation@archetype-api-2.iam.gserviceaccount.com/keys/b46d12f9f90569f6a6ffb813b8033dfbce52f491",
+            "private_key": "ewogICJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIsCiAgInByb2plY3RfaWQiOiAiYXJjaGV0eXBlLWFwaS0yIiwKICAicHJpdmF0ZV9rZXlfaWQiOiAiYjQ2ZDEyZjlmOTA1NjlmNmE2ZmZiODEzYjgwMzNkZmJjZTUyZjQ5MSIsCiAgInByaXZhdGVfa2V5IjogIi0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLVxuTUlJRXZBSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS1l3Z2dTaUFnRUFBb0lCQVFDeXhMbFQvWFZuR1llblxuMG45U3ZXU3lnYW5PT2hGcE11V1Z3S3hvSlZtL241bnU0U2hWcHVVenNOQTdNL0YwNEw1RFBlNUg4ZWVTVE1Gd1xuZkJZWWZHZmZraWFlVFBzNm11SkpINzdDa1QvTTBDTUVVU2dBTjBtRVNUNWxtTnFIY1dNSTFheFE3VVR2Rndpc1xuTUh6SHRSVmtKaXlmbSt5MnNGSzByVk00V3NBUFNEU2hyYTVUQnBnSjlEQTdEOUZ0Z2VhTHg3WUNPNWFSMEN1NFxubjZjZjdLTzFwNjNvZFNsM2EwVEdxVGZ1V0dxRHNNbXoxUmI2NVlvM096YjMwczVxYi9wcVJZaGk4ekI3SUtDQ1xuWWVydVJhQ2UwczlBbTNwZ2FnY1NPVFJ1d3R0eTJobUZOdjZ3Nk1kUWdwSUpxSUI1b0hGKzRLUXdNaFJWbkd2UVxuWktlUXpHNzdBZ01CQUFFQ2dnRUFESFhaMVJqZGpnb2hnbmtSbHRyTW9nU1hxV0F5eG5wZHRrcHZ1ZktnaHRMQlxuRC8xeHFrYXZ3eUtYNFFpRjAvZTJTMG1OeWtYNk5pQlRlMXVxenBvZFEyQVVnUVFzbnJFZEJLdkp5QllIdEdidFxuaWVkK09rV2dRakE0d1N3ZVQwSi8vS1dvRlJ2MHFoV1k3U2VVQ3ZkOFBqYks5WFVRMHNzSGVNQjhCSG1PMllHNVxuQzJhMGc5Z3hIR2Zya1NtUDlpYzFDM1U4eU1QVE5Md3ZmU25URWkwU0FWKytUcVd2K1p5Z1ZZNENvWWxrN0h5SVxuNHZSTEdXZ1NibVBsL3FuU1hMVm9SN3FPK1JXZ1lxWnpkUmVwc2RGZVdhZ2kxSGNRcHRDMzlJMG1hU2taNHY0alxuVDhSR0tKNVExRVhLbDdKNENnUHhSN21QVGxZT08rcFUrU3hhdjA4SndRS0JnUURoLzFlbVZseUJ6QVNjM0h4NVxuUDl0M05xRU9BV0FEalduRHF2Y3N2MERPUXJYdTZ4Uzd4TlZkMjR1eHV2RWZITHFtMGhTWWJkT3FQTVVjOE5ZMFxuQnZvbktmbVdxQkJPcjIrK250V1ZrVU5DbUZieUQyaFFuN0ZmbnAxNFhDay9PNDBlR01CQ085RFljdmVmVXRKMVxubnNvQjFVS01yTXRyRzQ3YjZ5THVoNmhndlFLQmdRREtnRWJLM3M5NEszOExkbitOL3A4VkVWTUhLWjlHVWY4TlxuYWZFWWFvYlYvQW1HbHU2ZzJPSjFYZnR3dEw5Qk5aQVFGSkpzdnhzVExNVERXdFg5R3RlN001V0xUaUdSRkdMNlxuV1VrbXAwTHJGT3BhQlNnZHhEc3dXTjdSci81UXFvV1JTUkdIUjQrT3hzRkNxS2R1SWxrcWFjdHRtWVJ4aGNwRFxuQ0puVFNFR1dGd0tCZ0VmQkVzczFVRm5GdFJFNDBDeVBJZGRQK1FMQlhRTER1M2pzcDE0RnUwWEIySkhyQWNJOFxuVktKZ09wSkxrSk1ZUkFzRFdKYXRDQzljN0Jpc1B0WjJBS2ErcFFnNGhEVDNicnRQSXZGQ0ZlRG5EWFA5Z0ZsWVxuMnJCSlpDWDYzUDIrb3FlVHBEZGpWb1Bpdk14Uk41RXd3V0tqbTJXZTExZENnTEZDanV5OUZiRGhBb0dBR0RaV1xuUUpON2d3YlFYZktCTmQwbjhFRHVDSUUxaGhidnhBN1N3UFNid3FJc0VXZWlpS0RtRXRwMTRmZjZsalZ0VUQ3bFxuY3hNMmpZaGd6bXJpQXkxZWRnZW83Y3Nkd0ZjTHJwdFdYOFRILzR1MHFhYk1NU0x4WU1wL2VkcnRNWC95RUhrSVxuRzRDMjdYOWVSRFllTHREMGtGbXQ5U0RSOFREcUNqSFJFcTRsQ1drQ2dZQXNLaGUwbEloSHhYOG01aHVtblRiUFxuYkt1czMxemZ6NHFhdGJ0ZitkUlJZTi8wV3kveFBqOVNORjhSc1BZUlZOOVBRUXZCOUVzcExjbHNVaW5LMDArMFxuSk1jV2VmUHpJbVhlUU5GSEJzMUpGU1VtZlZEb3QrOEFPMm9OcXdST3dUcmxobllxK1FKK1V5b1lqS0R3bS9CMlxuR0JkVkw5V1V3NVpyZmxYbmU1THEydz09XG4tLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tXG4iLAogICJjbGllbnRfZW1haWwiOiAiYXV0b21hdGlvbkBhcmNoZXR5cGUtYXBpLTIuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICJjbGllbnRfaWQiOiAiMTEwMTEzMjkzNjg4MzI3ODUyNTA0IiwKICAiYXV0aF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vb2F1dGgyL2F1dGgiLAogICJ0b2tlbl91cmkiOiAiaHR0cHM6Ly9vYXV0aDIuZ29vZ2xlYXBpcy5jb20vdG9rZW4iLAogICJhdXRoX3Byb3ZpZGVyX3g1MDlfY2VydF91cmwiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vb2F1dGgyL3YxL2NlcnRzIiwKICAiY2xpZW50X3g1MDlfY2VydF91cmwiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vcm9ib3QvdjEvbWV0YWRhdGEveDUwOS9hdXRvbWF0aW9uJTQwYXJjaGV0eXBlLWFwaS0yLmlhbS5nc2VydmljZWFjY291bnQuY29tIgp9Cg==",
+            "private_key_type": "TYPE_GOOGLE_CREDENTIALS_FILE",
+            "public_key": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvRENDQWVTZ0F3SUJBZ0lJTjF6QmJlakpuOVl3RFFZSktvWklodmNOQVFFRkJRQXdJREVlTUJ3R0ExVUUKQXhNVk1URXdNVEV6TWprek5qZzRNekkzT0RVeU5UQTBNQ0FYRFRJek1ERXdOVEUxTWpRek4xb1lEems1T1RreApNak14TWpNMU9UVTVXakFnTVI0d0hBWURWUVFERXhVeE1UQXhNVE15T1RNMk9EZ3pNamM0TlRJMU1EUXdnZ0VpCk1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQ3l4TGxUL1hWbkdZZW4wbjlTdldTeWdhbk8KT2hGcE11V1Z3S3hvSlZtL241bnU0U2hWcHVVenNOQTdNL0YwNEw1RFBlNUg4ZWVTVE1Gd2ZCWVlmR2Zma2lhZQpUUHM2bXVKSkg3N0NrVC9NMENNRVVTZ0FOMG1FU1Q1bG1OcUhjV01JMWF4UTdVVHZGd2lzTUh6SHRSVmtKaXlmCm0reTJzRkswclZNNFdzQVBTRFNocmE1VEJwZ0o5REE3RDlGdGdlYUx4N1lDTzVhUjBDdTRuNmNmN0tPMXA2M28KZFNsM2EwVEdxVGZ1V0dxRHNNbXoxUmI2NVlvM096YjMwczVxYi9wcVJZaGk4ekI3SUtDQ1llcnVSYUNlMHM5QQptM3BnYWdjU09UUnV3dHR5MmhtRk52Nnc2TWRRZ3BJSnFJQjVvSEYrNEtRd01oUlZuR3ZRWktlUXpHNzdBZ01CCkFBR2pPREEyTUF3R0ExVWRFd0VCL3dRQ01BQXdEZ1lEVlIwUEFRSC9CQVFEQWdlQU1CWUdBMVVkSlFFQi93UU0KTUFvR0NDc0dBUVVGQndNQ01BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQ1BFYTB0V2Z6dmgrdHNBVzFRU0hrUwp5dnQyUUNYQXZob1Y1N01QVGtjbzJCa0h0VGRMN3Zvb0pLNjBxdXR3cVQxRWh1eHV1NjJ2b2k2enpIRjlNRitlCnRtS3U0SDZ6ODRjNy8vM2FJTDhuVnJwT3VSUzJBeHA4UEUzRUVSUENxU1BJcHNJVHE5d0M1aFFHNzhpSVFUeHQKcFQzejlWencxTHg5aHk3WTkrTVFiNHU5ZW5CWlVsalIyVWtrSHJDcGtFNEF4cUJYUlVyU2lFQnlwUUdBNlBnTwo1TXhGakZFbm4rNGk5eWhQZDYyVnl5Umt4TDNvYXFYUFpZL2psVHRucGdIRGU3QTV4aXBOdmR6QkRTbzM1Nmc1CmlsOE5lZ2ZVcTllYjdKRkg3RGJxQURpcTVPWGQxUm1TM0htSmVPQll2bEdZYlRKQjdmT3hOVHcrYlF4TE5CY2wKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=",
+            "public_key_data": null,
+            "public_key_type": "TYPE_X509_PEM_FILE",
+            "service_account_id": "automation",
+            "valid_after": "2023-01-05T15:24:37Z",
+            "valid_before": "9999-12-31T23:59:59Z"
+          },
+          "sensitive_attributes": [],
+          "private": "bnVsbA==",
+          "dependencies": [
+            "data.google_billing_account.billing_account",
+            "google_project.project",
+            "google_service_account.service_account"
+          ]
+        }
+      ]
+    },
+    {
+      "mode": "managed",
+      "type": "local_file",
+      "name": "service_account_key_file",
+      "provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
+      "instances": [
+        {
+          "schema_version": 0,
+          "attributes": {
+            "content": "ewogICJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIsCiAgInByb2plY3RfaWQiOiAiYXJjaGV0eXBlLWFwaS0yIiwKICAicHJpdmF0ZV9rZXlfaWQiOiAiYjQ2ZDEyZjlmOTA1NjlmNmE2ZmZiODEzYjgwMzNkZmJjZTUyZjQ5MSIsCiAgInByaXZhdGVfa2V5IjogIi0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLVxuTUlJRXZBSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS1l3Z2dTaUFnRUFBb0lCQVFDeXhMbFQvWFZuR1llblxuMG45U3ZXU3lnYW5PT2hGcE11V1Z3S3hvSlZtL241bnU0U2hWcHVVenNOQTdNL0YwNEw1RFBlNUg4ZWVTVE1Gd1xuZkJZWWZHZmZraWFlVFBzNm11SkpINzdDa1QvTTBDTUVVU2dBTjBtRVNUNWxtTnFIY1dNSTFheFE3VVR2Rndpc1xuTUh6SHRSVmtKaXlmbSt5MnNGSzByVk00V3NBUFNEU2hyYTVUQnBnSjlEQTdEOUZ0Z2VhTHg3WUNPNWFSMEN1NFxubjZjZjdLTzFwNjNvZFNsM2EwVEdxVGZ1V0dxRHNNbXoxUmI2NVlvM096YjMwczVxYi9wcVJZaGk4ekI3SUtDQ1xuWWVydVJhQ2UwczlBbTNwZ2FnY1NPVFJ1d3R0eTJobUZOdjZ3Nk1kUWdwSUpxSUI1b0hGKzRLUXdNaFJWbkd2UVxuWktlUXpHNzdBZ01CQUFFQ2dnRUFESFhaMVJqZGpnb2hnbmtSbHRyTW9nU1hxV0F5eG5wZHRrcHZ1ZktnaHRMQlxuRC8xeHFrYXZ3eUtYNFFpRjAvZTJTMG1OeWtYNk5pQlRlMXVxenBvZFEyQVVnUVFzbnJFZEJLdkp5QllIdEdidFxuaWVkK09rV2dRakE0d1N3ZVQwSi8vS1dvRlJ2MHFoV1k3U2VVQ3ZkOFBqYks5WFVRMHNzSGVNQjhCSG1PMllHNVxuQzJhMGc5Z3hIR2Zya1NtUDlpYzFDM1U4eU1QVE5Md3ZmU25URWkwU0FWKytUcVd2K1p5Z1ZZNENvWWxrN0h5SVxuNHZSTEdXZ1NibVBsL3FuU1hMVm9SN3FPK1JXZ1lxWnpkUmVwc2RGZVdhZ2kxSGNRcHRDMzlJMG1hU2taNHY0alxuVDhSR0tKNVExRVhLbDdKNENnUHhSN21QVGxZT08rcFUrU3hhdjA4SndRS0JnUURoLzFlbVZseUJ6QVNjM0h4NVxuUDl0M05xRU9BV0FEalduRHF2Y3N2MERPUXJYdTZ4Uzd4TlZkMjR1eHV2RWZITHFtMGhTWWJkT3FQTVVjOE5ZMFxuQnZvbktmbVdxQkJPcjIrK250V1ZrVU5DbUZieUQyaFFuN0ZmbnAxNFhDay9PNDBlR01CQ085RFljdmVmVXRKMVxubnNvQjFVS01yTXRyRzQ3YjZ5THVoNmhndlFLQmdRREtnRWJLM3M5NEszOExkbitOL3A4VkVWTUhLWjlHVWY4TlxuYWZFWWFvYlYvQW1HbHU2ZzJPSjFYZnR3dEw5Qk5aQVFGSkpzdnhzVExNVERXdFg5R3RlN001V0xUaUdSRkdMNlxuV1VrbXAwTHJGT3BhQlNnZHhEc3dXTjdSci81UXFvV1JTUkdIUjQrT3hzRkNxS2R1SWxrcWFjdHRtWVJ4aGNwRFxuQ0puVFNFR1dGd0tCZ0VmQkVzczFVRm5GdFJFNDBDeVBJZGRQK1FMQlhRTER1M2pzcDE0RnUwWEIySkhyQWNJOFxuVktKZ09wSkxrSk1ZUkFzRFdKYXRDQzljN0Jpc1B0WjJBS2ErcFFnNGhEVDNicnRQSXZGQ0ZlRG5EWFA5Z0ZsWVxuMnJCSlpDWDYzUDIrb3FlVHBEZGpWb1Bpdk14Uk41RXd3V0tqbTJXZTExZENnTEZDanV5OUZiRGhBb0dBR0RaV1xuUUpON2d3YlFYZktCTmQwbjhFRHVDSUUxaGhidnhBN1N3UFNid3FJc0VXZWlpS0RtRXRwMTRmZjZsalZ0VUQ3bFxuY3hNMmpZaGd6bXJpQXkxZWRnZW83Y3Nkd0ZjTHJwdFdYOFRILzR1MHFhYk1NU0x4WU1wL2VkcnRNWC95RUhrSVxuRzRDMjdYOWVSRFllTHREMGtGbXQ5U0RSOFREcUNqSFJFcTRsQ1drQ2dZQXNLaGUwbEloSHhYOG01aHVtblRiUFxuYkt1czMxemZ6NHFhdGJ0ZitkUlJZTi8wV3kveFBqOVNORjhSc1BZUlZOOVBRUXZCOUVzcExjbHNVaW5LMDArMFxuSk1jV2VmUHpJbVhlUU5GSEJzMUpGU1VtZlZEb3QrOEFPMm9OcXdST3dUcmxobllxK1FKK1V5b1lqS0R3bS9CMlxuR0JkVkw5V1V3NVpyZmxYbmU1THEydz09XG4tLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tXG4iLAogICJjbGllbnRfZW1haWwiOiAiYXV0b21hdGlvbkBhcmNoZXR5cGUtYXBpLTIuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLAogICJjbGllbnRfaWQiOiAiMTEwMTEzMjkzNjg4MzI3ODUyNTA0IiwKICAiYXV0aF91cmkiOiAiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vb2F1dGgyL2F1dGgiLAogICJ0b2tlbl91cmkiOiAiaHR0cHM6Ly9vYXV0aDIuZ29vZ2xlYXBpcy5jb20vdG9rZW4iLAogICJhdXRoX3Byb3ZpZGVyX3g1MDlfY2VydF91cmwiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vb2F1dGgyL3YxL2NlcnRzIiwKICAiY2xpZW50X3g1MDlfY2VydF91cmwiOiAiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vcm9ib3QvdjEvbWV0YWRhdGEveDUwOS9hdXRvbWF0aW9uJTQwYXJjaGV0eXBlLWFwaS0yLmlhbS5nc2VydmljZWFjY291bnQuY29tIgp9Cg==",
+            "content_base64": null,
+            "directory_permission": "0777",
+            "file_permission": "0777",
+            "filename": "./service-account.json.base64",
+            "id": "f2c804478e39143d210adb7faef050e2992002e4",
+            "sensitive_content": null,
+            "source": null
+          },
+          "sensitive_attributes": [
+            [
+              {
+                "type": "get_attr",
+                "value": "content"
+              }
+            ]
+          ],
+          "private": "bnVsbA==",
+          "dependencies": [
+            "data.google_billing_account.billing_account",
+            "google_project.project",
+            "google_service_account.service_account",
+            "google_service_account_key.service_account_key"
+          ]
+        }
+      ]
+    }
+  ],
+  "check_results": null
+}
diff --git a/edera-api/scripts/tf/variables.tf b/edera-api/scripts/tf/variables.tf
new file mode 100644 (file)
index 0000000..63aa307
--- /dev/null
@@ -0,0 +1,22 @@
+variable "project_id" {
+  type = string
+}
+
+variable "project_name" {
+  type = string
+}
+
+variable "region" {
+  type = string
+  default = "europe-west1"
+}
+
+variable "zone" {
+  type = string
+  default = "europe-west1-b"
+}
+
+variable "billing_account" {
+  type = string
+  default = "011783-8751C0-5C335B"
+}
\ No newline at end of file