Approche moderne d'un projet IBM i

 Approche moderne d’un projet IBM i

Structure de données vraiment simpliste (Zen… ça vient après)

FOURNISSEUR (1) → (*) PRODUIT

CLIENT (1) → (*) COMMANDE

COMMANDE (1) → (*) LIGNE_COMMANDE

PRODUIT (1) → (*) LIGNE_COMMANDE

Structure des modules RPG (couches)

A. Couche DAO (Data Access Objects) - Accès aux données

  • FOURNISSEUR_DAO - CRUD pour FOURNISSEUR

  • PRODUIT_DAO - CRUD pour PRODUIT

  • CLIENT_DAO - CRUD pour CLIENT

  • COMMANDE_DAO - CRUD pour COMMANDE

  • LIGNE_CMD_DAO - CRUD pour LIGNE_COMMANDE

B. Couche Métier (Business Logic)

  • COMMANDE_BL - Logique métier commandes (validation, calculs totaux, etc.)

  • PRODUIT_BL - Logique métier produits (vérification stock, prix, etc.)

C. Couche Service (exposition REST API) … ou bon vieux DSPF… (...soupirs…)

  • Services REST pour chaque entité

  • Format JSON pour les échanges

  • DSPF ( 😹)

Exemple concret : Module DAO pour FOURNISSEUR

Comment structurer un module DAO complet en RPG Free-Form ? Exemple pour la table FOURNISSEUR avec :

  • Procédure Fournisseur_Create()

  • Procédure Fournisseur_Read()

  • Procédure Fournisseur_Update()

  • Procédure Fournisseur_Delete() (logique)

  • Procédure Fournisseur_List() (avec filtres)

Chaque module inclut :

  • Opérations CRUD complètes (Create, Read, Update, Delete)

  • Gestion des curseurs SQL pour les listings

  • Procédures utilitaires (Exists, Get, Count)

  • Support des filtres et paramètres optionnels

  • Suppression logique (FOURNISSEUR, PRODUIT, CLIENT)

  • SQL pur DB2 for i (pas de DDS… NAAAAAAAAAAAAAN !!!! SURTOUT PAS !!!!!!!!!!!!!!!!)

**FREE

//==============================================================================

// Module: FOURNISS_DAO (Data Access Object pour FOURNISSEUR)

// Description: Gère toutes les opérations CRUD sur la table FOURNISSEUR

//==============================================================================


ctl-opt nomain thread(*yes);


//------------------------------------------------------------------------------

// Prototypes des procédures exportées

//------------------------------------------------------------------------------


// Créer un nouveau fournisseur

dcl-pr Fournisseur_Create int(10) export;

    societe varchar(30) const;

end-pr;


// Lire un fournisseur par son code

dcl-pr Fournisseur_Read ind export;

    code int(10) const;

    societe varchar(30);

    suppression char(1);

    dateCreation date;

    dateModification date;

end-pr;


// Mettre à jour un fournisseur

dcl-pr Fournisseur_Update ind export;

    code int(10) const;

    societe varchar(30) const;

end-pr;


// Supprimer logiquement un fournisseur

dcl-pr Fournisseur_Delete ind export;

    code int(10) const;

end-pr;


// Lister les fournisseurs avec filtre optionnel

dcl-pr Fournisseur_List ind export;

    filtre varchar(30) const options(*nopass);

    supprimesInclus ind const options(*nopass);

end-pr;


// Vérifier si un fournisseur existe et est actif

dcl-pr Fournisseur_Exists ind export;

    code int(10) const;

end-pr;


// Obtenir le nom d'un fournisseur

dcl-pr Fournisseur_GetNom varchar(30) export;

    code int(10) const;

end-pr;


//------------------------------------------------------------------------------

// Implémentation des procédures

//------------------------------------------------------------------------------


//------------------------------------------------------------------------------

// Créer un nouveau fournisseur

// Retourne: Code du fournisseur créé, ou 0 en cas d'erreur

//------------------------------------------------------------------------------

dcl-proc Fournisseur_Create export;

    dcl-pi *n int(10);

        societe varchar(30) const;

    end-pi;

   

    dcl-s nouveauCode int(10) inz(0);

   

    exec sql

        INSERT INTO FOURNISSEUR (SOCIETE)

        VALUES (:societe);

   

    if sqlcode = 0;

        exec sql

            SELECT IDENTITY_VAL_LOCAL()

            INTO :nouveauCode

            FROM SYSIBM.SYSDUMMY1;

    endif;

   

    return nouveauCode;

end-proc;


//------------------------------------------------------------------------------

// Lire un fournisseur par son code

// Retourne: *ON si trouvé, *OFF sinon

//------------------------------------------------------------------------------

dcl-proc Fournisseur_Read export;

    dcl-pi *n ind;

        code int(10) const;

        societe varchar(30);

        suppression char(1);

        dateCreation date;

        dateModification date;

    end-pi;

   

    exec sql

        SELECT SOCIETE, SUPPRESSION, DATE_CREATION, DATE_MODIFICATION

        INTO :societe, :suppression, :dateCreation, :dateModification

        FROM FOURNISSEUR

        WHERE CODE = :code;

   

    return (sqlcode = 0);

end-proc;


//------------------------------------------------------------------------------

// Mettre à jour un fournisseur

// Retourne: *ON si succès, *OFF sinon

//------------------------------------------------------------------------------

dcl-proc Fournisseur_Update export;

    dcl-pi *n ind;

        code int(10) const;

        societe varchar(30) const;

    end-pi;

   

    exec sql

        UPDATE FOURNISSEUR

        SET SOCIETE = :societe,

            DATE_MODIFICATION = CURRENT_DATE

        WHERE CODE = :code

          AND SUPPRESSION = '0';

   

    return (sqlcode = 0 and sqlerrd(3) > 0);

end-proc;


//------------------------------------------------------------------------------

// Supprimer logiquement un fournisseur

// Retourne: *ON si succès, *OFF sinon

//------------------------------------------------------------------------------

dcl-proc Fournisseur_Delete export;

    dcl-pi *n ind;

        code int(10) const;

    end-pi;

   

    exec sql

        UPDATE FOURNISSEUR

        SET SUPPRESSION = '1',

            DATE_MODIFICATION = CURRENT_DATE

        WHERE CODE = :code

          AND SUPPRESSION = '0';

   

    return (sqlcode = 0 and sqlerrd(3) > 0);

end-proc;


//------------------------------------------------------------------------------

// Lister les fournisseurs

// Utilise un curseur SQL pour parcourir les résultats

// Retourne: *ON si des enregistrements trouvés, *OFF sinon

//------------------------------------------------------------------------------

dcl-proc Fournisseur_List export;

    dcl-pi *n ind;

        filtre varchar(30) const options(*nopass);

        supprimesInclus ind const options(*nopass);

    end-pi;

   

    dcl-s filtreLocal varchar(30);

    dcl-s inclureSupprimes ind inz(*off);

    dcl-s pattern varchar(32);

   

    // Gestion des paramètres optionnels

    if %parms() >= 1;

        filtreLocal = %trim(filtre);

    else;

        filtreLocal = '';

    endif;

   

    if %parms() >= 2;

        inclureSupprimes = supprimesInclus;

    endif;

   

    // Construire le pattern LIKE

    if filtreLocal <> '';

        pattern = '%' + %trim(filtreLocal) + '%';

    else;

        pattern = '%';

    endif;

   

    // Déclarer le curseur

    if inclureSupprimes;

        exec sql

            DECLARE C_FOURNISSEUR CURSOR FOR

            SELECT CODE, SOCIETE, SUPPRESSION, DATE_CREATION, DATE_MODIFICATION

            FROM FOURNISSEUR

            WHERE SOCIETE LIKE :pattern

            ORDER BY SOCIETE;

    else;

        exec sql

            DECLARE C_FOURNISSEUR CURSOR FOR

            SELECT CODE, SOCIETE, SUPPRESSION, DATE_CREATION, DATE_MODIFICATION

            FROM FOURNISSEUR

            WHERE SOCIETE LIKE :pattern

              AND SUPPRESSION = '0'

            ORDER BY SOCIETE;

    endif;

   

    exec sql OPEN C_FOURNISSEUR;

   

    return (sqlcode = 0);

end-proc;

//------------------------------------------------------------------------------

// Vérifier si un fournisseur existe et est actif

// Retourne: *ON si existe et actif, *OFF sinon

//------------------------------------------------------------------------------

dcl-proc Fournisseur_Exists export;

    dcl-pi *n ind;

        code int(10) const;

    end-pi;

   

    dcl-s compte int(10);

   

    exec sql

        SELECT COUNT(*)

        INTO :compte

        FROM FOURNISSEUR

        WHERE CODE = :code

          AND SUPPRESSION = '0';

   

    return (compte > 0);

end-proc;


//------------------------------------------------------------------------------

// Obtenir le nom d'un fournisseur

// Retourne: Nom du fournisseur ou chaîne vide si non trouvé

//------------------------------------------------------------------------------

dcl-proc Fournisseur_GetNom export;

    dcl-pi *n varchar(30);

        code int(10) const;

    end-pi;

   

    dcl-s nom varchar(30) inz('');

   

    exec sql

        SELECT SOCIETE

        INTO :nom

        FROM FOURNISSEUR

        WHERE CODE = :code

          AND SUPPRESSION = '0';

   

    if sqlcode <> 0;

        nom = '';

    endif;

   

    return nom;

end-proc;

Base de Données

-- ============================================================================

-- NETTOYAGE (si besoin de recommencer)

-- ============================================================================

-- Décommenter ces lignes si vous voulez supprimer les tables existantes


/*

DROP TABLE LIGNE_COMMANDE;

DROP TABLE COMMANDE;

DROP TABLE PRODUIT;

DROP TABLE CLIENT;

DROP TABLE FOURNISSEUR;

*/


-- ============================================================================

-- TABLE: FOURNISSEUR

-- Description: Gestion des fournisseurs

-- Suppression: Logique (flag SUPPRESSION)

-- ============================================================================


CREATE TABLE FOURNISSEUR (

    CODE               INTEGER NOT NULL

                       GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),

    SOCIETE            VARCHAR(30) NOT NULL,

    SUPPRESSION        CHAR(1) NOT NULL DEFAULT '0',

    DATE_CREATION      DATE NOT NULL DEFAULT CURRENT_DATE,

    DATE_MODIFICATION  DATE NOT NULL DEFAULT CURRENT_DATE,

   

    CONSTRAINT PK_FOURNISSEUR PRIMARY KEY (CODE),

    CONSTRAINT CHK_FOURNISSEUR_SUPPRESSION CHECK (SUPPRESSION IN ('0', '1'))

);


LABEL ON TABLE FOURNISSEUR IS 'Fournisseurs';


LABEL ON COLUMN FOURNISSEUR (

    CODE               IS 'Code fournisseur',

    SOCIETE            IS 'Nom de la société',

    SUPPRESSION        IS 'Suppression logique (0=Actif, 1=Supprimé)',

    DATE_CREATION      IS 'Date de création',

    DATE_MODIFICATION  IS 'Date de dernière modification'

);


COMMENT ON TABLE FOURNISSEUR IS

'Table des fournisseurs. Suppression logique via flag SUPPRESSION.';

-- ============================================================================

-- TABLE: CLIENT

-- Description: Gestion des clients

-- Suppression: Logique (flag SUPPRESSION)

-- ============================================================================


CREATE TABLE CLIENT (

    CODE               INTEGER NOT NULL

                       GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),

    PRENOM             VARCHAR(30) NOT NULL,

    NOM                VARCHAR(30) NOT NULL,

    SUPPRESSION        CHAR(1) NOT NULL DEFAULT '0',

    DATE_CREATION      DATE NOT NULL DEFAULT CURRENT_DATE,

    DATE_MODIFICATION  DATE NOT NULL DEFAULT CURRENT_DATE,

   

    CONSTRAINT PK_CLIENT PRIMARY KEY (CODE),

    CONSTRAINT CHK_CLIENT_SUPPRESSION CHECK (SUPPRESSION IN ('0', '1'))

);


LABEL ON TABLE CLIENT IS 'Clients';


LABEL ON COLUMN CLIENT (

    CODE               IS 'Code client',

    PRENOM             IS 'Prénom',

    NOM                IS 'Nom',

    SUPPRESSION        IS 'Suppression logique (0=Actif, 1=Supprimé)',

    DATE_CREATION      IS 'Date de création',

    DATE_MODIFICATION  IS 'Date de dernière modification'

);


COMMENT ON TABLE CLIENT IS

'Table des clients. Suppression logique via flag SUPPRESSION.';

-- ============================================================================

-- TABLE: PRODUIT

-- Description: Catalogue des produits

-- Suppression: Logique (flag SUPPRESSION)

-- Relation: PRODUIT → FOURNISSEUR (obligatoire)

-- ============================================================================

CREATE TABLE PRODUIT (

    CODE               INTEGER NOT NULL

                       GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),

    DESCRIPTION        VARCHAR(30) NOT NULL,

    FOURNISSEUR        INTEGER NOT NULL,

    PRIX               DECIMAL(8,2) NOT NULL DEFAULT 0,

    SUPPRESSION        CHAR(1) NOT NULL DEFAULT '0',

    DATE_CREATION      DATE NOT NULL DEFAULT CURRENT_DATE,

    DATE_MODIFICATION  DATE NOT NULL DEFAULT CURRENT_DATE,

   

    CONSTRAINT PK_PRODUIT PRIMARY KEY (CODE),

    CONSTRAINT CHK_PRODUIT_SUPPRESSION CHECK (SUPPRESSION IN ('0', '1')),

    CONSTRAINT CHK_PRODUIT_PRIX CHECK (PRIX >= 0));

LABEL ON TABLE PRODUIT IS 'Produits';

LABEL ON COLUMN PRODUIT (

    CODE               IS 'Code produit',

    DESCRIPTION        IS 'Description du produit',

    FOURNISSEUR        IS 'Code fournisseur (FK)',

    PRIX               IS 'Prix unitaire',

    SUPPRESSION        IS 'Suppression logique (0=Actif, 1=Supprimé)',

    DATE_CREATION      IS 'Date de création',

    DATE_MODIFICATION  IS 'Date de dernière modification');

COMMENT ON TABLE PRODUIT IS

'Catalogue des produits. Suppression logique via flag SUPPRESSION. ' ||

'Lié obligatoirement à un fournisseur.';


-- Clé étrangère vers FOURNISSEUR

-- Pas de CASCADE: on ne peut pas supprimer un fournisseur qui a des produits

ALTER TABLE PRODUIT

ADD CONSTRAINT FK_PRODUIT_FOURNISSEUR

FOREIGN KEY (FOURNISSEUR)

REFERENCES FOURNISSEUR(CODE);

COMMENT ON CONSTRAINT FK_PRODUIT_FOURNISSEUR ON PRODUIT IS

'Lien vers le fournisseur. Pas de CASCADE: utiliser la suppression logique.';


-- ============================================================================

-- TABLE: COMMANDE

-- Description: En-tête des commandes

-- Suppression: Physique (avec CASCADE sur les lignes)

-- Relation: COMMANDE → CLIENT (obligatoire)

-- ============================================================================


CREATE TABLE COMMANDE (

    NUMERO             INTEGER NOT NULL

                       GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),

    DATE_COMMANDE      DATE NOT NULL DEFAULT CURRENT_DATE,

    CLIENT             INTEGER NOT NULL,

   

    CONSTRAINT PK_COMMANDE PRIMARY KEY (NUMERO)

);


LABEL ON TABLE COMMANDE IS 'Commandes (en-tête)';


LABEL ON COLUMN COMMANDE (

    NUMERO             IS 'Numéro de commande',

    DATE_COMMANDE      IS 'Date de la commande',

    CLIENT             IS 'Code client (FK)'

);


COMMENT ON TABLE COMMANDE IS

'En-tête des commandes. Suppression physique avec CASCADE sur LIGNE_COMMANDE.';


-- Clé étrangère vers CLIENT

-- Pas de CASCADE: on ne peut pas supprimer un client qui a des commandes

ALTER TABLE COMMANDE

ADD CONSTRAINT FK_COMMANDE_CLIENT

FOREIGN KEY (CLIENT)

REFERENCES CLIENT(CODE);


COMMENT ON CONSTRAINT FK_COMMANDE_CLIENT ON COMMANDE IS

'Lien vers le client. Pas de CASCADE: protège l''historique des commandes.';

-- ============================================================================

-- TABLE: LIGNE_COMMANDE

-- Description: Détail des lignes de commande

-- Suppression: Physique avec CASCADE depuis COMMANDE

-- Relations: LIGNE_COMMANDE → COMMANDE (obligatoire, CASCADE)

--            LIGNE_COMMANDE → PRODUIT (obligatoire)

-- ============================================================================


CREATE TABLE LIGNE_COMMANDE (

    NUMERO_COMMANDE    INTEGER NOT NULL,

    NUMERO_LIGNE       INTEGER NOT NULL,

    PRODUIT            INTEGER NOT NULL,

    QUANTITE           INTEGER NOT NULL DEFAULT 1,

    PRIX               DECIMAL(9,2) NOT NULL DEFAULT 0,

   

    CONSTRAINT PK_LIGNE_COMMANDE PRIMARY KEY (NUMERO_COMMANDE, NUMERO_LIGNE),

    CONSTRAINT CHK_LIGNE_COMMANDE_QUANTITE CHECK (QUANTITE > 0),

    CONSTRAINT CHK_LIGNE_COMMANDE_PRIX CHECK (PRIX >= 0)

);


LABEL ON TABLE LIGNE_COMMANDE IS 'Lignes de commande (détail)';


LABEL ON COLUMN LIGNE_COMMANDE (

    NUMERO_COMMANDE    IS 'Numéro de commande (FK)',

    NUMERO_LIGNE       IS 'Numéro de ligne',

    PRODUIT            IS 'Code produit (FK)',

    QUANTITE           IS 'Quantité commandée',

    PRIX               IS 'Prix total de la ligne (quantité × prix unitaire)'

);


COMMENT ON TABLE LIGNE_COMMANDE IS

'Détail des lignes de commande. ' ||

'Supprimées automatiquement (CASCADE) quand la commande est supprimée.';


-- ⭐ Clé étrangère vers COMMANDE avec CASCADE

-- CASCADE: Si on supprime une COMMANDE, ses lignes n'ont plus de sens

ALTER TABLE LIGNE_COMMANDE

ADD CONSTRAINT FK_LIGNE_COMMANDE_COMMANDE

FOREIGN KEY (NUMERO_COMMANDE)

REFERENCES COMMANDE(NUMERO)

ON DELETE CASCADE;


COMMENT ON CONSTRAINT FK_LIGNE_COMMANDE_COMMANDE ON LIGNE_COMMANDE IS

'Lien vers la commande. CASCADE activé: suppression automatique des lignes.';

-- Clé étrangère vers PRODUIT

-- Pas de CASCADE: on ne peut pas supprimer un produit référencé dans des commandes

ALTER TABLE LIGNE_COMMANDE

ADD CONSTRAINT FK_LIGNE_COMMANDE_PRODUIT

FOREIGN KEY (PRODUIT)

REFERENCES PRODUIT(CODE);


COMMENT ON CONSTRAINT FK_LIGNE_COMMANDE_PRODUIT ON LIGNE_COMMANDE IS

'Lien vers le produit. Pas de CASCADE: protège l''historique des commandes.';


-- ============================================================================

-- INDEX POUR AMÉLIORER LES PERFORMANCES

-- ============================================================================


-- Index sur les colonnes de recherche fréquente

CREATE INDEX IDX_FOURNISSEUR_SOCIETE

    ON FOURNISSEUR(SOCIETE);


CREATE INDEX IDX_PRODUIT_DESCRIPTION

    ON PRODUIT(DESCRIPTION);


CREATE INDEX IDX_PRODUIT_FOURNISSEUR

    ON PRODUIT(FOURNISSEUR);


CREATE INDEX IDX_CLIENT_NOM

    ON CLIENT(NOM);


CREATE INDEX IDX_CLIENT_PRENOM

    ON CLIENT(PRENOM);


CREATE INDEX IDX_COMMANDE_CLIENT

    ON COMMANDE(CLIENT);


CREATE INDEX IDX_COMMANDE_DATE

    ON COMMANDE(DATE_COMMANDE);


CREATE INDEX IDX_LIGNE_PRODUIT

    ON LIGNE_COMMANDE(PRODUIT);

-- ============================================================================

-- VÉRIFICATIONS POST-INSTALLATION

-- ============================================================================


-- Afficher les tables créées

SELECT TABLE_NAME, TABLE_TEXT, TABLE_TYPE

FROM QSYS2.SYSTABLES

WHERE TABLE_SCHEMA = CURRENT_SCHEMA

  AND TABLE_TYPE = 'T'

ORDER BY TABLE_NAME;


-- Afficher les contraintes

SELECT TABLE_NAME,

       CONSTRAINT_NAME,

       CONSTRAINT_TYPE,

       DELETE_RULE,

       UPDATE_RULE

FROM QSYS2.SYSCST

WHERE CONSTRAINT_SCHEMA = CURRENT_SCHEMA

ORDER BY TABLE_NAME, CONSTRAINT_NAME;


-- Afficher les index

SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAMES

FROM QSYS2.SYSINDEXES

WHERE TABLE_SCHEMA = CURRENT_SCHEMA

ORDER BY TABLE_NAME, INDEX_NAME;


-- ============================================================================

-- TEST DU CASCADE

-- ============================================================================


-- Créer une commande test

INSERT INTO COMMANDE (CLIENT, DATE_COMMANDE)

VALUES (1, CURRENT_DATE);


-- Récupérer le numéro généré (devrait être 4)

VALUES IDENTITY_VAL_LOCAL();


-- Ajouter des lignes à la commande test (numéro 4)

INSERT INTO LIGNE_COMMANDE (NUMERO_COMMANDE, NUMERO_LIGNE, PRODUIT, QUANTITE, PRIX)

VALUES (4, 1, 1, 1, 100.00);


INSERT INTO LIGNE_COMMANDE (NUMERO_COMMANDE, NUMERO_LIGNE, PRODUIT, QUANTITE, PRIX)

VALUES (4, 2, 2, 1, 50.00);


-- Vérifier les lignes

SELECT * FROM LIGNE_COMMANDE WHERE NUMERO_COMMANDE = 4;

-- Devrait afficher 2 lignes


-- Supprimer la commande (les lignes doivent être supprimées automatiquement)

DELETE FROM COMMANDE WHERE NUMERO = 4;


-- Vérifier que les lignes ont disparu

SELECT * FROM LIGNE_COMMANDE WHERE NUMERO_COMMANDE = 4;

-- Devrait afficher 0 ligne (CASCADE fonctionne !)


-- ============================================================================

-- RÉSUMÉ DE L'INTÉGRITÉ RÉFÉRENTIELLE

-- ============================================================================


/*

┌─────────────────────────────────────────────────────────────────────┐

│ SCHÉMA DE LA BASE DE DONNÉES                                        │

├─────────────────────────────────────────────────────────────────────┤

│                                                                     │

│  FOURNISSEUR (suppression logique)                                  │

│      ↑                                                              │

│      │ FK (NO CASCADE)                                              │

│      │                                                              │

│  PRODUIT (suppression logique)                                      │

│                                                                     │

│                                                                     │

│  CLIENT (suppression logique)                                       │

│      ↑                                                              │

│      │ FK (NO CASCADE)                                              │

│      │                                                              │

│  COMMANDE (suppression physique)                                    │

│      ↑                                                              │

│      │ FK (CASCADE)                                                 │

│      │                                                              │

│  LIGNE_COMMANDE (suppression physique via CASCADE)                  │

│      │                                                              │

│      │ FK (NO CASCADE)                                              │

│      ↓                                                              │

│  PRODUIT                                                            │

│                                                                     │

└─────────────────────────────────────────────────────────────────────┘

Règles de suppression:

- FOURNISSEUR → Suppression LOGIQUE (flag SUPPRESSION='1')

- PRODUIT     → Suppression LOGIQUE (flag SUPPRESSION='1')

- CLIENT      → Suppression LOGIQUE (flag SUPPRESSION='1')

- COMMANDE    → Suppression PHYSIQUE (DELETE)

- LIGNE_CMD   → Suppression AUTOMATIQUE via CASCADE


Protections:

✅ Impossible de supprimer un FOURNISSEUR qui a des PRODUITS

✅ Impossible de supprimer un CLIENT qui a des COMMANDES

✅ Impossible de supprimer un PRODUIT référencé dans des LIGNE_COMMANDE

⭐ La suppression d'une COMMANDE supprime automatiquement ses LIGNE_COMMANDE

*/


-- ============================================================================

-- FIN DU SCRIPT

-- ============================================================================


Commentaires

Articles les plus consultés