Site hosted by Angelfire.com: Build your free website today!

Application structurée

Christian Brissa
Juin 1988

Abstract

Structurer un programme permet de n'écrire qu'une fois une partie qui sera utilisée plusieurs fois ce qui facilite la maintenance du programme. Structurer une application est le même principe, plutôt que d'écrire, et de maintenir, plusieurs fois la sélection de données, il suffira de ne l'écrire qu'une seule fois.

Un peu d'histoire

La direction C2 de l'O.C.R., direction qui s'occupe du transport de personnes (OM & BC, Bus - Cars et Minibus) sera la première direction à étaler ses paiements sur l'année.

Jusque là, la perception était organisée annuellement, le gros travail commençant en octobre par la formation des listes, pour se terminer en janvier par la clôture des paiements. Le reste de l'année étant occupé par l'exploitation du fichier, listes et statistiques diverses étant fournies au Ministre, à la direction ou à l'extérieur.

C'est en 1988 que les modifications des programmes sont prévues, nouveau dessin de fichier, création de la moulinette et adaptation de 150 programmes sont nécessaires. Délais généreusement offert par l'administration pour ce faire : 3 mois.

3 mois sont un délai un peu juste.
Heureusement, les programmeurs savaient le travail à faire, certains ayant participé aux réunions de travail préliminaires à la création de l'A.R. obligatoire, le fichier est dessiné avant l'annonce officielle du travail, la moulinette écrite et testée avant que ce dessin soit officiellement accepté.

L'idée m'est venue, je ne sais plus trop comment, de saucissonner les programmes ; j'utilisais déjà les paramètres pour les recherches, cette idée s'est vite imposée comme la seule qui permette de garantir un travail rapide.

On ramènera toutes ces valeurs à :

25 programmes à écrire au lieu de 147, on a déjà bien gagné sa journée.

Structurer

Programme structuré

Les principaux objectifs de la programmation structurée sont les suivants :

Source : Université Laval, Formation1-cobol, Immed Jarras (pdf)

Application structurée

Les principaux objectifs de la structuration d'une application sont les suivants :

Les objectifs sont identiques à un détail près : structurer une application augmente la capacité d'interroger le fichier de base.

Au travail

Fichier

Étaler le travail sur l'année au lieu de le conserver concentré sur 3 mois ne peut se faire qu'à la condition de remplacer le paiement annuel à date fixe par un paiement annuel à une date anniversaire, la date d'immatriculation par exemple. C'est ce qui a été fait.

Second changement important, une première à l'O.C.R., si le trop perçu n'est pas (encore) remboursé, il est déduit des futures sommes à payer. Sous certaines conditions, bien sûr.

C'est le premier travail à effectuer.

Le nouveau fichier comportera des dates (demande de paiement, rappel éventuel, paiement, validité de l'autorisation, etc.) et des montants (demandé, payé, solde éventuel, etc.) que l'ancien ne connaissait pas. An 2000 oblige, les dates sont déjà prévues en 8 caractères. Dernière particularité de la description du fichier, elle est plus longue que le fichier lui-même… La normalisation des noms est possible grâce à l'appel d'une routine ASSEMBLER après quelques MOVE. Pour éviter cette douzaine d'instructions, le fichier est dessiné avec, à la fin, les zones nécessaires au CALL et les 32 caractères indispensables à la réponses.

Moulinette

La moulinette, simple outil de transformation de format, doit prévoir la somme à payer. C'est, à peu près, le seul calcul.

C'est le second travail à effectuer.

Et si le dessin du fichier n'est pas accepté, tu auras travaillé pour rien.
Non.
À moins que je sois totalement con, ce que je ne pense pas du tout (même si c'est possible), le dessin proposé correspond à la demande telle que je l'ai comprise. Et si j'ai mal compris, tout ne sera pas à refaire.

Explications.

Il n'y aura pas d'item inutile puisqu'ils viennent de l'ancien dessin.
Il pourrait en manquer ?
Oui.
Si c'est le cas, il faut modifier le dessin du fichier et écraser l'ancienne version par la nouvelle, modifier la moulinette et compiler les programmes déjà écrits. C'est tout.
La zone manquante n'a jamais été utilisée (sinon, je me serais aperçu de son absence), ça s'arrête là, le programme déjà écrit tournera avec le nouveau dessin.

Dessiner le fichier, programmer la moulinette, OK, on peut commencer sans attendre la réunion sur le dessin.

Programmes

Pour réduire le nombre de programmes à écrire, il faut examiner la structure des programmes existants. Cette structure est assez simple et, généralement, standard.

Les étapes d'un programme
listes / rapports / statistiques / virements

LST RPT STA VIR
utilisé dans C2 O N O N
sélectionner O O O O
trier O O N/O O
imprimer O O O O

Le tableau est clair, en dehors des statistiques qui n'imposent pas un tri, les étapes d'un programme sont les mêmes pour tous, on imprime après avoir trié ce qu'on a sélectionné.

Pourquoi ne pas lier VIR et LST ?
Les virements sont une triple impression, la liste, la facture et le bulletin lui-même, la partie PRNT sera totalement différente.

À l'O.C.R., le Report de COBOL n'était pas utilisé, c'est dommage, quant aux virements, ils étaient imprimés aux Finances.

Structurer l'application, gagner du temps en simplifiant grandement le travail, consistera à séparer la sélection, le tri et l'impression.
C'est simple.
Reste à le faire.

Le code qui suit ne respecte pas les colonnes de COBOL et ce, simplement parce que LaTeX n'a pas les 80 colonnes nécessaires.

C2STA01

147 programmes d'exploitation à écrire et le premier à y passer est un programme de statistiques. Avant même le programme de sélection.
Pourquoi ?
La réponse tient en peu de mots :

Ça vous semble bizarre ?
Pas à moi.
Une dizaine de formats de mise en page pour les statistiques doivent se ramener à une dizaine de programmes d'impression.
Une dizaine de programmes d'impression, cela signifie que certaines données ne peuvent être programmées, elles devront être variables et, partant, être paramétrées.

Le premier programme écrit le sera avec deux idées bien différentes en tête, chaque idée étant traitée au bon moment :

  1. avoir une apparence correcte, sans égard aux résultats affichés ;
  2. avoir des résultats exacts, l'apparence n'est plus un souci.
Titre de l'impression

Ophain-Bois-Seigneur-Isaac-lez-Nivelles est un nom de commune un peu plus long que Huy. Si l'impression veut être agréable à l’œil, il faut centrer le titre, « Immatriculations à Huy » ne se place pas au même endroit que pour l'autre commune.

Code programme, code carte, il reste environ 72 caractères pour le titre. Il suffit de compter le nombre de caractères blancs à l'arrière du titre pour en savoir la longueur. Si la zone dans laquelle on a recopié la carte est une table on centre facilement sur la ligne d'impression du titre (L02-TITRE) et on souligne de la même manière et en même temps (L03-TITRE).

  05          TITRE-WS         PIC X(72)             VALUE 'C2STA01'.
  05          FILLER           REDEFINES  TITRE-WS.
    10        CARACTERE-WS     PIC X                 OCCURS 72.
  05 L2.
    10        FILLER           PIC X.
    10        FILLER           PIC X(30)             VALUE SPACE.
    10        L02-CARACTERE    PIC X                 OCCURS 72.

TITRE-WS est initialisé avec le nom du programme pour le cas où la carte titre manquerait.

Nom du destinataire

J'ai toujours eu pour habitude de mettre mon nom sur les impressions, histoire d'éviter de voir mes tests partir par la poste en direction d'un client. L'endroit prédestiné pour ça, la première ligne, L01-NOM, initialisé avec nom, local et n° de téléphone, verra son contenu remplacé par celui du demandeur. Si la carte est manquante, le résultat me sera envoyé, le client recherché, le batch relancé après correction.
Ma ligne de titre, L01 standard.

  05 L01.
    10        FILLER           PIC X.
    10        L01-NOM          PIC X(96)
    VALUE 'BRISCHRI - A L*ETAGE - 6511'.
    10        L01-DATE         PIC X(28).
    10        L01-PAGE         PIC -(6)9.
    10        FILLER           PIC X                 VALUE '-'.

Ceux qui se demandent pourquoi tous mes programmes contiennent la ligne

TRANSFORM L01-NOM        FROM '*'            TO QUOTE

devraient avoir compris maintenant : dans L01-NOM les apostrophes sont remplacées par des astérisques, ça évite de compter la longueur de la zone avant l'apostrophe, d'ajouter un FILLER PIC X VALUE QUOTE et de terminer par ce qu'il reste des 96 caractères.

Langue de la demande

La Belgique a la chance d'être un pays polyglotte où la langue de chacun est respectée. Les langues nationales sont, par ordre alphabétique, l'allemand, le français et le néerlandais, pour ne pas compliquer les choses, on y ajoutera l'anglais.

Demander à savoir la langue du demandeur n'est pas une coquetterie, la langue est nécessaire pour remplir correctement L01-DATE et initialiser les titres de colonnes. L01-DATE est rempli à l'aide d'une routine ASSEMBLER :

CALL routine-date       USING(L01-DATE,RL)

Sans RL, pas de date en long (plus exactement, elle serait en FR, pas gênant pour un francophone, pas grave pour un germanophone ou un anglophone mais il y aura réunion d'un comité de crise si le destinataire est anversois).
Sans RL, pas de titre de colonne. Les titres sont dans une table et la bonne ligne est recopiée sur L04-TITRE, tout simplement.

Nom du destinataire et langue de la demande sont sur la même carte paramètre.

Reste

La suite est beaucoup plus simple, c'est de la programmation standard, faire en sorte que le résultat affiché soit bien celui escompté. Ce premier programme servira de modèle pour tous les autres, aussi bien pour la série C2STAnn que C2LST01.

L'écriture de C2STA02 attendra, il y a des choses plus urgentes.

C2LST01

Une liste simple, la plus simple possible, sans rupture.
Ne pas avoir de saut de page parce qu'on change de commune ou de constructeur.
Surtout, ne pas oublier de signaler aux opérateurs qu'il ne s'agit que d'un test, qu'il ne faut surtout pas imprimer. Le reste est un travail normal, il faudra veiller à traduire les noms de colonne (heureusement, une des traductrices a compris l'idée, elle m'a traduit la plupart des noms des champs, pour nommer les colonne j'ai un point de départ superbe, quel que soit le programme, les colonnes auront le même intitulé si elles contiennent la même valeur).

C2LST01 sera le modèle de base de tous les C2LSTnn.

C2SEL

La sélection est la partie la plus simple : on compare l'enregistrement à certains critères, on conserve ou non selon le résultat de la comparaison.

Lorsque les critères sont des paramètres sur carte, il faut d'abord gérer les cartes, enregistrer les paramètres, signaler qu'il y en a trop, imprimer la liste en fin de gestion des cartes. Pas trop compliqué, il faut être organisé. Selon que le paramètre sera divisible (le code INS d'une commune correspond à une commune mais permet de savoir la province et l'arrondissement de la commune) ou non (le code postal ne vous dira rien quant à la position de la commune), son enregistrement sera plus ou moins simple, mais rien de vraiment complexe, plus tard, au moment des sélections, on aura la même chose, ce sera +/- simple.

Code postal
01  TOUS-LES-PARAMETRES.
  ...
  05  TABLE-CODES-POSTAUX.
    10        CTR-POST         PIC 9(5) COMP-3       VALUE 0.
    10        MAX-POST         PIC 9(5) COMP-3       VALUE 5.
    
    10  TABLE-CPOST.
     11       FILLER           OCCURS 5.
      15      T-CPOST          PIC X(4).

MAX-POST et la longueur de la table sont identique, si OCCURS 5 passait à 6, il faudrait modifier la valeur de MAX-POST en la passant à 6 elle aussi.

Enregistrement

Ajouter 1 à CTR-POST, si on obtient plus que la valeur de MAX-POST, il faut traiter l'erreur (message à la console, -1 – ce qui permet de continuer le travail – et on passe à la carte suivante), sinon, on enregistre le code postal dans T-CPOST(CTR-POST).

Fin d'enregistrement

Lorsque les paramètres de recherche sont enregistrés, on repasse par toutes les tables, la valeur de CTR devient celle de MAX (si la table est vide, MAX sera égal à zéro, inutile de lancer la recherche sur le paramètre).

Recherche
  IF A_CONSERVER AND MAX-POST GREATER THAN 0
      SET A_REJETER TO TRUE
      MOVE 1 TO CTR-POST
      PERFORM UNTIL CTR-POST > MAX-POST
          IF CPOST-C2 = T-CPOST(CTR-POST)
              SET A_CONSERVER TO TRUE
              ADD MAX-POST TO CTR-POST
          ENDIF
          ADD 1 TO CTR-POST
      END-PERFORM
  ENDIF
Code INS
01  TOUS-LES-PARAMETRES.
  ...
  05  TABLE-CODES-INS.
    10        CTR-INS          PIC 9(5) COMP-3       VALUE 0.
    10        MAX-INS          PIC 9(5) COMP-3       VALUE 5.
    10        TABLE-CINS.
     11       FILLER           OCCURS 5.
      15      T-TYP-INS        PIC X.
      15      T-COMM-INS.
        20    T-ARROND-INS.
          25  T-PROV-INS       PIC X.
          25  F-ARROND-INS     PIC X.
        20    F-COMM-INS       PIC XXX.

Le premier caractère du code est le code de la province, les deux premiers ensemble forment le code de l'arrondissement, le tout est le code, unique, de la commune.
Pour plus d'informations sur les codes INS des communes, voir le site Statbel, la Belgique en chiffres .

Rechercher sur ce code ne se fait pas de la même manière que pour un code postal : la recherche doit pouvoir se faire sur une partie du code.
Pour ce faire, il faudra définir le code INS comme étant un groupe. Il faudra interroger les morceaux qui peuvent être vides, non remplis, d'où les FILLER nommés (F-ARROND-INS et F-COMM-INS)

Enregistrement
  MOVE INS-CARD TO T-COMM-INS(CTR-INS)
  MOVE '1' TO T-TYP-INS(CTR-INS)
  IF F-COMM-INS(CTR-INS) = SPACE
      MOVE '2' TO T-TYP-INS(CTR-INS)
      IF F-ARROND-INS(CTR-INS) = SPACE
          MOVE '3' TO T-TYP-INS(CTR-INS)
      ENDIF
  ENDIF
Recherche
  IF A_CONSERVER AND MAX-INS GREATER THAN 0
      SET A_REJETER TO TRUE
      MOVE INS-C2 TO INS-WS
      MOVE 1 TO CTR-INS
      PERFORM UNTIL CTR-INS > MAX-INS
          IF T-TYP-INS(CTR-INS) = "1" AND (T-COMM-INS(CTR-INS) = COMM-WS)
              PERFORM INS_OK
          ELSE IF T-TYP-INS(CTR-INS) = "2" AND (T-ARROND-INS(CTR-INS) = ARROND-WS)
              PERFORM INS_OK
          ELSE T-TYP-INS(CTR-INS) = "3" AND (T-PROV-INS(CTR-INS) = PROV-WS)
              PERFORM INS_OK
          ENDIF
          ADD 1 TO CTR-INS
      END-PERFORM
  ENDIF

  ...
  
  INS_OK.
      SET A_CONSERVER TO TRUE
      ADD MAX-INS TO CTR-INS
      .

C2SRT

C2SRT est simple puisqu'il n'y a qu'une seule clef de tri, malgré ça, on va paramétrer le choix du tri… comme si plusieurs étaient possible, imposer l'option 1 en cas d'absence de choix (ce qui sera le cas tant qu'une seconde clef n'est pas nécessaire) et ne pas donner de clef à l'option 2, ça permettra à celui qui reprend le travail de savoir ce qu'il a à faire (et, surtout, montrer que le traitement paramétré est prêt).

PROCEDURE DIVISION.
      PERFORM INITIALISATION
      IF CLE-TRI = "1"
          PERFORM TRI-1
      ELSE IF CLE-TRI = "2"
          PERFORM TRI-2
      ENDIF
      STOP RUN
      .
      
      
  TRI-1.
      SORT SD-C2 
          ON ASCENDING KEY 
            INS-C2
            CPOST-C2
            RL-C2
            NOM_NORMALISE-C2
            NOM-C2
         USING FIC-IN GIVING FIC-OUT
      .    
      
  TRI-2.
      SORT SD-C2 
          ON ASCENDING KEY 
            RL-C2
            NOM_NORMALISE-C2
            NOM-C2
         USING FIC-IN GIVING FIC-OUT
      . 

C2SRT n'a jamais été écrit, la clef a été enregistrée et mise en œuvre dans C2SEL02.