From 51bed2b4f4e4468edb7b0639ce741229f17524bd Mon Sep 17 00:00:00 2001 From: Florian Briand Date: Mon, 7 Oct 2024 19:24:55 +0200 Subject: [PATCH] feat: implement group2, CPS situation for SSV_LireCartePS --- crates/fsv/src/fsv_parsing/blocks.rs | 4 + crates/fsv/src/fsv_parsing/groups/mod.rs | 33 +- .../fsv_parsing/groups/ssv_lire_carte_ps.rs | 479 +++++++++++++++++- crates/fsv/src/ssv/mod.rs | 27 +- 4 files changed, 522 insertions(+), 21 deletions(-) diff --git a/crates/fsv/src/fsv_parsing/blocks.rs b/crates/fsv/src/fsv_parsing/blocks.rs index 6dd2372..9676854 100644 --- a/crates/fsv/src/fsv_parsing/blocks.rs +++ b/crates/fsv/src/fsv_parsing/blocks.rs @@ -58,6 +58,10 @@ pub enum DataGroup { LireCartePS_Group1_Holder( #[deku(reader = "read_with_size(deku::reader, data_size as usize)")] groups::ssv_lire_carte_ps::group_1_holder::Holder), + #[deku(id = 2)] + LireCartePS_Group2_Situation( + #[deku(reader = "read_with_size(deku::reader, data_size as usize)")] + groups::ssv_lire_carte_ps::group_2_situation::Situation), #[deku(id = 60)] LireConfig_Group60_ConfigHeader( #[deku(reader = "read_with_size(deku::reader, data_size as usize)")] diff --git a/crates/fsv/src/fsv_parsing/groups/mod.rs b/crates/fsv/src/fsv_parsing/groups/mod.rs index 132acea..3779bf0 100644 --- a/crates/fsv/src/fsv_parsing/groups/mod.rs +++ b/crates/fsv/src/fsv_parsing/groups/mod.rs @@ -21,6 +21,16 @@ where .map_err(|e| DekuError::Parse(e.to_string().into())) } +/// # Extract raw bytes from a DataField, through a Vec +/// This function is used as deku map function to extract raw bytes +/// from a DataField +fn map_raw_from_data_field(data_field: DataField) -> Result +where + T: From>, +{ + Ok(data_field.data.into()) +} + /// # Extract an enum id from a string /// Deku enums only supports numbers as id, and we usually extract strings /// from data fields. This function is used as a context function to convert @@ -47,11 +57,11 @@ struct DataField { #[deku_derive(DekuRead)] #[derive(Debug, Clone, PartialEq)] -/// # Numeric string +/// # Data field: Numeric string (x CN) /// TODO: check if all the characters are numeric pub struct NumericString( #[deku(map = "map_from_data_field")] - String + pub String ); impl From<&str> for NumericString { fn from(s: &str) -> Self { @@ -61,9 +71,10 @@ impl From<&str> for NumericString { #[deku_derive(DekuRead)] #[derive(Debug, Clone, PartialEq)] +/// # Data field: Alphanumeric string (x CA/CE) pub struct AlphaNumericString( #[deku(map = "map_from_data_field")] - String + pub String ); impl From<&str> for AlphaNumericString { fn from(s: &str) -> Self { @@ -71,6 +82,22 @@ impl From<&str> for AlphaNumericString { } } +#[deku_derive(DekuRead)] +#[derive(Debug, Clone, PartialEq)] +/// # Data field: Raw bytes (x CB) +pub struct RawBytes( + #[deku(map = "map_raw_from_data_field")] + pub Vec +); + +#[deku_derive(DekuRead)] +#[derive(Debug, Clone, PartialEq)] +/// # Data field: Raw byte (1 CB) +pub struct RawByte( + #[deku(map = "|x: DataField| -> Result { Ok(x.data[0]) }")] + pub u8 +); + #[deku_derive(DekuRead)] #[derive(Debug, Clone, PartialEq)] #[deku(endian = "big")] diff --git a/crates/fsv/src/fsv_parsing/groups/ssv_lire_carte_ps.rs b/crates/fsv/src/fsv_parsing/groups/ssv_lire_carte_ps.rs index fc6f22a..6fbe567 100644 --- a/crates/fsv/src/fsv_parsing/groups/ssv_lire_carte_ps.rs +++ b/crates/fsv/src/fsv_parsing/groups/ssv_lire_carte_ps.rs @@ -1,15 +1,14 @@ //! # Structures de parsing des données de la fonction SSV_LireCartePS +#![allow(clippy::explicit_auto_deref)] // False positive on ctx attributes when using extract_enum_id_from_str use deku::deku_derive; -use crate::fsv_parsing::groups::NumericString; +use crate::fsv_parsing::groups::{ extract_enum_id_from_str, AlphaNumericString, NumericString, RawByte }; /// # Titulaire /// 1 occurence pub mod group_1_holder { - use crate::fsv_parsing::groups::{ extract_enum_id_from_str, AlphaNumericString }; - use super::*; /// Groupe 1 - Titulaire @@ -17,24 +16,24 @@ pub mod group_1_holder { #[derive(Debug, PartialEq)] pub struct Holder { #[deku(temp)] - card_type_raw: NumericString, + card_type_raw: NumericString, // Champ 1 : Type de carte PS (2 CN) #[deku(ctx = "extract_enum_id_from_str::(&*card_type_raw.0, 255)")] pub card_type: CardPSType, #[deku(temp)] - national_id_type_raw: NumericString, + national_id_type_raw: NumericString, // Champ 2 : Type d’identification nationale (1 CN) #[deku(ctx = "extract_enum_id_from_str::(&*national_id_type_raw.0, 255)")] pub national_id_type: NationalIDType, // TODO: handle national_id depending on national_id_type - pub national_id: AlphaNumericString, // /!\ CE and not CA - pub national_id_key: AlphaNumericString, + pub national_id: AlphaNumericString, // /!\ CE and not CA - Champ 3 : N° d’identification nationale (8-30 CE) + pub national_id_key: AlphaNumericString, // Champ 4 : Clé du N° d’identification nationale (1 CN) #[deku(temp)] - civility_code_raw: NumericString, + civility_code_raw: NumericString, // Champ 5 : Code civilité (2 CN) #[deku(ctx = "extract_enum_id_from_str::(&*civility_code_raw.0, 255)")] pub civility_code: CivilityCode, - pub holder_lastname: AlphaNumericString, // /!\ CE and not CA - pub holder_firstname: AlphaNumericString, // /!\ CE and not CA + pub holder_lastname: AlphaNumericString, // /!\ CE and not CA - Champ 6 : Nom du PS (27 CE) + pub holder_firstname: AlphaNumericString, // /!\ CE and not CA - Champ 7 : Prénom du PS (27 CE) #[deku(temp)] - category_card_size: u8, + category_card_size: u8, // Champ 8 : Catégorie Carte (1 CA) pub category_card: CategoryCard, } @@ -180,10 +179,432 @@ pub mod group_1_holder { } } +/// # Situation +/// 1-16 occurences +pub mod group_2_situation { + + use super::*; + + /// Groupe 2 - Situation du PS + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + pub struct Situation { + pub id: RawByte, // Champ 1 : N° logique de la situation de facturation du PS (1 CB) + #[deku(temp)] + practice_mode_raw: NumericString, // Champ 2 : Mode d’exercice (2 CN) + #[deku(ctx = "extract_enum_id_from_str::(&*practice_mode_raw.0, 255)")] + pub practice_mode: PracticeMode, + // #[deku(temp)] + pub practice_status_raw: NumericString, // Champ 3 : Statut d’exercice (3 CN) + // #[deku(ctx = "extract_enum_id_from_str::(&*practice_status_raw.0, 255)")] + // pub practice_status: PracticeStatus, + #[deku(temp)] + activity_sector_raw: NumericString, // Champ 4 : Secteur d’activité (3 CN) + #[deku(ctx = "extract_enum_id_from_str::(&*activity_sector_raw.0, 255)")] + pub activity_sector: ActivitySector, + #[deku(temp)] + structure_id_type_raw: NumericString, // Champ 5 : Type d’identification structure (1 CN) + #[deku(ctx = "extract_enum_id_from_str::(&*structure_id_type_raw.0, 255)")] + pub structure_id_type: StructureIDType, + pub structure_id: AlphaNumericString, // Champ 6 : N° d’identification structure (14 CA) + pub structure_id_key: NumericString, // Champ 7 : Clé du n° d’identification structure (1 CN) + pub structure_name: AlphaNumericString, // Champ 8 : Raison sociale structure (40 CE) + pub ps_billing_number: NumericString, // Champ 9 : N° d’identification de facturation du PS (8 CN) + pub ps_billing_number_key: NumericString, // Champ 10 : Clé du n° d’identification de facturation du PS (1 CN) + pub ps_replacement_number: AlphaNumericString, // Champ 11 : N° d’identification du PS remplaçant (30 CA) -- TODO OPTIONNEL + pub ps_replacement_number_key: NumericString, // Champ 12 : Clé du n° d’identification du PS remplaçant (1 CN) -- TODO OPTIONNEL + #[deku(temp)] + convention_code_raw: NumericString, // Champ 13 : Code conventionnel (1 CN) + #[deku(ctx = "extract_enum_id_from_str::(&*convention_code_raw.0, 255)")] + pub convention_code: ConventionCode, + #[deku(temp)] + specialty_code_raw: NumericString, // Champ 14 : Code spécialité (2 CN) + #[deku(ctx = "extract_enum_id_from_str::(&*specialty_code_raw.0, 255)")] + pub specialty_code: SpecialtyCode, + // #[deku(temp)] + pub rate_zone_code_raw: NumericString, // Champ 15 : Code zone tarifaire (2 CN) + // #[deku(ctx = "extract_enum_id_from_str::(&*rate_zone_code_raw.0, 255)")] + // pub rate_zone_code: RateZoneCode, // CF p53-55 - Attribution complexe, dépendant du practice_status, du specialty_code et du convention_code + #[deku(temp)] + ik_zone_code_raw: NumericString, // Champ 16 : Code zone IK - Indemnité kilométrique (2 CN) + #[deku(ctx = "extract_enum_id_from_str::(&*ik_zone_code_raw.0, 255)")] + pub ik_zone_code: IkZoneCode, + #[deku(temp)] + approval_code_1_raw: NumericString, // Champ 17 : Code agrément 1 (1 CN) + #[deku(ctx = "extract_enum_id_from_str::(&*approval_code_1_raw.0, 255)")] + pub approval_code_1: ApprovalCode, + pub approval_code_2_raw: NumericString, // Champ 18 : Code agrément 2 (1 CN) - Non utilisé pour le moment + pub approval_code_3_raw: NumericString, // Champ 19 : Code agrément 3 (1 CN) - Non utilisé pour le moment + pub invoice_signature_permission: NumericString, // Champ 20 : Habilitation à signer une facture (1 CN) + pub lot_signature_permission: NumericString, // Champ 21 : Habilitation à signer un lot (1 CN) + } + + // Fields + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + #[deku(ctx = "id: u8", id = "id")] + /// Mode d'exercice + pub enum PracticeMode { + #[deku(id = 0)] + Liberal, // Libéral, exploitant, commerçant + #[deku(id = 1)] + Salarie, + #[deku(id = 4)] + Remplacant, + #[deku(id = 7)] + Benevole, + } + // + // #[deku_derive(DekuRead)] + // #[derive(Debug, PartialEq)] + // #[deku(ctx = "id: u8", id = "id")] + // /// Statut d'exercice + // pub enum PracticeStatus { + // // Cf. TAB-Statuts géré par l’ANS + // } + + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + #[deku(ctx = "id: u8", id = "id")] + /// Secteur d'activité + pub enum ActivitySector { + #[deku(id = 10)] + EtablissementPublicDeSante, // Etablissement Public de santé + #[deku(id = 11)] + HopitauxMilitaires, // Hôpitaux Militaires + #[deku(id = 16)] + EtablissementPrivePSPH, // Etablissement Privé PSPH + #[deku(id = 17)] + EtablissementPriveNonPSPH, // Etablissement Privé Non PSPH + #[deku(id = 25)] + DispensaireDeSoins, // Dispensaire de soins + #[deku(id = 26)] + AutresStructuresDeSoinsArmee, // Autres structures de soins relevant du Service de santé des armées + #[deku(id = 31)] + CabinetIndividuel, // Cabinet individuel + #[deku(id = 32)] + CabinetDeGroupe, // Cabinet de Groupe + #[deku(id = 33)] + ExerciceEnSociete, // Exercice en Société + #[deku(id = 34)] + SecteurPrivePHTempsPlein, // Secteur privé PH temps plein + #[deku(id = 35)] + TransportSanitaire, // Transport sanitaire + #[deku(id = 37)] + EntrepriseDInterim, // Entreprise d'intérim + #[deku(id = 41)] + EtablissementDeSoinsEtPrevention, // Etablissement de Soins et Prévention + #[deku(id = 42)] + PreventionEtSoinsEnEntreprise, // Prévention. Et Soins en Entreprise + #[deku(id = 43)] + SanteScolaireEtUniversitaire, // Santé scolaire & universitaire + #[deku(id = 44)] + RecrutementEtGestionRH, // Recrutement & gestion RH + #[deku(id = 45)] + PMIPlanificationFamiliale, // P.M.I. Planification familiale + #[deku(id = 51)] + EtablissementPourHandicapes, // Etablissement pour Handicapés + #[deku(id = 52)] + ComMarketingConsultingMedia, // Com/Marketing/Consulting/Media + #[deku(id = 53)] + EtablissementPersonnesAgees, // Etablissement Personnes Agées + #[deku(id = 54)] + EtablissementAideALaFamille, // Etablissement Aide à la famille + #[deku(id = 55)] + EtablissementDEnseignement, // Etablissement d'enseignement + #[deku(id = 56)] + EtablissementsDeProtectionDeLEnfance, // Etablissements de protection de l'enfance + #[deku(id = 57)] + EtablissementsDHebergementEtDeReadaptation, // Etablissements d'hébergement et de réadaptation + #[deku(id = 58)] + Recherche, // Recherche + #[deku(id = 61)] + AssurancePrivee, // Assurance Privée + #[deku(id = 62)] + OrganismeDeSecuriteSociale, // Organisme de Sécurité Sociale + #[deku(id = 65)] + MinistereEtServicesDeconcentres, // Ministère & Serv. Déconcentrés + #[deku(id = 66)] + CollectivitesTerritoriales, // Collectivités Territoriales + #[deku(id = 68)] + AssoEtOrgaHumanitaire, // Asso et orga humanitaire + #[deku(id = 71)] + LABM, // LABM + #[deku(id = 75)] + AutreEtablissementSanitaire, // Autre établissement Sanitaire + #[deku(id = 81)] + ProdEtComGrosBienMed, // Prod. & Com. Gros Bien Med. + #[deku(id = 85)] + CommDetailDeBiensMedicaux, // Comm. Détail de biens médicaux + #[deku(id = 86)] + PharmacieDOfficine, // Pharmacie d'officine + #[deku(id = 87)] + CentreDeDialyse, // Centre de dialyse + #[deku(id = 88)] + ParaPharmacie, // Para-pharmacie + #[deku(id = 91)] + AutreSecteurDActivite, // Autre secteur d'activité + #[deku(id = 92)] + SecteurNonDefini, // Secteur non défini + #[deku(id = 93)] + CentreAntiCancer, // Centre anti-cancer + #[deku(id = 94)] + CentreDeTransfusionSanguine, // Centre de transfusion sanguine + #[deku(id = 95)] + ChaineDuMedicament, // Répart. Distrib. Fab. Exploit. Import Médicaments + #[deku(id = 96)] + IncendiesEtSecours, // Incendies et secours + #[deku(id = 97)] + EntreprisesIndustriellesNonPharma, // Entreprises industrielles et tertiaires hors industries pharmaceutiques + #[deku(id = 98)] + EntiteDUnTOM, // Entité d'un TOM + #[deku(id = 99)] + ChaineDuDispositifMedical, // Fab. Exploit. Import. Médicaments et Dispositifs Médicaux + } + + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + #[deku(ctx = "id: u8", id = "id")] + /// Type d'identification structure + pub enum StructureIDType { + #[deku(id = 0)] + ADELICabinet, // Id Cabinet ADELI + #[deku(id = 1)] + FINESS, // N° FINESS + #[deku(id = 2)] + SIREN, // N° SIREN + #[deku(id = 3)] + SIRET, // N° SIRET + #[deku(id = 4)] + RPPSCabinet, // Id Cabinet RPPS + } + + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + #[deku(ctx = "id: u8", id = "id")] + /// Code conventionnel + /// Dictionnaire des données FSV, p37 + pub enum ConventionCode { + #[deku(id = 0)] + NonConventionne, + #[deku(id = 1)] + Conventionne, + #[deku(id = 2)] + ConventionneAvecDepassement, + #[deku(id = 3)] + ConventionneAvecHonorairesLibres, + } + + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + #[deku(ctx = "id: u8", id = "id")] + /// Code spécialité + /// Dictionnaire des données FSV, p43 + pub enum SpecialtyCode { + #[deku(id = 1)] + MedecineGenerale, // Médecine générale + #[deku(id = 2)] + AnesthesieReanimation, // Anesthésie-Réanimation + #[deku(id = 3)] + Cardiologie, // Cardiologie + #[deku(id = 4)] + ChirurgieGenerale, // Chirurgie Générale + #[deku(id = 5)] + DermatoVenerologie, // Dermatologie et Vénérologie + #[deku(id = 6)] + Radiologie, // Radiologie + #[deku(id = 7)] + GynecologieObstetrique, // Gynécologie obstétrique + #[deku(id = 8)] + GastroEnterologieHepatologie, // Gastro-Entérologie et Hépatologie + #[deku(id = 9)] + MedecineInterne, // Médecine interne + #[deku(id = 10)] + NeuroChirurgie, // Neuro-Chirurgie + #[deku(id = 11)] + OtoRhinoLaryngologie, // Oto-Rhino-Laryngologie + #[deku(id = 12)] + Pediatrie, // Pédiatrie + #[deku(id = 13)] + Pneumologie, // Pneumologie + #[deku(id = 14)] + Rhumatologie, // Rhumatologie + #[deku(id = 15)] + Ophtalmologie, // Ophtalmologie + #[deku(id = 16)] + ChirurgieUrologique, // Chirurgie urologique + #[deku(id = 17)] + NeuroPsychiatrie, // Neuro-Psychiatrie + #[deku(id = 18)] + Stomatologie, // Stomatologie + #[deku(id = 19)] + ChirurgienDentiste, // Chirurgien dentiste + #[deku(id = 20)] + ReanimationMedicale, // Réanimation médicale + #[deku(id = 21)] + SageFemme, // Sage-femme + #[deku(id = 22)] + SpécialisteEnMGDiplome, // Spécialiste en médecine générale avec diplôme + #[deku(id = 23)] + SpécialisteEnMGReconnu, // Spécialiste en médecine générale reconnu par l’Ordre + #[deku(id = 24)] + Infirmier, // Infirmier + #[deku(id = 25)] + Psychologue, // Psychologue + #[deku(id = 26)] + MasseurKinesitherapeute, // Masseur Kinésithérapeute + #[deku(id = 27)] + PedicurePodologue, // Pédicure Podologue + #[deku(id = 28)] + Orthophoniste, // Orthophoniste + #[deku(id = 29)] + Orthoptiste, // Orthoptiste + #[deku(id = 30)] + LaboAnalysesMedicales, // Laboratoire d'analyses médicales + #[deku(id = 31)] + ReeducationReadaptationFonctionnelle, // Rééducation Réadaptation fonctionnelle + #[deku(id = 32)] + Neurologie, // Neurologie + #[deku(id = 33)] + Psychiatrie, // Psychiatrie + #[deku(id = 34)] + Geriatrie, // Gériatrie + #[deku(id = 35)] + Nephrologie, // Néphrologie + #[deku(id = 36)] + ChirurgieDentaireSpecialiteODF, // Chirurgie Dentaire spécialité O.D.F + #[deku(id = 37)] + AnatomoCytoPathologie, // Anatomo-Cyto-Pathologie + #[deku(id = 38)] + MedecinBiologiste, // Médecin biologiste + #[deku(id = 39)] + LaboPolyvalent, // Laboratoire polyvalent + #[deku(id = 40)] + LaboAnatomoCytoPathologique, // Laboratoire d’anatomo-cyto-pathologique + #[deku(id = 41)] + ChirurgieOrthopediqueTraumatologie, // Chirurgie Orthopédique et Traumatologie + #[deku(id = 42)] + EndocrinologieMetabolisme, // Endocrinologie et Métabolisme + #[deku(id = 43)] + ChirurgieInfantile, // Chirurgie infantile + #[deku(id = 44)] + ChirurgieMaxilloFaciale, // Chirurgie maxillo-faciale + #[deku(id = 45)] + ChirurgieMaxilloFacialeStomatologie, // Chirurgie maxillo-faciale et stomatologie + #[deku(id = 46)] + ChirurgiePlastiqueReconstructriceEsthetique, // Chirurgie plastique reconstructrice et esthétique + #[deku(id = 47)] + ChirurgieThoraciqueCardioVasculaire, // Chirurgie thoracique et cardio-vasculaire + #[deku(id = 48)] + ChirurgieVasculaire, // Chirurgie vasculaire + #[deku(id = 49)] + ChirurgieVisceraleDigestive, // Chirurgie viscérale et digestive + #[deku(id = 50)] + PharmacieDOfficine, // Pharmacie d’officine + #[deku(id = 51)] + PharmacieMutualiste, // Pharmacie Mutualiste + #[deku(id = 53)] + ChirurgienDentisteSpecialiteCO, // Chirurgien dentiste spécialité C.O. + #[deku(id = 54)] + ChirurgienDentisteSpecialiteMBD, // Chirurgien dentiste spécialité M.B.D. + #[deku(id = 60)] + PrestataireDeTypeSociete, // Prestataire de type société + #[deku(id = 61)] + PrestataireArtisan, // Prestataire artisan + #[deku(id = 62)] + PrestataireDeTypeAssociation, // Prestataire de type association + #[deku(id = 63)] + Orthesiste, // Orthésiste + #[deku(id = 64)] + Opticien, // Opticien + #[deku(id = 65)] + Audioprothesiste, // Audioprothésiste + #[deku(id = 66)] + EpithesisteOculariste, // Épithésiste Oculariste + #[deku(id = 67)] + PodoOrthesiste, // Podo-orthésiste + #[deku(id = 68)] + Orthoprothesiste, // Orthoprothésiste + #[deku(id = 69)] + ChirurgieOrale, // Chirurgie orale + #[deku(id = 70)] + GynecologieMedicale, // Gynécologie médicale + #[deku(id = 71)] + Hematologie, // Hématologie + #[deku(id = 72)] + MedecineNucleaire, // Médecine nucléaire + #[deku(id = 73)] + OncologieMedicale, // Oncologie médicale + #[deku(id = 74)] + OncologieRadiotherapique, // Oncologie radiothérapique + #[deku(id = 75)] + PsychiatrieEnfantAdolescent, // Psychiatrie de l’enfant et de l’adolescent + #[deku(id = 76)] + Radiotherapie, // Radiothérapie + #[deku(id = 77)] + Obstetrique, // Obstétrique + #[deku(id = 78)] + GenetiqueMedicale, // Génétique médicale + #[deku(id = 79)] + ObstetriqueGynecologieMedicale, // Obstétrique et Gynécologie médicale + #[deku(id = 80)] + SantePubliqueMedecineSociale, // Santé publique et médecine sociale + #[deku(id = 81)] + MaladiesInfectieusesTropicales, // Médecine des Maladies infectieuses et tropicales + #[deku(id = 82)] + MedecineLegaleExpertisesMedicales, // Médecine légale et expertises médicales + #[deku(id = 83)] + MedecineDurgence, // Médecine d’urgence + #[deku(id = 84)] + MedecineVasculaire, // Médecine vasculaire + #[deku(id = 85)] + Allergologie, // Allergologie + #[deku(id = 86)] + IPA, // Infirmier exerçant en Pratiques Avancées (IPA) + } + + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + #[deku(ctx = "id: u8", id = "id")] + /// Code zone IK - Indemnité kilométrique + /// Dictionnaire des données FSV, p44 + pub enum IkZoneCode { + #[deku(id = 0)] + PasDIndemniteKilometrique, // Pas d'indemnité kilométrique + #[deku(id = 1)] + IndemniteKilometriquePlaine, // Indemnité kilométrique plaine + #[deku(id = 2)] + IndemniteKilometriqueMontagne, // Indemnité kilométrique montagne + } + + #[deku_derive(DekuRead)] + #[derive(Debug, PartialEq)] + #[deku(ctx = "id: u8", id = "id")] + /// Code agrément + /// Dictionnaire des données FSV, p44 + pub enum ApprovalCode { + #[deku(id = 0)] + PasAgrementRadio, // Pas d'agrément radio + #[deku(id = 1)] + AgrementDOuDDASS, // Agrément D ou DDASS + #[deku(id = 2)] + AgrementABCEF, // Agrément A, B, C, E, F + #[deku(id = 3)] + AgrementGHJ, // Agrément G, H, J + #[deku(id = 4)] + AgrementK, // Agrément K + #[deku(id = 5)] + AgrementL, // Agrément L + #[deku(id = 6)] + AgrementM, // Agrément M + } +} + #[cfg(test)] mod tests { use deku::DekuContainerRead as _; use group_1_holder::{CardPSType, CategoryCard, CivilityCode, NationalIDType}; + use group_2_situation::{ActivitySector, ApprovalCode, ConventionCode, IkZoneCode, PracticeMode, SpecialtyCode, StructureIDType}; use crate::fsv_parsing::blocks::BlockHeader; @@ -223,7 +644,7 @@ mod tests { 1, // Type d'identification structure, 1 CN 49, 9, // N° d'identification structure, 14 CA - 48, 66, 48, 50, 52, 54, 50, 56, 54, + 48, 66, 48, 50, 52, 54, 50, 56, 54, // 0B0246286 1, // Clé du N° d'identification structure, 1 CN 54, 34, // Raison sociale structure, 40 CE @@ -232,7 +653,7 @@ mod tests { 32, 82, 79, 85, 84, 73, 69, 82, 69, 50, 52, 54, 50, 56, 8, // N° d'identification de facturation du PS, 8 CN - 48, 48, 50, 48, 57, 51, 54, 56, + 48, 48, 50, 48, 57, 51, 54, 56, // 00209368 1, // Clé du N° d'identification de facturation du PS, 1 CN 48, 0, // N° d'identification du PS remplaçant, 30 CA @@ -255,7 +676,7 @@ mod tests { 1, // Habilitation à signer une Facture // 1 CN 49, 1, // Habilitation à signer un lot // 1 CN - 49 + 49, ]; } @@ -269,7 +690,7 @@ mod tests { #[test] fn test_group_1_holder() { - env_logger::init(); // Uncomment and run with RUST_LOG=trace for deku debugging + // env_logger::init(); // Uncomment and run with RUST_LOG=trace for deku debugging let offset = 3*8; let (_rest, holder) = group_1_holder::Holder::from_bytes((data::BUFFER, offset)).unwrap(); assert_eq!(holder.card_type, CardPSType::CPS, "Card type"); @@ -281,4 +702,32 @@ mod tests { assert_eq!(holder.holder_firstname.0, "GILBERT", "Holder Firstname"); assert_eq!(holder.category_card, CategoryCard::Unknown, "Category card"); } + + #[test] + fn test_group_2_situation() { + // env_logger::init(); // Uncomment and run with RUST_LOG=trace for deku debugging + let offset = 3*8 + 53*8 + 3*8; + let (_rest, situation) = group_2_situation::Situation::from_bytes((data::BUFFER, offset)).unwrap(); + assert_eq!(situation.id.0, 1, "ID"); + assert_eq!(situation.practice_mode, PracticeMode::Liberal, "Practice mode"); + assert_eq!(situation.practice_status_raw.0, "1", "Practice status raw"); + assert_eq!(situation.activity_sector, ActivitySector::PharmacieDOfficine, "Activity sector"); + assert_eq!(situation.structure_id_type, StructureIDType::FINESS, "Structure ID type"); + assert_eq!(situation.structure_id.0, "0B0246286", "Structure ID"); + assert_eq!(situation.structure_id_key.0, "6", "Structure ID key"); + assert_eq!(situation.structure_name.0, "PHARMACIE DE LA GARE ROUTIERE24628", "Structure name"); + assert_eq!(situation.ps_billing_number.0, "00209368", "PS billing number"); + assert_eq!(situation.ps_billing_number_key.0, "0", "PS billing number key"); + assert_eq!(situation.ps_replacement_number.0, "", "PS replacement number"); + assert_eq!(situation.ps_replacement_number_key.0, "0", "PS replacement number key"); + assert_eq!(situation.convention_code, ConventionCode::Conventionne, "Convention code"); + assert_eq!(situation.specialty_code, SpecialtyCode::PharmacieDOfficine, "Specialty code"); + assert_eq!(situation.rate_zone_code_raw.0, "10", "Rate zone code raw"); + assert_eq!(situation.ik_zone_code, IkZoneCode::PasDIndemniteKilometrique, "IK zone code"); + assert_eq!(situation.approval_code_1, ApprovalCode::PasAgrementRadio, "Approval code 1"); + assert_eq!(situation.approval_code_2_raw.0, "0", "Approval code 2 raw"); + assert_eq!(situation.approval_code_3_raw.0, "0", "Approval code 3 raw"); + assert_eq!(situation.invoice_signature_permission.0, "1", "Invoice signature permission"); + assert_eq!(situation.lot_signature_permission.0, "1", "Lot signature permission"); + } } \ No newline at end of file diff --git a/crates/fsv/src/ssv/mod.rs b/crates/fsv/src/ssv/mod.rs index c087647..a8f4385 100644 --- a/crates/fsv/src/ssv/mod.rs +++ b/crates/fsv/src/ssv/mod.rs @@ -158,7 +158,7 @@ mod tests { use utils::config::load_config; use anyhow::{bail, Result}; - use crate::fsv_parsing::blocks::DataGroup; + use crate::fsv_parsing::{blocks::DataGroup, groups::ssv_lire_carte_ps::{group_1_holder::CardPSType, group_2_situation::{PracticeMode, SpecialtyCode}}}; use super::*; @@ -184,11 +184,32 @@ mod tests { } #[test] - #[ignore="WARNING: Read the card with PIN 1234 - Risk of blocking the card"] + #[ignore=" + WARNING: Read the card with PIN 1234 - Risk of blocking the card + WARNING: This test will only work with GILBERT's PHARMOFFICE card (titulaire kit pharmacie) + "] fn test_read_professional_card_good_pin() -> Result<()> { let lib = setup::init()?; let pin_code = "1234"; - lib.read_professional_card(pin_code)?; + let cps_blocks = lib.read_professional_card(pin_code)?; + // Check the first group is the holder group + let holder_group = cps_blocks.blocks.first().unwrap(); + assert_eq!(holder_group.header.group_id.0, 1); + let holder_content = match &holder_group.content { + DataGroup::LireCartePS_Group1_Holder(content) => { content }, + _ => bail!("Wrong group type"), + }; + assert_eq!(holder_content.card_type, CardPSType::CPS, "Card type"); + assert_eq!(holder_content.holder_firstname.0, "GILBERT", "Holder firstname"); + // Check the second group is a situation group + let situation_group = cps_blocks.blocks.get(1).unwrap(); + assert_eq!(situation_group.header.group_id.0, 2); + let situation_content = match &situation_group.content { + DataGroup::LireCartePS_Group2_Situation(content) => { content }, + _ => bail!("Wrong group type"), + }; + assert_eq!(situation_content.practice_mode, PracticeMode::Liberal, "Practice mode"); + assert_eq!(situation_content.specialty_code, SpecialtyCode::PharmacieDOfficine, "Specialty code"); Ok(()) }