
    Jjlk                        d Z ddlZddlZddlZddlZej                            ej                            e                    Z	ej        
                    de	           ddlZej                            ej                            e	          dd          ZdZ ej        ddd          Zg d	Zd
ZdZdZd Zd Zd Zd Zd Zd Zd Zd ZdefdZdddddddddddd d!Zd<d#Z d$ Z!d% Z"d=d&Z#d>d(Z$d=d)Z%	 d?d*Z&d@d+Z'd=d,Z(d=d-Z)	 	 dAd/Z*d0 Z+dBd1Z,e-d2k    rddl.Z/ e/j0                    Z1 e2ej3                  dk    rej3        d         d3k    r e e1            e4d4d5z              e"e1          D ]3Z5 e4d6e5d7         e5d8         e5d9         e5d:         e5d;         fz             4e16                                 dS dS )Cut  
PLANNING TEAM - absences, conges payes et compteurs.

Deux facons de decompter une absence, selon ce que Jerome coche :

  * « CP »     -> on retire des jours ouvrables du solde de conges payes
                  (lundi -> samedi, jours feries exclus) ;
  * « heures » -> on retire des heures du planning, sans toucher au solde. Le creneau
                  reste visible mais hachure, pour qu'on voie tout de suite ou sont
                  les trous. C'est ce que le cabinet attend pour les arrets de travail
                  (« combien d'heures a deduire »).

Le solde affiche n'est jamais ecrase : il se recalcule toujours comme

    solde initial  -  jours pris  +  ajustements manuels

Chaque correction a la main est donc conservee avec sa date et son motif. C'est
exactement ce qui manquait en 2025, quand des CP oublies au decompte n'ont ete
rattrapes qu'en mai 2026.
    Ndocsu%   Planning vacances-Récap congés.xlsxz	2026-2027        )
)cpu   Congé payér   T)maladieu   Arrêt maladieheuresF)	materniteu   Maternité / paternitér	   T)conge_parentalu   Congé parentalr	   F)ajppAJPPr	   F)revisionu   Jour de révisionr	   T)	formationu   Formation / écoler	   T)evenement_familialu   Événement familialr	   T)recuperationu   RécupérationrecupT)
sans_soldez
Sans solder	   Fu	  
CREATE TABLE IF NOT EXISTS absence (
    id            INTEGER PRIMARY KEY,
    salarie_id    INTEGER NOT NULL REFERENCES salarie(id) ON DELETE CASCADE,
    date_debut    TEXT NOT NULL,
    date_fin      TEXT NOT NULL,
    motif         TEXT NOT NULL,
    decompte      TEXT NOT NULL DEFAULT 'cp',    -- cp | heures | recup
    jours         REAL,                          -- jours ouvrables, si decompte = cp
    heures        REAL,                          -- heures retirees, si decompte = heures
    salaire_maintenu INTEGER NOT NULL DEFAULT 1,
    statut        TEXT NOT NULL DEFAULT 'validee',  -- demandee | validee | refusee
    demandee_par  TEXT,
    validee_par   TEXT,
    libelle       TEXT,                          -- le texte d'origine, si import
    origine       TEXT NOT NULL DEFAULT 'saisie',-- saisie | import
    cree_le       TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS cp_solde (
    salarie_id     INTEGER PRIMARY KEY REFERENCES salarie(id) ON DELETE CASCADE,
    solde_initial  REAL NOT NULL DEFAULT 0,
    date_initiale  TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS cp_ajustement (
    id         INTEGER PRIMARY KEY,
    salarie_id INTEGER NOT NULL REFERENCES salarie(id) ON DELETE CASCADE,
    jours      REAL NOT NULL,           -- signe : +2 rend deux jours, -1 en retire un
    motif      TEXT NOT NULL,
    fait_le    TEXT NOT NULL,
    auteur     TEXT
);

-- Compteurs d'heures, un par type et par personne :
--   recup — les heures a recuperer (positif) ou dues (negatif) ;
--   hs    — les heures supplementaires a payer, transmises au cabinet.
-- Meme principe que pour les conges payes : le solde n'est jamais ecrase, il se
-- recalcule comme solde initial + ecarts au cycle + ajustements manuels.
CREATE TABLE IF NOT EXISTS heures_solde (
    salarie_id    INTEGER NOT NULL REFERENCES salarie(id) ON DELETE CASCADE,
    type          TEXT NOT NULL,
    solde_initial REAL NOT NULL DEFAULT 0,
    date_initiale TEXT NOT NULL,
    PRIMARY KEY (salarie_id, type)
);

CREATE TABLE IF NOT EXISTS heures_ajustement (
    id         INTEGER PRIMARY KEY,
    salarie_id INTEGER NOT NULL REFERENCES salarie(id) ON DELETE CASCADE,
    type       TEXT NOT NULL,
    heures     REAL NOT NULL,      -- signe : +2 ajoute deux heures, -1 en retire une
    motif      TEXT NOT NULL,
    fait_le    TEXT NOT NULL,
    auteur     TEXT
);

CREATE INDEX IF NOT EXISTS idx_absence_salarie ON absence (salarie_id, date_debut);
)r   hs)absencecp_soldecp_ajustementheures_soldeheures_ajustementc                 x    d |                      d          D             t          fd|D                       S )z'Toutes ces tables existent-elles deja ?c                     h | ]
}|d          S r    .0ls     $/opt/planning-team/travail/conges.py	<setcomp>z#tables_completes.<locals>.<setcomp>{   s.     @ @ @!1 @ @ @    z3SELECT name FROM sqlite_master WHERE type = 'table'c              3       K   | ]}|v V  	d S Nr   )r   t	presentess     r!   	<genexpr>z#tables_completes.<locals>.<genexpr>}   s'      ..!qI~......r#   )executeall)cxtablesr'   s     @r!   tables_completesr-   y   sY    @ @rzz= ?  ? @ @ @I....v......r#   c                     t          | t                    rdS |                     t                     |                                  dS )aE  
    Ne fait rien si toutes les tables existent deja : `executescript` validerait la
    transaction en cours, ce qui rendrait definitives les ecritures d'une simulation.

    Le controle porte sur la liste complete, pas sur une seule table : sinon l'ajout
    d'une table dans une version ulterieure passerait inapercu.
    N)r-   TABLESexecutescriptSCHEMAcommit)r+   s    r!   creer_schemar3      s@     F## VIIKKKKKr#   c                 ~   ddl }ddlmc m} d|                    |           5 }|                    d          5 }fd|                    |                                          D             }ddd           n# 1 swxY w Y   |                    d          5 }d |                    |                                                              dz             D             }ddd           n# 1 swxY w Y   ||vrt          d	|z            |                    d
|
                    |          dz   z            5 }i }|                    |                                                              dz             D ]}	|	                    dz             }
|
|
j        $|	                    d          dk    r|t          |
j                           n|
j        }|pd                                }|r|||	                    d          <   	 ddd           n# 1 swxY w Y   ddd           n# 1 swxY w Y   |S )z;{ 'D7': 'valeur' } pour une feuille du classeur des conges.r   Nz;{http://schemas.openxmlformats.org/spreadsheetml/2006/main}zxl/sharedStrings.xmlc           	      z    g | ]7}d                      d |                    dz             D                       8S ) c              3   (   K   | ]}|j         pd V  dS )r6   N)text)r   r&   s     r!   r(   z+_lire_feuille.<locals>.<listcomp>.<genexpr>   s(      GGqv|GGGGGGr#   r&   )joiniter)r   sinss     r!   
<listcomp>z!_lire_feuille.<locals>.<listcomp>   sT     8 8 8 wwGGRWWR#X5F5FGGGGG 8 8 8r#   zxl/workbook.xmlc                 8    g | ]}|                     d           S )name)get)r   ss     r!   r=   z!_lire_feuille.<locals>.<listcomp>   s"    TTTaAEE&MMTTTr#   sheetzFeuille introuvable : %szxl/worksheets/sheet%d.xmlr   cvr&   rA   r6   r)zipfilexml.etree.ElementTreeetreeElementTreeZipFileopenparsegetrootr:   
ValueErrorindexfindr8   r@   intstrip)cheminnom_feuillerF   ETzffchainesnomscellrC   rD   texter<   s               @r!   _lire_feuiller\      sJ   NNN&&&&&&&&&	FB		 	  -BWW+,, 	88 8 8 8!#!!4!4!6!68 8 8G	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 	8 WW&'' 	U1TT288A;;+>+>+@+@+E+Eb7l+S+STTTD	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	U 	Ud""8;FGGGWW0DJJ{4K4Ka4OPQQ 		-UVDXXa[[((**//S99 - -FF28$$901c

c0A0AAF,,qv"++-- -',Ds$-		- 		- 		- 		- 		- 		- 		- 		- 		- 		- 		- 		- 		- 		- 		-- - - - - - - - - - - - - - -$ Ks   H24A<0H2<B 	 H2B 	H2AC0$H20C4	4H27C4	8AH2?CHH2H	H2"H	#H22H69H6c                     i }|                                  D ]J\  }}t          j        d|          }|r|dv r!|                    d          }|t	          |          f||<   K|S )z
    La ligne 2 donne les prenoms ; la colonne suivante porte les jours poses.
    Renvoie { prenom : (colonne periode, colonne jours) }.
    z^([A-Z]+)2$)Semainesu   DébutFinr   )itemsrematchgroup_colonne_suivante)rZ   resultatrefvaleurmcols         r!   _colonnes_par_prenomrj      sz    
 Hzz|| 9 9VH^S)) 	F@@@ggajj!23!7!78Or#   c                     d}| D ]}|dz  t          |          dz
  z   }|dz  }d}|dk    r1t          |dz
  d          \  }}t          d|z             |z   }|dk    1|S )Nr      @   r   r6   A   )orddivmodchr)ri   nchrA   rE   s        r!   rd   rd      s    	A $ $Fc"ggl#FA
A
a%%a!eR  1QKK!O a%% Hr#   c           	      |   | pd                                 }|sdS |                                dv rfdt          d          D             S t          j        d|          rbt          j        ddd	          t          j        t          t          |                              z   }d
|z
  j
        cxk    rdk    rn n|gS dS fd}g }t          j        d|t          j                  D ]} |t          |                    d                    t          |                    d                              } |t          |                    d                    t          |                    d          p|                    d                              rA|r?|k    r9|                    fdt          |z
  j
        dz             D                        t          j        dd|t          j                  }t          j        d|          D ]}t          |                    d                    }	 |t          |                    d                    |	           |t          |                    d                    |	          c}rA|r?|k    r9|                    fdt          |z
  j
        dz             D                        t          j        dd|          }t!          t          j        d|                    }
|
D ]e} |t          |                    d                    t          |                    d                              }|r|                    |           f|
rt          |
d
                             d                    }	|d|
d
                                                  }t          j        d|          D ]E} |t          |                    d                    |	          }|r|                    |           F|sdS t'          t)          |                    S )u  
    Traduit un libelle du classeur des conges en vraies dates.

    Le classeur ecrit les periodes a la main, sous des formes tres variees :
        « x »                       -> toute la semaine
        « Lu 08/06 », « Le 31/08 »  -> un jour
        « du 04 au 08/08 »          -> une periode
        « 26-27/10 », « 22-24/07 »  -> une periode dans le mois
        « 15 +17/06 », « 02+03/06 » -> plusieurs jours
        « Lu 31/08 et Ma 01/09 »    -> plusieurs jours, chacun date

    Renvoie la liste des dates, ou None si le libelle reste indechiffrable — auquel
    cas on ne devine pas : mieux vaut une absence sans date qu'une fausse date.
    r6   N)xxxc                 >    g | ]}t          j        |          z   S r   datetime	timedelta)r   ilundis     r!   r=   z$dates_du_libelle.<locals>.<listcomp>   s)    @@@!*1---@@@r#   r   z\d{5}(\.0+)?ik        r      c                     g }j         dz
  j         j         dz   fD ]<}	 |                    t          j        |||                      -# t          $ r Y 9w xY w|rt          |fd          ndS )zGL'annee est celle qui rapproche le plus la date du lundi de la semaine.r   c                 2    t          | z
  j                  S r%   )absdays)dr|   s    r!   <lambda>z1dates_du_libelle.<locals>.caler.<locals>.<lambda>   s    S!e)1A-B-B r#   )keyN)yearappendry   daterN   min)jourmois
candidatesanneer|   s       r!   calerzdates_du_libelle.<locals>.caler   s    
j1nej%*q.A 	 	E!!(-tT"B"BCCCC   GQ[s:#B#B#B#BCCCCW[[s   )A
AAz8du\s+(\d{1,2})(?:/(\d{1,2}))?\s+au\s+(\d{1,2})/(\d{1,2})      r      c              3   F   K   | ]}t          j        |          z   V  d S r%   rx   r   r{   debuts     r!   r(   z#dates_du_libelle.<locals>.<genexpr>   O       A A !3A!6!66 A A A A A Ar#   z0du\s+\d{1,2}(?:/\d{1,2})?\s+au\s+\d{1,2}/\d{1,2} )flagsz#(\d{1,2})\s*-\s*(\d{1,2})/(\d{1,2})c              3   F   K   | ]}t          j        |          z   V  d S r%   rx   r   s     r!   r(   z#dates_du_libelle.<locals>.<genexpr>  r   r#   z\d{1,2}\s*-\s*\d{1,2}/\d{1,2}z(\d{1,2})/(\d{1,2})z(?<![\d/])(\d{1,2})(?![\d/]))rR   lowerrangera   	fullmatchry   r   rz   rQ   floatr   finditer
IGNORECASErc   extendsublistr   startsortedset)libeller|   r[   r   r   datesrh   finrester   
explicitesavantr   s    `          @r!   dates_du_libeller      sg    ]!!##E t{{}}##@@@@uQxx@@@@ 
|OU++ }T2r**X-?E%LL@Q@Q-R-RR #((((q(((((6Mt\ \ \ \ \ E [T/ / A AeC

OOS__55c!''!**oos1771::+C'D'DEE 	AS 	AUc\\LL A A A A"'u(:Q(>"?"?A A A A A AFF/ / /E [?GG A A1771::U3qwwqzz??D1155QWWQZZ$3O3O
s 	AS 	AUc\\LL A A A A"'u(:Q(>"?"?A A A A A AF3S%@@E bk"8%@@AAJ  uS__c!''!**oo66 	LL #:a=&&q))**,z!}**,,,-<eDD 	# 	#A5QWWQZZ$//D #T""" t#e**r#   c                     g | d         | d         }}}| dd         D ]+}||z
  j         dk    r|                    ||f           |}|},|                    ||f           |S )z?Regroupe une liste de dates en periodes continues (debut, fin).r   r   N)r   r   )r   periodesr   
precedenter   s        r!   _periodes_contiguesr     s    "$eAhaZeHabb	  :#a''OOUJ/000E

OOUJ'(((Or#   c                     	 t          t          |                               dd                    S # t          t          f$ r Y d S w xY w)N,.)r   strreplace	TypeErrorrN   )r[   s    r!   _nombrer   %  sR    SZZ''S11222z"   tts   /2 AAc           
      >   t          | pt          |          }t          |          }i }|                                D ]\  }\  }}t	          |                    |dz                       }|0g }	t          dd          D ]}
|                    d||
fz            }|                    d||
fz            }|s|s;t	          |          }|	                    t	          |                    d|
z                      |pd||du o|du||ndd           ||	d	||<   |S )
u  
    Extrait du classeur : le solde de chacun au 1er juin 2026, et la liste des
    periodes posees depuis, semaine par semaine.

    Les libelles sont conserves tels quels (« Lu 08/06 », « du 04 au 08/08 », « x »
    pour une semaine entiere) : ils ne sont pas assez reguliers pour etre transformes
    en dates fiables, mais ils gardent la trace de ce qui a ete pose. Les periodes
    importees servent au solde, pas au planning : l'outil demarre en septembre 2026.

    Renvoie { prenom : {'solde': float, 'periodes': [...]} }
    6N   ;   z%s%dzA%dr6   )semainer   jourshors_cpnote)solder   )r\   CLASSEUR_CONGESrj   r`   r   r@   r   r   )rS   feuillerZ   colonnesre   prenomcol_periode	col_joursr   r   ligner   brutr   s                 r!   lire_congesr   ,  se    2?G<<D#D))HH,4NN,<,< B B((iS1122=1b\\ 	 	Ehhve(<<==G88Fi%7788D 4 DMMEOO"488EEM#:#:;;"=b D==T-= %4      &+AAOr#   ANMCSaVEGStPTC)AuroreNicolasu   Marie-CécileSariakau   ValérieElodieu   Élodie
Gwendolineu
   StéphaniePatriciaThomasu   ChloéTc                    t          |            |                     d           |                     d           d |                     d          D             }t          |          }t          j                                                            d          }g g ddf\  }}}}	|                                D ]\  }
}t                              |
          }|r|                    |          nd}||	                    |
           Q|                     d	||d
         t                                          f           |d         D ]}|d         |d         pddk    rdnd}t          j        |t          |d                             }t          |d         |          }|rt          |          }t!          |d         t#          t%          |                    z  d          }|D ]P\  }}|                     d||                                |                                ||d         |f           |dz  }Q|	dz  }	|                     d||                                |t          j        d          z                                   |d         |d         |f           |dz  }|	                    |
d|d         d|d         d           |                                  |rt+          dt%          |          t%          |          z
  ||	t%          |          fz             |D ]}t+          d|z             |r2t+          dd                    t/          |                    z             |S )z:Reprend les soldes et l'historique du classeur des conges.zDELETE FROM cp_soldez>DELETE FROM absence WHERE origine IN ('import', 'import-date')c                 ,    i | ]}|d          |d         S )initialeidr   r   s     r!   
<dictcomp>z#importer_soldes.<locals>.<dictcomp>b  s8     M M M jM1T7 M M Mr#   z SELECT id, initiale FROM salariesecondstimespecr   NzPINSERT INTO cp_solde (salarie_id, solde_initial, date_initiale) VALUES (?, ?, ?)r   r   r   r      r   i  r   r   zINSERT INTO absence
                             (salarie_id, date_debut, date_fin, motif, decompte, jours,
                              libelle, origine, cree_le)
                           VALUES (?, ?, ?, 'cp', 'cp', ?, ?, 'import-date', ?)r   zINSERT INTO absence
                         (salarie_id, date_debut, date_fin, motif, decompte, jours,
                          libelle, origine, cree_le)
                       VALUES (?, ?, ?, 'cp', 'cp', ?, ?, 'import', ?)r   u    : « u    » (semaine )u]   Congés importés : %d soldes, %d périodes (%d libellés datés, %d ancrés sur la semaine).z   sans date : %sz!Colonnes sans correspondance : %sz, )r3   r)   r   ry   now	isoformatr`   CORRESPONDANCEr@   r   DEBUT_PERIODEcallundi_derQ   r   r   roundr   lenrz   r2   printr9   r   )r+   rS   bavardpar_initialedonnees
maintenantinconnus	sans_dateimportes
dates_luesr   blocr   sidpr   r|   r   r   partr   r   r   s                          r!   importer_soldesr   \  s   JJ%&&&JJOPPPM MZZ(JKKM M ML&!!G"&&((22I2FFJ02B1-Hi: /I /I!%%f--,4>lx((($;OOF###


^$w-!8!8!:!:;	= 	= 	= j! $	I $	IAz!y\.Q255DD4ELAiL(9(9::E$Qy\599E I /u55QwZ%H*>*>>BB"* " "JE3JJS eoo//$9z34 4 4 MHHa


 

J %//++h0333>>@@wZ9z;< < < A  $*FFAiLLL!I,,,"H I I I IG$	IJ IIKKK V IWH-xS^^TU 	V 	V 	V  	0 	0E&.//// 	V66(CSCS9T9TTUUUOr#   c                    |                      d|f                                          }|r|d         nd}|                      d|f                                          d         }|                      d|f                                          d         }t          |d          t          |d          t          |d          t          ||z
  |z   d          dS )	zl
    Le solde de conges payes, recalcule :
        solde initial  -  jours pris  +  ajustements manuels
    z7SELECT solde_initial FROM cp_solde WHERE salarie_id = ?solde_initial        z|SELECT COALESCE(SUM(jours), 0) AS n FROM absence
            WHERE salarie_id = ? AND decompte = 'cp' AND statut = 'validee'rr   zKSELECT COALESCE(SUM(jours), 0) AS n FROM cp_ajustement WHERE salarie_id = ?r   )initialprisajustementsr   )r)   fetchoner   )r+   
salarie_idr   r   ajustes        r!   solde_cpr    s    
 jjA	 xzz  +2:go&&sG::	O	   xzz#'D
 ZZU	 xzz#'F Wa((%a.. ++7T>F2A668 8 8r#   c                     g }|                      d          D ]_}t          | |d                   }|                    |d         |d         |d         |d         d           |                    |           `|S )z6Le compteur de CP de chacun, dans l'ordre d'affichage.zlSELECT id, initiale, prenom, groupe FROM salarie
                WHERE actif = 1 ORDER BY groupe_rang, ordrer   r   r   groupe)r  r   r   r  )r)   r  updater   )r+   re   r    r   s       r!   soldesr	    s    HZZ?@ @   AdG$$1T7* {akC C 	D 	D 	DOr#   c           	         t          | |          d         }t          t          |          |z
  d          }t          |          dk     rd|dS |pd                                st          d          |                     d|||                                t          j                                        	                    d	
          |f           | 
                                 |t          | |          d         dS )z
    Corrige le solde a la main sans jamais l'ecraser : on enregistre l'ecart comme un
    ajustement date et motive. Le solde reste donc toujours explicable.
    r   r   {Gzt?r   ecartr   r6   z/un motif est obligatoire pour corriger un soldezgINSERT INTO cp_ajustement (salarie_id, jours, motif, fait_le, auteur)
           VALUES (?, ?, ?, ?, ?)r   r   )r  r   r   r   rR   rN   r)   ry   r   r   r2   )r+   r  nouveau_soldemotifauteuractuelr  s          r!   ajuster_solder    s    
 b*%%g.F%&&/33E
5zzEv...KR   MKLLLJJ	%	UEKKMM					 	 	*	*I	*	>	>	HI I I
 IIKKKXb*%=%=g%FGGGr#   r   c                    t          |            |                     d||f                                          }|r|d         nd}|                     d||f                                          d         }|dk    r|nd}t          |d          t          |d          t          |d          t          ||z   |z   d          dS )	ax  
    Le solde d'un compteur d'heures :
        solde initial  +  ecarts au cycle (recup seulement)  +  ajustements manuels

    `ecart_planning` vient de reel.compteur_recuperation : les heures retirees ou
    ajoutees a la main sur le planning. Elles n'alimentent que la recuperation ; les
    heures supplementaires, elles, resultent d'un arbitrage explicite de Jerome.
    zHSELECT solde_initial FROM heures_solde WHERE salarie_id = ? AND type = ?r   r   ziSELECT COALESCE(SUM(heures), 0) AS n FROM heures_ajustement
            WHERE salarie_id = ? AND type = ?rr   r   r   )r   planningr  r   )r3   r)   r  r   )r+   r  type_compteurecart_planningr   r  r  s          r!   solde_heuresr    s     jjR	]#% %%-XZZ  +2:go&&sGZZ	1	]#% % &.XZZ5F
 ,w66NNCEWa((eE1oo ++7U?V3Q779 9 9r#   c                 \   |pi }g }|                      d          D ]fd                                D             }|                    d          |d<   t          D ]7}t	          | |d         ||                    |d         d                    ||<   8|                    |           |S )zJLes deux compteurs d'heures de chacun. `ecarts` = { salarie_id : heures }.zSELECT id, initiale, prenom, groupe, couleur FROM salarie
                WHERE actif = 1 AND hors_planning = 0
                ORDER BY groupe_rang, ordrec                 "    i | ]}||         S r   r   )r   kr    s     r!   r   z!soldes_heures.<locals>.<dictcomp>  s    ***A1Q4***r#   r   r  r   )r)   keyspopTYPES_HEURESr  r@   r   )r+   ecartsre   r   r  r    s        @r!   soldes_heuresr    s    \rFHZZ/0 0 	 	 +******!XXd^^\) 	T 	TM".r43E}/5zz$|:Lc/R/R#T #TDOr#   c           
      <   |t           vrt          d|z            t          | |||          d         }t          t	          |          |z
  d          }t          |          dk     rd|dS |pd                                st          d          |                     d	||||                                t          j        	                                
                    d
          |f           |                                  |t          | |||          d         dS )zGCorrige un compteur d'heures a la main, en gardant la trace de l'ecart.zcompteur inconnu : %sr   r   r  r   r  r6   z2un motif est obligatoire pour corriger un compteurzuINSERT INTO heures_ajustement (salarie_id, type, heures, motif, fait_le, auteur)
           VALUES (?, ?, ?, ?, ?, ?)r   r   )r  rN   r  r   r   r   rR   r)   ry   r   r   r2   )	r+   r  r  r  r  r  r  r  r  s	            r!   ajuster_heuresr!    s3    L((1MABBB"j-HHQF%&&/33E
5zzEv...KR   PNOOOJJ	(	]E5;;==					 	 	*	*I	*	>	>	HI I I
 IIKKK!"j-PPQXY[ [ [r#   c                 r   t          t          |          d          }|dk    rt          d          |pd|z  }t          j                                                            d          }|                     d|| |||f           |                     d|||||f           |                                  d	S )
u   
    L'arbitrage du cadrage : « les deux selon les cas ». On sort des heures de la
    recuperation pour les passer en heures supplementaires a payer.
    r   r   u%   indiquer un nombre d’heures positifu:   %g h de récupération passées en heures supplémentairesr   r   z{INSERT INTO heures_ajustement (salarie_id, type, heures, motif, fait_le, auteur)
           VALUES (?, 'recup', ?, ?, ?, ?)zxINSERT INTO heures_ajustement (salarie_id, type, heures, motif, fait_le, auteur)
           VALUES (?, 'hs', ?, ?, ?, ?)N)r   r   rN   ry   r   r   r)   r2   )r+   r  r	   r  r  r   r   s          r!   basculer_recup_en_hsr#  $  s    
 5==!$$F{{ABBB "] !G"&&((22I2FFJJJ	.	fWgz6:< < < JJ	+	VWj&9; ; ; IIKKKKKr#   c                     dgg }}|r*|                     d           |                     |           |                     dd                    |          z  |          }d |D             S )N1 = 1a.salarie_id = ?zSELECT a.*, s.prenom FROM heures_ajustement a
             JOIN salarie s ON s.id = a.salarie_id
            WHERE %s ORDER BY a.fait_le DESC AND c                 ,    g | ]}t          |          S r   dictr   s     r!   r=   z%historique_heures.<locals>.<listcomp>C      $$$DGG$$$r#   r   r)   r9   )r+   r  
conditionsparamsligness        r!   historique_heuresr0  :  s    !BJ ",---j!!!ZZ	029,,z2J2J	KLRT TF %$V$$$$r#   c                 x    |r|                      d|f          }n|                      d          }d |D             S )NzSELECT a.*, s.prenom FROM cp_ajustement a
                 JOIN salarie s ON s.id = a.salarie_id
                WHERE a.salarie_id = ? ORDER BY a.fait_le DESCzSELECT a.*, s.prenom FROM cp_ajustement a
                 JOIN salarie s ON s.id = a.salarie_id
                ORDER BY a.fait_le DESCc                 ,    g | ]}t          |          S r   r)  r   s     r!   r=   z*historique_ajustements.<locals>.<listcomp>Q  r+  r#   )r)   )r+   r  r/  s      r!   historique_ajustementsr3  F  s\     	,BDN=R R
 +, , %$V$$$$r#   valideec                    d t           D             }||vrt          d|z            ||         \  }}}}|p|}||}t          j                            |          }t          j                            |          }||k     rt          d          |dk    r%|
#t          t          j        ||                    }
n|dk    rd}
|                     d||	                                |	                                |||
||rdnd||	t          j        
                                	                    d	
          f          }|                                  |j        S )u   
    Enregistre une absence. En mode « cp », les jours ouvrables sont comptes
    automatiquement (lundi -> samedi, feries exclus).
    c                      i | ]}|d          |S r   r   )r   rh   s     r!   r   z!poser_absence.<locals>.<dictcomp>]  s    )))Q1q)))r#   zmotif inconnu : %sNz'la date de fin precede la date de debutr   zINSERT INTO absence
             (salarie_id, date_debut, date_fin, motif, decompte, jours, heures,
              salaire_maintenu, statut, libelle, origine, cree_le)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'saisie', ?)r   r   r   r   )MOTIFSrN   ry   r   fromisoformatr   r   compter_ouvrablesr)   r   r   r2   	lastrowid)r+   r  
date_debutdate_finr  decompter	   salaire_maintenustatutr   r   	reference_decompte_defautmaintenu_defautr   r   curs                     r!   poser_absencerE  V  sm    *)&)))II.6777-6u-=*Aq/?*?H*M''
33E
-
%
%h
/
/C
U{{CDDD 4EMc+E37788	T		
**	A 
U__&&%	&-Avw					 	 	*	*I	*	>	>	@A AC IIKKK=r#   c                 \    |                      d|f           |                                  d S )Nz DELETE FROM absence WHERE id = ?)r)   r2   )r+   
absence_ids     r!   supprimer_absencerH  }  s*    JJ1J=AAAIIKKKKKr#   c                    dgg }}|r*|                     d           |                     |           |r*|                     d           |                     |           |r*|                     d           |                     |           |                     dd                    |          z  |          }d |D             S )Nr%  za.date_fin >= ?za.date_debut <= ?r&  zSELECT a.*, s.initiale, s.prenom, s.couleur FROM absence a
             JOIN salarie s ON s.id = a.salarie_id
            WHERE %s ORDER BY a.date_debutr'  c                 ,    g | ]}t          |          S r   r)  r   s     r!   r=   zabsences.<locals>.<listcomp>  r+  r#   r,  )r+   r   r   r  r-  r.  r/  s          r!   absencesrK    s    !BJ +,,,e
 -...c ",---j!!!ZZ	.07Z0H0H	IJPR RF %$V$$$$r#   __main__importz
%-14s %9s %8s %11s %9s)u   SalariéInitialPrisAjustementsSoldez%-14s %9.2f %8.2f %11.2f %9.2fr   r   r   r  r   )NTr%   )r   )r   N)NN)NNNr4  NN)NNN)7__doc__ry   osra   syspathdirnameabspath__file__BASEinsert
calendrierr   r9   r   FEUILLE_CONGESr   r   r7  r1   r  r/   r-   r3   r\   rj   rd   r   r   r   r   r   r   r  r	  r  r  r  r!  r#  r0  r3  rE  rH  rK  __name__basebd	connexionr+   r   argvr   rA   closer   r#   r!   <module>rc     s   *  				 				 



	wrwx0011 4       ',,rwt44fKM M dAq))
 
 
:
x  
W/ / /     6  	 	 	P P Pf	 	 	   ^ # # # #T 43c## F F F FV8 8 80
 
 
H H H H.9 9 9 94   $ /3[ [ [ [,   ,	% 	% 	% 	%% % % %" @D8<$ $ $ $N  
% % % %$ z	B
s38}}qSXa[H44	E
% )C C D D DVBZZ V V/8a	lAfIq7G7TU 	V 	V 	V 	VHHJJJJJ r#   