Fenêtre w_m_dom
Règles de Gestion Domicile

Documentation technique des règles de validation, de contrôle et de mise à jour implémentées dans la fenêtre de maintenance des domiciles du logiciel ULIS CS (PowerBuilder 9). Source : w_m_dom.txt — Université de Liège (ULiège).

9 Règles de Gestion — w_m_dom

La fenêtre w_m_dom est le composant central de gestion des domiciles dans ULIS CS. Elle implémente 9 règles métier distinctes, couvrant la validation de saisie, les contrôles d'intégrité référentielle, les alertes légales et la gestion concurrentielle.

🚫

Caractère Invalide — Semicolon

Interdit le caractère ; dans les champs d'adresse (domlib, domrue, domnuo, domboi, domloc) pour éviter la corruption des exports CSV/DMFA.

🏘️

Auto-résolution CPOS

Résolution automatique de la localité (domloc) à partir du code postal belge via le dictionnaire CPOS. Initialise aussi le pays par défaut (DEF_DOMPAY).

✈️

Alerte Expatriation

Affiche une information légale non bloquante (message 10526) lorsque le pays du domicile diffère de BEL. Parse la localité étrangère pour extraire le code postal et la commune.

🏥

Alerte Medex

Détecte si un agent du foyer possède un dossier de contrôle médical actif (PHMSER) lors du changement de code postal. Émet des avertissements cumulés via guo_msg.

📅

Continuité des Périodes

Impose la continuité absolue des périodes d'adresse (sans trous ni chevauchements). Scinde automatiquement la période précédente à DateDebutNouvelle - 1 jour.

📮

Subordination Territoriale CPOS

Rend le code postal obligatoire pour tout domicile belge si le paramètre $FORCECPOS$ est actif. Bloque la sauvegarde et force le focus sur le champ manquant.

🔒

Verrouillage Concurrentiel

Pose un verrou exclusif (guo_lock.uf_lockdom) sur tous les cohabitants du foyer dès le chargement. Supporte le paramètre $NOLOCK= pour les traitements automatisés.

🔄

Cascade CSAN/REGEN

Lors d'un changement de pays ou de cohabitants, invalide les carrières concernées pour recalcul REGEN différé. Met à jour le code postal des ménages CSAN en cascade.

🛡️

MDM µULIS / ULIS Central

Protège les données centrales en architecture distribuée. Bloque toute modification locale d'un domicile ou matricule importé d'ULIS Central (ID < 500 000).

Interdiction du Caractère Point-Virgule dans l'Adresse

Fichier source : w_m_dom.txt · Fonction : dw_dom::itemchanged · Lignes : 1250–1262

Cette règle interdit l'utilisation du caractère point-virgule (;) dans les champs de texte décrivant l'adresse.

Référence Guide ULIS CS v11

Pas de référence dans le Guide ULIS CS v11.

Référence Analyse de Vincent Kieffer

« La plupart du temps, il faut vérifier qu'il n'y ait pas de "point-virgule" ou ";" dans une des données reprises ici : domlib, domrue, domnuo, domboi et domloc. S'il y a un ";" dans une de ces données, le message d'avertissement est activé et visible, et l'on ne peut pas passer à une autre zone. »

Contexte et Utilité

ULIS CS exporte des fichiers d'échanges ou des flux textuels formatés (CSV ou formats plats délimités par des points-virgules) vers les institutions nationales (Banques/SEPA, DmfA, Medex). La présence d'un point-virgule dans l'adresse corromprait la structure du fichier d'export.

Comportement du Système

Lors de la modification de l'un des champs de l'adresse (domlib, domrue, domnuo, domboi, domloc) :

  1. Le système recherche la présence du caractère ;.
  2. Si détecté, un message d'avertissement est émis via guo_msg.uf_msg (message 12236, libellé 12234 : "L'adresse ne peut pas comporter de « ; ». Veuillez corriger.").
  3. L'événement renvoie 1 (return 1), ce qui rejette le changement et conserve le focus sur le champ.
// IA : Vérification sur les champs textuels de l'adresse pour interdire l'utilisation du point-virgule (";").
if pos("domlib domrue domnuo domboi domloc",  lower(dwo.name)) > 0 then
    lb_fip = uf_getfilterinprogress()

    uf_setfilterinprogress (True)
    // IA : Si un point-virgule est détecté dans la valeur saisie (data), un message d'avertissement est affiché et la saisie est rejetée.
    if pos(data, ";") > 0 then
        // 12236 "Le libellé est invalide" 12234 "L'adresse ne peut pas comporter de « ; ». Veuillez corriger."
        guo_msg.uf_msg(uf_getwindowparent(), 12236, guo_dict.uf_olex(12234), 'warning')

        uf_setfilterinprogress(lb_fip)
        // IA : Retourne 1 pour bloquer l'acceptation de la saisie et conserver le focus sur le champ actuel.
        return 1    // no accept, no focus change
    end if
    uf_setfilterinprogress(lb_fip)
end if

Résolution Automatique de la Localité (CPOS)

Fichier source : w_m_dom.txt · Fonction : dw_dom::itemchanged · Lignes : 1266–1289

Cette règle automatise la saisie de la commune en fonction du code postal encodé.

Référence Guide ULIS CS v11 (Section 3.2.1)

« La zone « Localité » est automatiquement complétée par ULIS CS dès que vous acceptez le code postal. Le ' ?' initial signifie 'Inconnu'. »

Référence Analyse de Vincent Kieffer

« Le code postal belge est basé sur le dictionnaire OLDICT='CPOS', qui permet d'ailleurs de trouver, du coup, la localité belge concernée. »

« de plus, si le code postal belge existe, la localité existe aussi et le code postal permet d'encoder directement la localité (même si l'agent responsable peut la modifier par la suite). »

Contexte et Utilité

Pour éviter les erreurs de saisie et garantir l'homogénéité des données d'adressage vers les organismes officiels belges, le système s'appuie sur le dictionnaire CPOS.

Comportement du Système

À la saisie ou modification du code postal (domcpo) :

  1. Une recherche est lancée dans le dictionnaire CPOS via guo_dict.uf_gettextwithdate.
  2. Si une localité correspondante est trouvée, le champ domloc est automatiquement mis à jour.
  3. Si aucun pays n'est renseigné (ou s'il contient ? ou est vide), le pays est initialisé à la valeur par défaut DEF_DOMPAY (généralement BEL).
case 'domcpo'
    // IA : Recherche de la localité associée au code postal saisi (data) dans le dictionnaire officiel 'CPOS' à la date du jour.
    ls = guo_dict.uf_gettextwithdate ('CPOS', data, today ())
    if ls <> '' then
        // IA : Sauvegarde de l'état du filtre en cours pour éviter les boucles d'événements.
        lb_fip = uf_getfilterinprogress ()
        uf_setfilterinprogress (True)
        // Sets name of city
        // IA : Remplacement automatique de la localité (domloc) par celle retournée par le dictionnaire.
        setitem (row, 'domloc', ls)
        // Sets default name of country if none
        // IA : Vérification si le pays est défini. Si vide, nul ou '?', il est automatiquement complété avec le pays par défaut.
        ls_pays = getitemstring (row, 'dompay')
        lb_setdefpays = isnull (ls_pays)
        if not lb_setdefpays then lb_setdefpays = (trim (ls_pays) = '?' or trim (ls_pays) = '')
        if lb_setdefpays then
            // IA : Récupération du pays par défaut dans le dictionnaire SYST (DEF_DOMPAY).
            ls_pays = guo_dict.uf_gettext ('SYST', 'DEF_DOMPAY')
            if ls_pays = '' then
                guo_msg.uf_msgw (00002, '@00009 : SYST ("DEF_DOMPAY" ?)')
                ls_pays = '?'
            end if
            setitem (row, 'dompay', ls_pays)
        end if
        uf_setfilterinprogress (lb_fip)
    end if

Alerte de Résidence à l'Étranger (Expatriation)

Fichier source : w_m_dom.txt · Fonctions : dw_dom::itemchanged, wf_informationbox_if_agent_lives_abroad · Lignes : 1321–1332, 679–704

Cette règle affiche une alerte légale informative lorsque le pays du domicile est défini en dehors de la Belgique.

Référence Guide ULIS CS v11 (Section 3.5.4)

« Et si vous ne connaissez pas le nouveau domicile de la personne qui déménage ? Ajoutez-lui tout de même un nouveau domicile en laissant vides les zones facultatives (Libellé, Rue, Numéro, Boîte et Code Postal) et en remplaçant les informations obligatoires (Localité et pays) par des « ? ». »

Référence Analyse de Vincent Kieffer (Section Localité & Pays)

« si la personne physique habite à l'étranger (donc pas en Belgique), alors on active un message donnant le code postal et la localité de l'adresse à l'étranger. Ce message n'est pas bloquant mais si la personne physique n'a pas de code postal ou de localité, alors ce sera aux risques et périls de l'agent chargé d'encoder la nouvelle adresse de cette personne. C'est effectué à partir de : wf_informationbox_if_agent_lives_abroad(dompay, domloc) »

Contexte et Utilité

Les agents résidant à l'étranger sont assujettis à des règles fiscales et sociales spécifiques (calcul de la DmfA / NISS, exclusion éventuelle de certaines obligations territoriales belges).

Comportement du Système

Lors du changement du pays (dompay) ou de la localité (domloc) dans dw_dom :

  1. Si le pays encodé est différent de la valeur par défaut (BEL), le système appelle wf_informationbox_if_agent_lives_abroad.
  2. Cette fonction parse la localité saisie pour en extraire le code postal étranger et le nom de la localité.
  3. Elle affiche une boîte d'information légale référencée par le code message 10526 et les libellés 10364 (code postal) et 10365 (localité).
// ==================== dw_dom::itemchanged ====================
    case 'dompay'
        // IA : Si le pays du domicile est modifié, on compare avec le pays par défaut de l'institution.
        ls_pays = guo_dict.uf_gettext ('SYST', 'DEF_DOMPAY')
        if ls_pays = '' then
            guo_msg.uf_msgw (00002, '@00009 : SYST ("DEF_DOMPAY" ?)')
            ls_pays = '?'
        elseif upper(data) <> ls_pays then
//          setitem(row,'domcpo',ls_null)
        end if
        // IA : Si le pays devient étranger, on déclenche l'affichage d'une alerte d'expatriation.
        wf_informationbox_if_agent_lives_abroad(data,getItemString(row, 'domloc'))// PO 04/09/03
        // IA : Repositionnement ou effacement des colonnes CP/localité selon le pays (Belgique ou étranger).
        FUNCTION POST wf_set_cpos()//PG 18/06/00
    case 'domloc'
        // IA : Si la localité change, on déclenche également l'affichage d'une alerte d'expatriation si le pays est étranger.
        wf_informationbox_if_agent_lives_abroad(getItemString(row,'dompay'),data)// PO 04/09/03

// ==================== wf_informationbox_if_agent_lives_abroad ====================
// IA : Affiche une boîte d'information légale si l'agent réside à l'étranger (nécessaire pour la conformité des déclarations sociales DMFA / ONSS).
protected function boolean wf_informationbox_if_agent_lives_abroad (string as_pays, string as_domloc);// PO 04/09/03
// Display an information (cfr WAUTOT_0470_DMFA-NISS absent.doc) if agent lives out of belgium

String ls_code_post, ls_loca
Long ll

// IA : Si le pays est la Belgique (BEL), aucune alerte d'expatriation n'est requise.
If upper(as_pays) = 'BEL' then return true
IF ISNULL(as_domloc) OR ISNULL(as_pays) THEN return true
IF trim(as_domloc) = '' OR trim(as_pays) = '' THEN return true

// IA : Découpage de la localité pour extraire séparément le code postal étranger et le nom de la localité.
ll = pos(as_domloc,' ')
IF ll = 0 THEN
    ls_code_post = as_domloc
ELSE
    ls_code_post = left(as_domloc, ll - 1)
    if ll < len(as_domloc) then
        ll = len(as_domloc) - ll
        ls_loca = right(as_domloc, ll)
    end if
END IF
// IA : Affichage de l'information via la boîte de dialogue système (message 10526).
guo_msg.uf_msgi(10526, guo_dict.uf_olex(10364)+" = ~'"+ ls_code_post+"~', "+guo_dict.uf_olex(10365)+" = ~'" + ls_loca + "~'")

return true
end function

Cohérence Géographique pour le Contrôle Médical (Medex)

Fichier source : w_m_dom.txt · Fonctions : dw_dom::itemchanged, wf_get_medex · Lignes : 1290–1320, 604–663

Cette règle alerte le gestionnaire RH d'une anomalie potentielle de localisation si un agent cohabitant a un dossier médical actif.

Référence Guide ULIS CS v11

Pas de référence dans le Guide ULIS CS v11.

Référence Analyse de Vincent Kieffer (Section Code postal)

« De plus, si l'on gère le "service traitant du SPMT" (PHMSER = "SPMT : Serv. traitant"), alors on ajoute un message relatif à toutes les personnes physiques du "nouveau" domicile et aux éventuelles personnes morales associées en indiquant que, si la personne physique change de province et fait partie de "MEDEX", il faudra aussi adapter le "MEDEX" associé. »

Contexte et Utilité

En Belgique, le service Medex gère le contrôle médical des agents du secteur public en cas d'absence. Si un agent est sous contrôle médical et que son adresse est modifiée, le médecin contrôleur risque de se rendre à une mauvaise adresse. Le système interroge immédiatement la table ULIS.U2LIP pour détecter tout dossier de contrôle médical en cours.

Comportement du Système

Lors du changement de code postal (domcpo) :

  1. Si ib_checkmedex est actif, la fonction wf_get_medex interroge ULIS.U2LIP avec le type de relation PHMSER pour les agents du domicile à la date du jour.
  2. Si des dossiers actifs sont identifiés, le système insère des messages d'alerte (warning 00142, libellés 11899 et 11900) listant les agents concernés.
  3. Ces avertissements sont affichés via guo_msg.uf_show sans bloquer la sauvegarde.

Code source PowerScript

// ==================== dw_dom::itemchanged ====================
    // VK 16/10/06
    // IA : Si le contrôle médical Medex est activé (ib_checkmedex), on vérifie les dossiers médicaux en cours pour les agents domiciliés à cette adresse.
    IF ib_checkmedex THEN
        ll_count = wf_get_medex(ll_permats[], ll_medexs[])
        // 00142 11899 11900
        // IA : Insertion de messages d'avertissement dans le gestionnaire de messages guo_msg pour notifier le gestionnaire d'un possible conflit de localisation Medex.
        guo_msg.uf_insert(parent, 00142, guo_dict.uf_olex(11899), 'warning')
        guo_msg.uf_insert(parent, 00142, guo_dict.uf_olex(11900), 'warning')
        guo_msg.uf_insert(parent, 00142, '', 'warning')
        IF ll_count > 0 THEN
            CHOOSE CASE gs_language
                CASE '1'
                    guo_msg.uf_insert(parent, 00142, 'Actuellement: ', 'warning')
                CASE '2'
                    guo_msg.uf_insert(parent, 00142, 'Nu:', 'warning')
                CASE ELSE
                    guo_msg.uf_insert(parent, 00142, '', 'warning')
            END CHOOSE
            FOR ll = 1 TO ll_count STEP +1
                // IA : Itère sur chaque cohabitant impacté pour lister les dossiers de contrôle médical (PMOS) actifs.
                IF ll_medexs[ll] = 0 THEN
                    guo_msg.uf_insert(parent, 00142, guo_dict.uf_gettext('PPHS', String(ll_permats[ll], '000000'), '1') + &
                                                     ' => ?', &
                                                     'warning')
                ELSE
                    guo_msg.uf_insert(parent, 00142, guo_dict.uf_gettext('PPHS', String(ll_permats[ll], '000000'), '1') + &
                                                     ' => ' + &
                                                     guo_dict.uf_gettext('PMOS', String(ll_medexs[ll], '000000'), '1'), &
                                                     'warning')
                END IF
            NEXT
        END IF
        guo_msg.uf_show(parent, 00142, luo_null)
    END IF

// ==================== wf_get_medex ====================
// IA : Recherche s'il existe des dossiers médicaux (service de contrôle médical) actifs à ce jour pour les agents habitant le domicile.
private function long wf_get_medex (ref long al_permats[], ref long al_medexs[]);// VK 16/10/06

Datetime    ldt_now
String      ls_msg, ls_pphinfo
Long        ll_permats[]
Long        ll_medexs[]
Long        ll_row, ll_permat, ll_medex, ll_1, ll_2

ldt_now = Datetime(Today(), Now())

// IA : Parcours de tous les cohabitants (dw_lidcoh) pour trouver les matricules d'agents.
FOR ll_Row = dw_lidcoh.RowCount() TO 1 STEP -1
    IF dw_lidcoh.getItemString(ll_row, 'lidnat') = 'PERMAT' THEN
        ll_permat = dw_lidcoh.getItemNumber(ll_row, 'lidvan')

        // IA : Analyse de l'information signalétique pour s'assurer que la personne physique possède un numéro administratif (agent actif).
        ls_pphinfo = guo_dict.uf_gettext('PPH1', string (ll_permat, '0000000000'), '1')
        ll_1 = 0 ; ll_2 = 0
        ll_1 = Pos(ls_pphinfo, '²')
        IF ll_1 > 0 THEN
            ll_2 = Pos(ls_pphinfo, '²', ll_1 + 1)
            IF ll_2 < 1 THEN ll_2 = Len(ls_pphinfo) + 1
        END IF
        IF (ll_1 = 0) OR (ll_2 = 0) OR (ll_2 = ll_1 + 1) THEN
            CONTINUE // No numadm => not an agent => skip permat
        END IF

        // IA : Requête SQL pour vérifier si un dossier médical (PHMSER) est actif à la date/heure actuelle.
          SELECT "ULIS"."U2LIP"."LIDVAN"
            INTO :ll_medex
            FROM "ULIS"."U2LIP"
           WHERE ( "ULIS"."U2LIP"."LIDNAT" = 'PHMSER' ) AND
                 ( "ULIS"."U2LIP"."LIDEXT" = :ll_permat ) AND
                 ( "ULIS"."U2LIP"."LIDEXN" = 'PH' ) AND
                 (:ldt_now between "ULIS"."U2LIP"."LIDDEB" and "ULIS"."U2LIP"."LIDFIN") USING sqlca1 ;

        // IA : Gestion du code retour SQL (SQLCode) et validation de la transaction ou rollback en cas d'erreur.
        IF sqlca1.SQLCode < 0 THEN
            ls_msg = sqlca1.uf_getMsg()
            sqlca1.uf_rollBack()
            guo_msg.uf_msge(00002, ls_msg)
            RETURN 0
        ELSEIF sqlca1.SQLCode > 0 THEN // NOT found
            sqlca1.uf_commitI()
            ll_permats[UpperBound(ll_permats[]) + 1] = ll_permat
            ll_medexs[UpperBound(ll_medexs[]) + 1] = 0
            CONTINUE
        ELSE
            sqlca1.uf_commitI()
            ll_permats[UpperBound(ll_permats[]) + 1] = ll_permat
            ll_medexs[UpperBound(ll_medexs[]) + 1] = ll_medex
        END IF
    END IF
NEXT

al_permats[] = ll_permats[]
al_medexs[] = ll_medexs[]

RETURN UpperBound(al_permats[])

end function

Continuité des Périodes et Unicité de Domicile

Fichier source : w_m_dom.txt · Fonctions : dw_dom::ue_insertrow, dw_lidcoh::ue_deleterow · Lignes : 1183–1191, 1630–1635

Cette règle impose la continuité absolue des adresses au cours de la vie de l'agent et contrôle la scission des périodes.

Référence Guide ULIS CS v11 (Section 3.1)

« Vous devez être attentif au fait que toute personne physique ne peut avoir qu'un et un seul domicile pendant une même période, mais DOIT en avoir un, et ce depuis sa date de naissance. Au niveau du domicile, la continuité des périodes est obligatoire. »

« En outre, vous devez être attentif au fait que vous ne pouvez jamais modifier les dates de validité d'un domicile. La seule chose que vous puissiez faire, c'est ajouter un domicile avec une nouvelle date de début de validité. »

Référence Analyse de Vincent Kieffer (Section Détail spécifique / dw_lidcoh)

« Une personne physique aura toujours une adresse au départ, quitte à ce que celle-ci reste à "?" dans certains cas spécifiques. A un moment donné, une personne peut changer d'adresse. Dans ce cas, l'encodage de l'ancienne adresse se termine à la date de fin donnée et celui de la nouvelle adresse débute à la date de début de cette nouvelle adresse, le lendemain de la date de fin de l'ancienne adresse. »

« Si c'est bon, l'ancien domicile de cette personne se termine à la veille du début du domicile concerné. Le reste est classique, avec l'ajout du matricule au niveau du nouveau domicile, la fin de ce même matricule à la veille pour l'ancien domicile... »

« A la suppression d'un matricule... 2. Vérifications de la dernière date de ce matricule juste avant celle du domicile et remise à jour de ce matricule à "+ l'infini" pour ce "domicile précédent qui redevient l'actuel". »

Contexte et Utilité

Les tables d'adressage BDA et BVA gèrent les adresses par périodes historiques successives. La continuité temporelle (sans trous ni chevauchements) est indispensable pour les déclarations fiscales et sociales rétroactives.

Comportement du Système

À l'insertion d'une nouvelle période :

  1. L'utilisateur encode la date de prise d'effet.
  2. Le framework guo_dateddw.uf_insert insère la ligne et ajuste automatiquement la date de fin de la période précédente à DateEffet - 1 jour.

À la suppression :

  1. Le système interdit la suppression brute si c'est l'unique adresse de l'agent (allant de gd_deb à gd_fin), à moins d'une confirmation explicite (dialogue 11046).
// IA : Règle de continuité et d'unicité temporelle. Insertion d'une nouvelle période d'adresse pour dw_dom.
// IA : L'appel à guo_dateddw.uf_insert gère la scission automatique de la période en cours et assure la continuité.
if not guo_entry.uf_datesentry(ld_deb,ld_null,gd_deb,gd_fin,'') then return
if isnull(ld_deb) then return
if ib_new_dom_the_first_of_the_month then ld_deb = Date(Year(ld_deb),Month(ld_deb),1)
if not guo_dateddw.uf_insert(this,'1=1',gd_deb,gd_fin) then return
ll=ilog.istr_date.tl_key;setnull(ilog.istr_date.tl_key)//force key change for scrolltokey
uf_scrolltokey(ll)

// IA : Règle d'unicité et de maintien d'une période par personne physique.
// IA : Si l'utilisateur tente de supprimer la dernière période de cohabitation (couvrant gd_deb à gd_fin), une confirmation est requise.
if date(getitemdatetime(ll,2))=gd_deb and date(getitemdatetime(ll,3))=gd_fin then //last row delete for this permat !
    if guo_msg.uf_msg(parent,11046,'','confirm')='KO' then return
    deleterow(ll)
    wf_scroll(uf_getdatevisu(),ll_1)
    return
end if

Subordination Territoriale (Code Postal Obligatoire)

Fichier source : w_m_dom.txt · Fonctions : wf_check_if_cpos_encoded, wf_window, dw_dom::ue_insertrow, dw_lidcoh::ue_hist · Lignes : 1012–1041, 498–505, 1164–1170, 1538–1544

Cette règle impose la saisie obligatoire d'un code postal pour tout domicile situé en Belgique.

Référence Guide ULIS CS v11 (Section 3.2.1)

« Lorsque vous encodez que l'agent est domicilié en Belgique, ULIS CS va ajouter une zone « Code Postal », destinée à recevoir le code postal, au besoin via l'option « Dictionnaire » du menu contextuel. »

« Si le domicile est hors Belgique, il n'y a qu'une zone localité dans laquelle il faut encoder le code postal et la localité. »

Référence Analyse de Vincent Kieffer (Section Code postal & wf_window)

« Si le pays est "Belgique", alors le code postal est très utile voire, dans certains cas, indispensable (par ex. pour les déclarations fiscales annuelles, DIMONA, DMFA, etc.). »

« 1. On commence par vérifier le code postal si nécessaire (si pas bon => retour sans sauver) ; »

Contexte et Utilité

L'adresse de résidence conditionne le calcul de la fiscalité locale (taxes communales et régionales) dans les déclarations DmfA et précompte professionnel. L'absence de code postal invalide la déclaration de paie. Si le pays correspond à DEF_DOMPAY (configuré à BEL par défaut), le code postal est strictement obligatoire pour sauvegarder.

Comportement du Système

Si le paramètre $FORCECPOS$ est actif et que l'utilisateur valide un domicile belge sans code postal :

  1. Le focus est forcé sur le champ domcpo de la DataWindow dw_dom.
  2. Un message d'avertissement ciblé (aide 00003) est affiché.
  3. L'enregistrement ou la sauvegarde est bloqué(e).
// IA : Cette fonction valide que le code postal (domcpo) est renseigné si le pays est la Belgique (défini par DEF_DOMPAY dans le dictionnaire SYST).
private function boolean wf_check_if_cpos_encoded ();// VK 01/12/14

String              ls_cpos

// IA : Si le dictionnaire global n'est pas valide, on court-circuite la validation.
IF NOT Isvalid(guo_dict) THEN RETURN True

// IA : Si le pays du domicile n'est pas le pays par défaut (Belgique), le code postal n'est pas obligatoire.
if dw_dom.getitemstring(1,'dompay') <> guo_dict.uf_gettext ('SYST', 'DEF_DOMPAY') then
    RETURN True
END IF

ls_cpos = dw_dom.getitemstring(1,'domcpo')

// IA : Si le code postal n'est pas nul et a une longueur après trim > 0, la validation réussit.
IF NOT isNull(ls_cpos) THEN
    IF Len(Trim(ls_cpos)) > 0 THEN
        RETURN True
    END IF
END IF

// IA : En cas d'échec, on force le focus sur la colonne 'domcpo' de la datawindow des domiciles (dw_dom).
dw_dom.SetColumn('domcpo')
IF isValid(dw_dom) THEN
    IF GetFocus() <> dw_dom THEN
        dw_dom.setFocus()
    END IF
END IF

// IA : On affiche un message d'aide (numéro 00003) pour avertir l'utilisateur.
guo_msg.uf_mhelpw( 00003, '')

RETURN FAlse

end function

Verrouillage Concurrentiel (Locking)

Fichier source : w_m_dom.txt · Fonctions : dw_lid::retrieveend, wf_lockper · Lignes : 1428–1439, 822–842

Cette règle protège les dossiers des agents contre les modifications simultanées en posant un verrou sur tous les domiciliés du foyer concerné.

Référence Guide ULIS CS v11

Pas de référence dans le Guide ULIS CS v11.

Référence Analyse de Vincent Kieffer (Section Principe général)

« l'action initiale "locke" soit une seule donnée... soit toutes les personnes physiques associées à ce domicile à un moment donné... elle ne permet donc pas qu'un autre agent puisse éventuellement modifier des données du même domicile. »

« Rien de spécifique ici si ce n'est le fait, au "retrieveend", de "locker" le domicile, ou plus précisément toutes les personnes associées à ce domicile à un moment donné... »

Contexte et Utilité

Un domicile (W_M_DOM) est une entité partagée pouvant relier plusieurs cohabitants (membres d'un ménage). Si un gestionnaire modifie l'adresse, l'impact se répercute sur tous les agents rattachés. Pour éviter les conflits d'accès concurrentiels, le système verrouille chacun des cohabitants identifiés par son matricule (PERMAT).

Comportement du Système

Au chargement initial des cohabitants dans dw_lid :

  1. Le système itère sur chaque cohabitant et appelle guo_lock.uf_lockdom pour poser un verrou exclusif.
  2. Si l'un des verrous échoue, la transaction d'édition est annulée (ue_cancel), le flag de modification est désactivé et l'accès est bloqué.
  3. Le paramètre $NOLOCK= (analysé par wf_lockper) permet d'exclure certains matricules spécifiques du verrouillage, typiquement pour les traitements en lot ou d'automatisation.
// ==================== dw_lid::retrieveend ====================
// IA : Règle de verrouillage concurrentiel. Lors du retrieveend des cohabitants du domicile, le système tente de verrouiller en édition chaque matricule (PERMAT).
// IA : Si le verrouillage échoue (déjà verrouillé par un autre utilisateur), l'édition est désactivée et la modification annulée.
        if not ib_visu then
            if not guo_lock.uf_lockdom(ll_1,is_locklist) then
                dw_dom.istr_base.tb_update=false;dw_lid.istr_base.tb_update=false
                dw_lidcoh.istr_base.tb_update=false
                // Trigger of ue_cancel in "open" process => GPF !!!
                if guo_open.ib_openinprogress then ib_lockedatopen = true &
                    else parent.postevent('ue_cancel')
                // "RETURN" is ABSOLUTELY necessary here, otherwise process goes on, but
                // window could still be refered to by this script (or other ones !!!)
                RETURN -1
            end if
        end if

// ==================== wf_lockper ====================
// IA : Fonction d'acquisition du verrou sur un matricule d'agent. Permet d'ignorer le verrouillage si l'agent est explicitement exclu via le paramètre $NOLOCK=.
private function boolean wf_lockper (long al_permat, ref string as_locklist);//GF 01/10/10 Lock permat except if exclusion list $NOLOCK=$
//GF 02/08/12 Add as_lockList, don't use is_lockList

string  ls_PermatList, ls_Permat

if Pos(istr_objectparm.ts_viewparm, "$NOLOCK=") > 0 then
    ls_Permat = String(al_Permat)
    wf_Unstrip(istr_objectparm.ts_viewparm, "$NOLOCK=", ls_PermatList)

    if IsNull(ls_PermatList) or ls_PermatList = "" or (Pos(ls_PermatList, ls_Permat) <= 0) then
        return guo_lock.uf_lockPer(al_permat, as_locklist)
    else
        return true // permat is in list so no lock
    end if
else
    return guo_lock.uf_lockPer(al_permat, as_locklist)
end if

return true

end function

Cascade CSAN et Invalidation des Carrières (REGEN)

Fichier source : w_m_dom.txt · Fonctions : wf_window, wf_updatecsan · Lignes : 562–575, 579–598, 265–305

Cette règle garantit la mise à jour cohérente du statut de cohabitation et le recalcul des carrières lors du changement de domicile.

Référence Guide ULIS CS v11

Pas de référence dans le Guide ULIS CS v11.

Référence Analyse de Vincent Kieffer (Section wf_window & Historique)

« Ensuite, si l'on gère le "centre de santé" (ib_phcsan) a changé, il faut confirmer l'éventuel changement... »

« 2. Ensuite, on vérifie si les carrières (dossiers administratifs, pécuniaires, etc.) doivent être modifiées (le pays du domicile et/ou les cohabitants peuvent avoir changé), d'où il faut, si nécessaire, invalider le ou les dossiers concernés ; »

« 3. Enfin, on vérifie aussi, si nécessaire, si le centre de santé est correct (sinon, on retourne avec un avertissement, sans sauver et sans sortir de la fenêtre). »

« Cette fonction [wf_super_window] inspecte, si nécessaire, les modifications qui ont été introduites dans les trois datawindows dw_dom, dw_lid et dw_lidcoh, et sur base de celles-ci, retourne la liste des matricules dont l'historique des domiciliations serait modifié si la sauvegarde était effectuée maintenant. »

Contexte et Utilité

Un changement d'adresse peut modifier le statut de cohabitation fiscale ou sociale (CSAN). Tout changement doit être propagé aux cohabitants déclarés et forcer le recalcul du moteur de paie/statut (processus REGEN différé).

Comportement du Système

Lors de la sauvegarde globale (wf_window) avec l'option UPDATE :

  1. Si les conditions de régénération (REGEN) sont remplies (changement de pays DOMPAY ou modification des cohabitants dw_lidcoh), le système parcourt chaque membre du ménage.
  2. Pour chaque membre, il acquiert un verrou partagé (guo_lock.uf_lockdosshared) et invalide formellement sa carrière (guo_mgr.uf_invalidate_permat), déclenchant son recalcul au prochain batch REGEN.
  3. Si la gestion CSAN est activée (ib_phcsan), wf_updatecsan met à jour en cascade le code postal des ménages concernés via u_nv_updatecsan. Si la mise à jour échoue, un rollback SQL est opéré.
// ==================== wf_window ====================
    // Invalidation of all (co)resident careers
    // IA : Si un élément déclencheur de recalcul est modifié (ex: pays ou cohabitants), invalider les carrières associées pour planification d'un REGEN différé.
    if lb_regen then
        for ll = dw_lidcoh.RowCount() to 1 step -1
            ll_permat = dw_lidcoh.GetItemNumber(ll, 'lidvan')
            if ll_permat > 0 then
                // IA : Verrouillage partagé du dossier de l'agent pour modification sécurisée.
                if not guo_lock.uf_lockdosshared(ll_permat , is_locklist) then
                    guo_msg.uf_msgw(11028, guo_dict.uf_olex(00007))
                    return false
                end if
                if not ib_uliscmed then //LS 22/03/05 : malvoz
                   // IA : Invalidation formelle de la carrière de l'agent dans guo_mgr.
                   if not guo_mgr.uf_invalidate_permat(ll_permat, false) then return false
                end if
            end if
        next
    end if
end if

//RO 30/07/09
// IA : Si l'option d'intégrité CSAN est active (ib_phcsan), mettre à jour en cascade les dossiers cohabitants lors d'un UPDATE.
if upper(as_options) = 'UPDATE' and ib_phcsan then
    if dw_dom.rowCount() > 0 then
        String  ls_domcpo

        ls_domcpo = dw_dom.getItemString(1, 'domcpo')
        // IA : Propagation du code postal (ls_domcpo) à l'ensemble du ménage / cohabitants via la fonction wf_updatecsan.
        if not wf_updatecsan(ls_domcpo) then
            dw_dom.itr.uf_rollback()
            return false
        end if
    else
        return wf_super_window(as_options)
    end if
    // IA : Commit ou rollback de la transaction SQL en fonction du résultat de la sauvegarde globale (wf_super_window).
    if wf_super_window(as_options) then
        dw_dom.itr.uf_commiti()
        return true
    else
        dw_dom.itr.uf_rollback()
        return false
    end if
end if

// ==================== wf_updatecsan ====================
// IA : Met à jour la table des cohabitants (PHCSAN) avec le code postal as_cpo pour l'ensemble des habitants identifiés par PERMAT.
protected function boolean wf_updatecsan (string as_cpo);//RO 30/07/09 21/08/09
//GF 26/02/10
//GF 01/10/10   call wf_LockPer
//GF 02/08/12   Use ls_lockList not is_lockList
//RO 27/09/13 Refactoring
//GF 26/07/24 Vérifie as_cpo (SVICOMF-2336)

//Updates the PHCSAN given the post code as_cpo for all the inhabitant if necessary. Commit have to be done by calling function !
//Returns true if succeeds, false otherwise.

long ll_permat, ll_count, ll_i, ll_isAgent, ll_return, ll_umax, ll_phcsan_new
datetime ldt_new, ldt_newm1, ldt_fin
string ls_msg, ls
string  ls_lockList
u_nv_updatecsan luo_csan

// IA : Instanciation de l'objet de gestion de mise à jour CSAN (u_nv_updatecsan).
luo_csan = create u_nv_updatecsan
luo_csan.uf_constructor(dw_dom.itr,istr_objectparm.ts_viewparm,f_map())

ll_count = dw_lidcoh.rowCount()
// IA : Itération sur tous les cohabitants enregistrés dans la datawindow (dw_lidcoh).
for ll_i = 1 to ll_count
    // IA : Seuls les matricules physiques d'agents (PERMAT) sont concernés.
    if dw_lidcoh.getItemString(ll_i, 'lidnat') <> 'PERMAT' then continue

    ll_permat = dw_lidcoh.getItemNumber(ll_i,'lidvan')
    ldt_new = dw_lidcoh.getItemDatetime(ll_i,'liddeb')
    ldt_fin = dw_lidcoh.getItemDatetime(ll_i,'lidfin')

    //RO 27/09/13 Centralize the business logic because we need to call it in w_m_dosu2
    // GF 26/07/24
    // IA : Si le code postal est nul, mise à jour simple sans code postal. Sinon, mise à jour ciblée avec le code postal.
    if isNull(as_cpo) then
        // J'ai l'impression que cela ne sert a rien si on ne donne pas un code postal
        if not luo_csan.uf_update(ll_permat, ldt_new, ldt_fin) then return false
    else
        if not luo_csan.uf_update(ll_permat,as_cpo,ldt_new,ldt_fin) then return false
    end if
next
if isValid(luo_csan) then destroy luo_csan

return true

end function

Master Data Management (µULIS / ULIS Central)

Fichier source : w_m_dom.txt · Fonctions : dw_lidcoh::ue_deleterow, dw_lidcoh::ue_insertrow, wf_open2 · Lignes : 1609–1627, 1666–1672, 1688–1699, 752–763

Contexte de migration : Bien que cette règle soit documentée par Vincent Kieffer comme obsolète pour la future cible de production (les serveurs satellites distribués n'étant plus d'actualité), elle reste pleinement active dans le code source PowerBuilder 9 analysé. Elle doit donc être prise en compte dans l'analyse de l'existant.

Cette règle protège l'intégrité des données centrales d'adressage dans un déploiement décentralisé satellite µULIS.

Référence Guide ULIS CS v11

Pas de référence dans le Guide ULIS CS v11.

Référence Analyse de Vincent Kieffer (Section Détail spécifique dw_lidcoh)

« La partie MicroUlis (ou µULIS) n'est plus d'application, nous ne parlerons donc pas du reste, spécifique à cette partie µULIS. »

« Encore une fois, nous ne parlerons pas de µULIS pour le reste, qui n'a en effet plus lieu d'être. »

Contexte et Utilité

Dans l'architecture distribuée d'Ulis CS, certaines entités décentralisées (serveurs satellites d'hôpitaux ou de facultés) utilisent une base locale appelée µulisserver. Pour éviter des désynchronisations avec la base centrale d'ULIS (alimentée par le Registre National), les agents centraux (ID de personne physique ou domicile < 500000) ne doivent pas être modifiés localement.

Comportement du Système

Si l'environnement est détecté comme µulisserver :

  1. Ouverture (wf_open2) : si le matricule est < 500000, la DataWindow dw_dom est verrouillée en lecture seule (protected).
  2. Insertion (ue_insertrow) ou suppression (ue_deleterow) d'un cohabitant dans dw_lidcoh : si l'ID du domicile (il_objectid) ou le matricule (lidvan) est < 500000, l'opération est bloquée.
  3. Un avertissement en français est affiché : "Domicile provenant d'ULIS !" ou "Matricule provenant d'ULIS !" (code 11030).
// ==================== dw_lidcoh::ue_deleterow ====================
// IA : Dans un environnement satellite (µulisserver), les modifications sur les domiciles ou matricules importés du serveur central (identifiés par un ID < 500000) sont formellement bloquées.
if not ib_visu and f_getservertype () = 'µulisserver' then
    // IA : Bloque si le domicile (il_objectid) provient d'ULIS central.
    if il_objectid < 500000 then
        // Not allowed to modify a dom coming from ULIS (french msg because µULIS only) !!!
        guo_msg.uf_msg (parent, 11030, "Domicile provenant d'ULIS !", 'warning')
        RETURN
    end if
end if

ll=getrow()
if ll=0 then return

ll_1=getitemnumber(ll,'lidvan')  // YQ 31/07/00 : Gets agent's matricule
// IA : Bloque si le matricule de l'agent cohabitant (ll_1) provient d'ULIS central.
if not ib_visu and f_getservertype () = 'µulisserver' then
    if ll_1 < 500000 then
        // Not allowed to modify a dom for a PPH coming from ULIS (french msg because µULIS only) !!!
        guo_msg.uf_msg (parent, 11030, "Matricule provenant d'ULIS !", 'warning')
        RETURN
    end if
end if

// ==================== dw_lidcoh::ue_insertrow ====================
// IA : Dans un environnement satellite (µulisserver), bloquer l'insertion d'un nouveau cohabitant si le domicile courant provient de l'ULIS central.
if not ib_visu and f_getservertype () = 'µulisserver' then
    if il_objectid < 500000 then
        // Not allowed to modify a dom coming from ULIS (french msg because µULIS only) !!!
        guo_msg.uf_msg (parent, 11030, "Domicile provenant d'ULIS !", 'warning')
        RETURN
    end if
end if

// ... [code d'insertion] ...

// IA : Empêcher l'insertion d'un cohabitant dont le matricule est importé de l'ULIS central (ID < 500000).
if not ib_visu and f_getservertype () = 'µulisserver' then
    ll = pos (ls, '$')
    if ll > 0 then ls = left (ls, ll - 1)
    if len (ls) > 0 then
        ll = long (ls)
        if ll < 500000 then
            // Not allowed to modify a dom for a PPH coming from ULIS (french msg because µULIS only) !!!
            guo_msg.uf_msg (parent, 11030, "Matricule provenant d'ULIS !", 'warning')
            RETURN
        end if
    end if
end if

// ==================== wf_open2 ====================
// protect bda if ULIS permat in µULIS (=> local adresses only for local people !)
// IA : En mode µULIS, si l'agent identifié par il_objectid est un agent central (ID < 500000), son domicile est verrouillé en lecture seule.
if not ib_visu and f_getservertype () = 'µulisserver' then
    if il_objectid < 500000 then
        dw_dom.uf_settsprotect ('a')
        dw_dom.uf_dwmodify ('protected')
        dw_lidcoh.uf_popsetoptions ('', 'i', '')
    else
        dw_dom.uf_settsprotect ('')
        dw_dom.uf_dwmodify ('restore')
        dw_lidcoh.uf_popsetoptions ('i', '', '')
    end if
end if