Skip to content

Implement webservice integration for Alloggiati Web (Police) and Ross1000 #1

@dgpaci

Description

@dgpaci

Objective

Implement a webservice integration layer to communicate guest data to:

  1. Alloggiati Web (Italian Police - Polizia di Stato)
  2. Ross1000 (Hotel management software)

Background

The Italian Police provides a SOAP webservice for accommodations to report guest data:

  • URL: https://alloggiatiweb.poliziadistato.it/service/service.asmx
  • Protocol: SOAP/XML
  • Authentication: Credentials → WSKEY → Temporary Token
  • Record Format: 170 characters per guest (168 + CR/LF)

Current Database Compatibility

Our database already contains all required data:

Alloggiati Web Field Our Database
Guest Type (16-20) stay_guest.guest_type_id
Arrival Date stay.check_in_date
Days of Stay stay.nights (auto-calculated)
Surname/Name guest.@anagrafica_id.cognome/nome
Gender (1=M, 2=F) guest.@anagrafica_id.sesso
Birth data guest.@anagrafica_id.*_nascita
Citizenship guest.@anagrafica_id.cittadinanza
Document guest.document_*

Proposed Implementation

1. AlloggiatiWebClient (SOAP Client)

File: resources/services/alloggiati_webservice.py

class AlloggiatiWebClient:
    """SOAP client for Alloggiati Web (Italian Police)"""
    
    WSDL_URL = "https://alloggiatiweb.poliziadistato.it/service/service.asmx?wsdl"
    
    def __init__(self, utente, password, wskey):
        """Initialize client with credentials"""
        
    def generate_token(self):
        """Generate authentication token (valid ~1 hour)"""
        # Method: GenerateToken
        # Returns: TokenInfo (issued, expires, token)
        
    def test_schedine(self, schedine_list):
        """Test guest records without sending (dry-run)"""
        # Method: Test
        # Returns: ElencoSchedineEsito with validation errors
        
    def send_schedine(self, schedine_list):
        """Send guest records to Police system"""
        # Method: Send
        # Returns: ElencoSchedineEsito with submission results
        
    def download_ricevuta(self, data):
        """Download receipt PDF for specific date"""
        # Method: Ricevuta
        # Returns: PDF as byte array
        
    def download_tabelle(self, tipo_tabella):
        """Download reference tables (Luoghi, Tipi_Documento, etc.)"""
        # Method: Tabella
        # Returns: CSV string

2. AlloggiatiExporter (170-char Record Builder)

File: resources/services/alloggiati_export.py

class AlloggiatiExporter:
    """Generates 170-character records for Alloggiati Web"""
    
    def export_stay(self, db, stay_id):
        """Export all guests for a stay as 170-char records"""
        # Returns: List of strings (170 chars each)
        
    def build_schedina_line(self, stay, stay_guest, guest, anagrafica):
        """Build single 170-character line for one guest"""
        # Format according to TABELLA 1 specification
        
    def _format_field(self, value, length, field_type):
        """Pad value to specified length"""
        # N = numeric (left-pad with zeros)
        # A/AN = alpha/alphanumeric (right-pad with spaces)

Record Structure (170 chars):

Position  Length  Field                    Format      Required
--------  ------  -----------------------  ----------  ---------
0-1       2       Guest Type               N           Yes
2-11      10      Arrival Date             gg/mm/aaaa  Yes
12-13     2       Days of Stay             N           Yes (max 30)
14-63     50      Surname                  A           Yes
64-93     30      Name                     A           Yes
94-94     1       Gender                   N           Yes (1=M, 2=F)
95-104    10      Birth Date               gg/mm/aaaa  Yes
105-113   9       Birth Municipality       N           If Italy
114-115   2       Birth Province           A           If Italy
116-124   9       Birth Country            N           Yes
125-133   9       Citizenship              N           Yes
134-138   5       Document Type            AN          Yes (leader only)
139-158   20      Document Number          AN          Yes (leader only)
159-167   9       Document Issue Place     N           Yes (leader only)
168-169   2       CR+LF                    -           All except last

3. Ross1000Exporter (To Be Defined)

File: resources/services/ross1000_export.py

class Ross1000Exporter:
    """Export guest data for Ross1000 hotel software"""
    
    def export_stay(self, db, stay_id, format='csv'):
        """Export stay in Ross1000 format"""
        # Format to be defined based on Ross1000 specs
        # Supported formats: csv, xml, json

4. Integration Service

File: resources/services/guest_reporting_service.py

class GuestReportingService:
    """Main service coordinating all external integrations"""
    
    def __init__(self, db):
        self.db = db
        self.alloggiati_client = None  # Lazy init with credentials
        self.alloggiati_exporter = AlloggiatiExporter()
        self.ross1000_exporter = Ross1000Exporter()
        
    def report_to_police(self, stay_id, test_mode=False):
        """Report stay to Alloggiati Web (Police)"""
        # 1. Generate 170-char records
        # 2. Test records (dry-run)
        # 3. Send records (if test passed)
        # 4. Save receipt
        # 5. Update stay status
        
    def report_to_ross1000(self, stay_id):
        """Export stay to Ross1000"""
        # Generate Ross1000 format
        # Send/Save file
        
    def report_all(self, stay_id):
        """Report to all configured systems"""
        # Police + Ross1000 + any other integrations

UI Integration

Table Handler Updates

File: resources/tables/stay/th_stay.py

Add button in stay form:

def th_form(self, form):
    # ... existing code ...
    
    # Add button for reporting
    toolbar = top.div(margin='10px')
    toolbar.button('Report to Police (Test)', 
                  fire='.report_police_test',
                  icon='test')
    toolbar.button('Report to Police (Send)', 
                  fire='.report_police_send',
                  icon='send',
                  disabled='^.status != "ready"')
    toolbar.button('Download Receipt', 
                  fire='.download_receipt',
                  icon='download')

Configuration

Add to site config (instanceconfig.xml or env):

<police_reporting>
    <alloggiati_web>
        <utente>XX002458</utente>
        <password>***</password>
        <wskey>***</wskey>
    </alloggiati_web>
</police_reporting>

Data Flow

1. User creates stay in host system
   ↓
2. AlloggiatiExporter generates 170-char records from stay_guest
   ↓
3. AlloggiatiWebClient tests records (dry-run)
   ↓
4. If validation passes, send records to Police
   ↓
5. Receive and store receipt PDF
   ↓
6. Update stay status
   ↓
7. (Optional) Export to Ross1000

Dependencies

  • Python SOAP Library: zeep or suds-community
    pip install zeep

Benefits

  1. Zero Data Duplication - All data already in our database
  2. Code Reuse - Similar to existing police_export.py (178 chars)
  3. Aligned Lookups - guest_type, document_type already compliant
  4. Automatic Calculations - nights, tax_amount auto-computed
  5. Audit Trail - Store receipts and submission logs

Testing Strategy

  1. Unit Tests - Record formatting, field padding
  2. Integration Tests - SOAP client with test credentials
  3. Dry-run Mode - Always test before sending
  4. Validation - Use Test method before Send

Acceptance Criteria

  • SOAP client successfully authenticates with token
  • Export generates valid 170-character records
  • Test method validates records before sending
  • Send method submits records to Police system
  • Receipt PDF is downloaded and stored
  • Ross1000 export format defined and implemented
  • UI buttons trigger reporting workflow
  • Error handling and logging implemented
  • Documentation updated

References

  • Alloggiati Web Manual: /Users/dgpaci/Downloads/MANUALEWS.pdf
  • WSDL: https://alloggiatiweb.poliziadistato.it/service/service.asmx?wsdl
  • Police Export Service: resources/services/police_export.py (existing)

Estimated Effort

  • AlloggiatiWebClient: 2-3 days
  • AlloggiatiExporter: 1-2 days
  • Ross1000Exporter: 1-2 days (pending specs)
  • Integration Service: 1 day
  • UI Integration: 1 day
  • Testing: 2 days

Total: ~8-10 days

MANUALEWS.pdf

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions