
    `j0                       d Z ddlmZ ddlZddlmZmZ ddlmZ ddl	m
Z
 ddlmZ ddlmZ dd	lmZ dd
lmZmZmZmZ ddlmZ ddlmZ ddlmZ ddlmZ ddlmZm Z m!Z!m"Z"m#Z# ddl$m%Z% ddl&m'Z'm(Z( ddl)m*Z*  ejV                  e,      Z-ddZ.	 	 	 	 	 	 ddZ/ddZ0ddZ1	 	 	 	 	 	 ddZ2	 	 	 	 	 	 	 	 d dZ3	 	 	 	 	 	 d!dZ4	 	 	 	 	 	 	 	 d"dZ5y)#z=Orchestration service for isolated ISTAT XML file generation.    )annotationsN)date	timedelta)Booking),build_c59_aggregation_payloads_for_structure)build_guest_payload)XmlPayloadValidationError)build_xml_export_result)build_c59_daily_xml_filenamebuild_c59_xml_filenamebuild_c59_zip_filenamebuild_guest_xml_filename)build_c59_zip_result)IstatXmlExportResult)IstatZipExportResult)build_c59_xml_document)build_daily_c59_rowscalculate_c59_available_bedscalculate_c59_available_roomscalculate_c59_occupied_roomsfetch_c59_bookings)serialize_payloads)generate_guest_nightswith_guest_night_prefetch)	Structurec                b    | t        | d      t        | t              st        | d      | S )Nz is requiredz must be a date instance)r	   
isinstancer   )value
field_names     8/backend/istat/xml_export/services/xml_export_service.py_validate_dater!   +   s;    }':,l(CDDeT"':,6N(OPPL    c                Z    t        | d      }t        |d      }||k  rt        d      ||fS )N
start_dateend_datez4end_date must be greater than or equal to start_date)r!   r	   )r$   r%   valid_start	valid_ends       r    _validate_date_ranger(   3   sA     !\:Kx4I;'B
 	
 	!!r"   c                |    t         j                  j                  |       j                         }|t	        d      |S )N)idzStructure not found)r   objectsfilterfirstr	   )structure_id	structures     r    _get_structure_or_raiser0   @   s<    !!((L(9??AI'(=>>r"   c                    t        | j                  xs d      j                         }|st        d| j                   d      |S )N z
Structure z  is missing ISTAT structure code)str
istat_codestripr	   r*   )r/   r4   s     r    "_get_structure_istat_code_or_raiser6   G   sI    Y))/R0668J'&FG
 	
 r"   c                z    t        t        j                  j                  | d||            j	                  dd      S )a  Return checked-in bookings that overlap the given date range.

    Only bookings with ``is_checked_in=True`` are included so that the XML
    export dataset exactly matches the ISTAT page dataset, which also filters
    to checked-in reservations only.
    T)r.   is_checked_incheck_in_date__ltecheck_out_date__gtcheck_in_dater*   )r   r   r+   r,   order_byr.   r$   r%   s      r    _fetch_overlapping_bookingsr>   P   sB     %%')	 	 	
 h%&r"   c                   t        |       }t        ||      \  }}|t        d      z   }t        t	        | ||            }t        |||      }|D 	cg c]  }	|	j                  s|	 }
}	|D ci c]  }|j                  | c}|D ci c]:  }|j                  j                         D ]  }|j                  |j                  | < c}}t        fd|
D        d      }|t        d|j                   d      |
D 	cg c]*  }	t        |	j                     |	j                     |		      , }}	t        |      }t!        | ||      }t#        ||
      }t$        j'                  d| t)        |j*                  xs d      j-                         xs dd|j/                         |j/                         t1        |      |dddd
       |S c c}	w c c}w c c}}w c c}	w )zDGenerate a guest movement XML export for a structure and date range.   daysr=   )period_startperiod_end_exclusiveNc              3  r   K   | ].  }|j                   |j                   vs|j                  vr| 0 y w)N)guest_id
booking_id).0guest_nightbookings_by_idguests_by_ids     r    	<genexpr>z,generate_guest_xml_export.<locals>.<genexpr>   sA      	
3##+##<7%%^;	 3s   47z$Guest export failed because booking z& occupancy exceeds saved guest records)bookingguestrI   filenamecontentzGenerated guest XML exportr2   	guest_xmlFsuccess)
r.   structure_istat_codeexport_typer$   r%   payload_countrP   xsd_validation_enabledxsd_pathstatusextra)r0   r(   r   listr>   r   is_arrival_nightr*   guestsallnextr	   rG   r   rF   r   r   r
   loggerinfor3   r4   r5   	isoformatlen)r.   r$   r%   r/   r&   r'   rD   bookingsguest_nightsrI   arrival_guest_nightsrM   rN   missing_guest_nightpayloadsxml_contentrP   resultrJ   rK   s                     @@r    generate_guest_xml_exportrl   f   s<    (5I1*hGK$ya'88#%"	
H ) 1L (4'3{7S7S|   :BBggjj')BN  G^^'')E88 	%) 	L 	
3	
 		 &'2"--..TV
 	
 0 0K 	";#9#9:{334#	

 0   %X.K'!H
 %hLF
KK$(I((.B/557?4&%//1!++- ] &+
  " Mw C,s   G'G1G?G/Gc                f   t        |       }t        |      }t        |d      }t        | ||      }t	        |||t        |      t        |      t        | |            }t        | |      }t        ||      }t        j                  d| |d|j                         t        |      |d	d
dd	       |S )z3Generate a C59 XML export for a single report date.report_dater=   r.   target_datestructure_codern   rowsavailable_roomsavailable_bedsoccupied_rooms)r.   export_daterO   zGenerated C59 XML exportc59_xmlFNrS   )	r.   rT   rU   rn   	row_countrP   rW   rX   rY   rZ   )r0   r6   r!   r   r   r   r   r   r   r
   ra   rb   rc   rd   )	r.   rn   r/   rT   valid_report_daters   rj   rP   rk   s	            r    generate_c59_xml_exportr{      s     (5I=iH&{MB7!$"D
 )+%5i@3I>3%)

K &!%H %hLF
KK"($8$,668T &+

   Mr"   c                @   t        ||      \  }}t        |       }t        |      }t        |      }t	        |      }t        t        j                  j                  | d||      j                  ddd      j                  d      j                  dd            }	g }
|}||k  r|	D cg c]"  }|j                  |k  r|j                  |k\  r|$ }}t        |||	      }t        | |
      }t!        ||||||      }t#        |      }|
j%                  ||f       |t'        d      z  }||k  rt)        ||      }t+        ||
      }t,        j/                  d| |d|j1                         |j1                         |j2                  ||j4                  dd	       |S c c}w )u~  Generate a ZIP archive containing one C59 XML file per calendar day.

    The Liguria / Ross1000 specification requires one XML file per day.
    Days with no guest activity still produce a valid (empty) XML document.

    Performance notes:
    - Structure metadata (istat_code, room/bed counts) is fetched once.
    - All bookings that overlap the full date range are fetched in a single
      query and held in memory; per-day aggregation is done in Python to
      avoid N+1 database round-trips.
    - Occupied-room counts still require one lightweight DB query per day
      (distinct property_id count) — this is unavoidable without a more
      complex pre-aggregation step, but each query is O(1) in rows returned.

    Args:
        structure_id: ID of the Structure to report on.
        start_date:   First day of the reporting period (inclusive).
        end_date:     Last day of the reporting period (inclusive).

    Returns:
        An :class:`IstatZipExportResult` with the raw ZIP bytes ready for
        an HTTP download response.

    Raises:
        XmlPayloadValidationError: On invalid inputs or missing structure data.
    T)r.   r8   r9   check_out_date__gtepropertyproperty_typer/   r^   r;   r*   )r/   rp   re   ro   rq   )rn   r@   rA   )r$   r%   )zip_filenamedaily_fileszGenerated C59 ZIP exportc59_ziprS   )	r.   rT   rU   r$   r%   
file_countr   zip_byte_sizerY   rZ   )r(   r0   r6   r   r   r\   r   r+   r,   select_relatedprefetch_relatedr<   r;   check_out_dater   r   r   r   appendr   r   r   ra   rb   rc   r   	byte_size)r.   r$   r%   r&   r'   r/   rT   rt   ru   all_bookingsr   current_daybday_bookingsrs   rv   rj   daily_filenamer   rk   s                       r    generate_c59_zip_exportr      s   @ 2*hGK (5I=iH 4I>O1)<N %( +	 	 	
 

O[	A		(	#	/4	(
L *,KK

" $
#!+-!2B2Bk2Q | 	 

 $#!
 6%#

 -/#+))
 6+NNK89ya((? 
"D *[9UL!!F
 KK"($8$%//1!++- ++(#--

   Mg
s   %'F)r   date | Noner   r3   returnr   )r$   r   r%   r   r   ztuple[date, date])r.   intr   r   )r/   r   r   r3   )r.   r   r$   r   r%   r   )r.   r   r$   r   r%   r   r   r   )r.   r   rn   r   r   r   )r.   r   r$   r   r%   r   r   r   )6__doc__
__future__r   loggingdatetimer   r   bookings.modelsr   1istat.xml_export.builders.c59_aggregation_builderr   )istat.xml_export.builders.payload_builderr   istat.xml_export.exceptionsr	   #istat.xml_export.files.file_builderr
   istat.xml_export.files.namingr   r   r   r   "istat.xml_export.files.zip_builderr   %istat.xml_export.models.export_resultr   )istat.xml_export.models.zip_export_resultr   /istat.xml_export.serializers.c59_xml_serializerr   1istat.xml_export.services.c59_aggregation_servicer   r   r   r   r   istat.xml_export.xml.serializerr   services.guest_night_servicer   r   structures.modelsr   	getLogger__name__ra   r!   r(   r0   r6   r>   rl   r{   r    r"   r    <module>r      s=   C "  $ # J A G  D F J R  ? ( 
		8	$
"
"
" 
"&& & 	&,SS S 	S
 Sl.. . 	.btt t 	t
 tr"   