feat: add DMED comparador library with TXT and XLS processing
- Implemented TXT processor for DMED files, extracting RPPSS records. - Added XLS processor for Prosoft reports, supporting both XLS and XLSX formats. - Created a comparison tool to identify discrepancies between TXT and XLS data. - Developed a GUI using eframe for user-friendly interaction. - Included detailed README documentation for setup and usage instructions. - Added error handling for file processing and improved data extraction methods. - Implemented tests for data processing functions and utilities. - Introduced a build script for Windows resource management. - Added example scripts for basic and complete comparisons. - Included a report generation feature for discrepancies found during comparisons.
This commit is contained in:
Generated
+4472
-1
File diff suppressed because it is too large
Load Diff
+15
@@ -7,6 +7,21 @@ edition = "2021"
|
|||||||
name = "dmed"
|
name = "dmed"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "gui"
|
||||||
|
path = "src/bin/gui.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
calamine = "0.26"
|
||||||
|
encoding_rs = "0.8"
|
||||||
|
eframe = "0.29"
|
||||||
|
egui = "0.29"
|
||||||
|
rfd = "0.15"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
winres = "0.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
chrono = "0.4"
|
||||||
|
|||||||
@@ -1,341 +0,0 @@
|
|||||||
Dmed|2026|2025|N|||
|
|
||||||
RESPO|21681939835|Anelisa Pirola Chalegre|17|35316200|205||anelisa@edegarpirolacontabilidade.com.br|
|
|
||||||
DECPJ|03364887000195|CLINICA DE CIRURGIA PLASTICA DR VIDAL LTDA|1|||01877281867|N|||
|
|
||||||
PSS|
|
|
||||||
RPPSS|00276808860|MARCO ANTONIO MACHADO|2200000|
|
|
||||||
RPPSS|00633999032|ELISANDRA BARBOSA TORRES|600000|
|
|
||||||
RPPSS|01133252826|JULIA AGUIAR MARTINEZ JACOB|130000|
|
|
||||||
RPPSS|01201476887|ANNIBAL ANTONIO BIANCHINI|50000|
|
|
||||||
RPPSS|01313055816|JUSSARA TAVARES PEREIRA|90000|
|
|
||||||
RPPSS|01595808620|GABRIEL SOUZA VASCONCELOS|30000|
|
|
||||||
RPPSS|01611859409|Maria Raquel dos Santos Silva|150000|
|
|
||||||
RPPSS|01869161831|SOZIGENES EDUARDO BARALDI|110000|
|
|
||||||
RPPSS|01876897848|VALTER ESTEVES ASCENCIO|100000|
|
|
||||||
RPPSS|01887885811|APARECIDA VERA BALDAO FACHINI|50000|
|
|
||||||
RPPSS|01888554835|MARIA HELENA DE PAIVA VIEIRA|1416000|
|
|
||||||
RPPSS|02013748892|ANA MARIA SCARDELATO SAURIN|4532000|
|
|
||||||
RPPSS|02039876839|MARIUSA DE FATIMA ALVES VERMELHO|120000|
|
|
||||||
RPPSS|02229875108|RODRINY FERREIRA PORFIRIO|50000|
|
|
||||||
RPPSS|02347481843|MARLENE MOYSES|2000000|
|
|
||||||
RPPSS|02656473802|EUNICE APARECIDA L MORCELLE|3632000|
|
|
||||||
RPPSS|02926769806|MARIA JOSE BORDUQUE|4390000|
|
|
||||||
RPPSS|03078759547|LUCIANA SILVA CARDOSO|1300000|
|
|
||||||
RPPSS|03109032899|MARIA CRISTINA FROELICH FELIPPE|40000|
|
|
||||||
RPPSS|03222414807|CRISTINA DE F�TIMA LADEIA|360000|
|
|
||||||
RPPSS|03298225517|Joana Edisia Barbosa de�Ara�jo|20000|
|
|
||||||
RPPSS|03429318815|ALIPIO MARIA|200000|
|
|
||||||
RPPSS|04408590851|JUSSARA MANCHINI|1140000|
|
|
||||||
RPPSS|04583077858|MARCOS ARLINDO PEDRAZZOLI|400000|
|
|
||||||
RPPSS|04718485819|HELOISA HELENA DE OLIVEIRA|4974000|
|
|
||||||
RPPSS|04974084810|MARIA SILVA SALES BATISTA|1553000|
|
|
||||||
RPPSS|05124141816|ELIAN ANTONIO FIORIN|2270000|
|
|
||||||
RPPSS|05406352865|RITA ELIANA SILV�RIO GARCIA|50000|
|
|
||||||
RPPSS|05511018813|LEILA SILVIA BRAGA|3666000|
|
|
||||||
RPPSS|06058898803|SONIA ISABEL TOFONELLI SANFLORIAN|130000|
|
|
||||||
RPPSS|06102333861|CLEUSA APARECIDA BARQUILLA DA CRUZ|3200000|
|
|
||||||
RPPSS|06230090805|Rubens Piva Junior|50000|
|
|
||||||
RPPSS|06232611896|REGINA ELIZABETH CLEMENTE DE MORAES|1453000|
|
|
||||||
RPPSS|06381245816|Ant�nio Cesar Cappellanes|80000|
|
|
||||||
RPPSS|06441062631|ALINE FERNANDES|2350000|
|
|
||||||
RPPSS|06512306830|MARCIO ARAUJO|3000000|
|
|
||||||
RPPSS|06647641858|Luzia dos Santos Scandelai|1163000|
|
|
||||||
RPPSS|07044365825|ELISABETE RIBEIRO|550000|
|
|
||||||
RPPSS|07049345822|JEANE KELLI RAMOS ALVARENGA CAMPANHOLO|820000|
|
|
||||||
RPPSS|07063335863|ELIANE PELIZZON|400000|
|
|
||||||
RPPSS|07063375814||1600000|
|
|
||||||
RPPSS|07458218800|ANA CLAUDIA CAMARGO BENATTI|30000|
|
|
||||||
RPPSS|07549485879|LUCIA HELENA KATER NOBALBOS|40000|
|
|
||||||
RPPSS|08070565888|JOSELINA DA COSTA SIMON|1400000|
|
|
||||||
RPPSS|08294891851|DALVA APARECIDA LODI JULIANO|40000|
|
|
||||||
RPPSS|08458045885|SILVIA HELENA GONSALVES DE OLIVEIRA|210000|
|
|
||||||
RPPSS|08477994846|ANA LUCIA DORIGAN PEDRAZZOLI|5141000|
|
|
||||||
RPPSS|08511120807|LILIANI APARECIDA CASEMIRO|4000000|
|
|
||||||
RPPSS|08662369880|ANGELA BERNADETTE SARANZA AYUSSO|120000|
|
|
||||||
RPPSS|09073721202|ANDRE LUIS PERRI VALENTIM FILHO|90000|
|
|
||||||
RPPSS|09249519826|VANETE APARECIDA NUNES VIEIRA|310000|
|
|
||||||
RPPSS|09405406817|LUIZ PAULO SILVA|50000|
|
|
||||||
RPPSS|09815468898|LUCIENE THOMAZ DA SILVA|50000|
|
|
||||||
RPPSS|09819839874|Mara Elisa Dias Martin|1760000|
|
|
||||||
RPPSS|09830857816|EDSON ANDRELLA|110000|
|
|
||||||
RPPSS|09831539818|MIRIAM LEILA ISA BOTURA|50000|
|
|
||||||
RPPSS|09835947830|RICARDO PINFILDI|40000|
|
|
||||||
RPPSS|10031204821|SILVIA HELENA ZACARIAS BETANHO|4000000|
|
|
||||||
RPPSS|10171610890|MARCIA CRISTINA FALSONI SIMAO|260000|
|
|
||||||
RPPSS|10178140821|SONIA APARECIDA GALASSI CHIARI 213611|3660000|
|
|
||||||
RPPSS|10295110805|MARIA DE FATIMA SOARES GAIARIM|1303000|
|
|
||||||
RPPSS|10324217897|IRANI TEODORO GODOI|3200000|
|
|
||||||
RPPSS|10942717805|SILMARA ALEXANDRA TARSITANO RENZETTI|170000|
|
|
||||||
RPPSS|10946768889|ROSEMEIRE FERRASSA|540000|
|
|
||||||
RPPSS|11287629865|SIDNEI GIAMPANI DI BACCHI|3723000|
|
|
||||||
RPPSS|11395324808|FABIANA FONSECA PALETA|30000|
|
|
||||||
RPPSS|11408372819|MARIVONE BOTOS DE CARVALHO|3682000|
|
|
||||||
RPPSS|12150927845|GLAUCIA REGINA DOTTO ESTEVES PAES|270000|
|
|
||||||
RPPSS|12158433809|ADEVAIR EDSON RASCAZZI|1500000|
|
|
||||||
RPPSS|12158599880|ROBERTA PAZIANI ESCOBOSA 004280|50000|
|
|
||||||
RPPSS|12160043826|LUCIANA CARVALHO T BIASI|150000|
|
|
||||||
RPPSS|12177777844|ALESSANDRA RENATA FERREIRA|3300000|
|
|
||||||
RPPSS|12767645881|DEBORA CRISTINA LOPES RIBEIRO|3200000|
|
|
||||||
RPPSS|12830717899|Adilson dos Santos Marques|50000|
|
|
||||||
RPPSS|13222453802|ELIANE CASSIA FALC�O|2866000|
|
|
||||||
RPPSS|13451628805|Maria Aparecida Valheri Lobato|40000|
|
|
||||||
RPPSS|13593785889|GISSELE DE CASTRO SILVA LEAL|000|
|
|
||||||
RPPSS|13779233800|DENISE BUSNARDO|50000|
|
|
||||||
RPPSS|14101127808|MARCIA REGINA NOBUKUNI FRANCISCO|3150000|
|
|
||||||
RPPSS|14112719809|CLEIDE DE LOURDES REGHIN HAINST|2700000|
|
|
||||||
RPPSS|14874805809|Angela Maria Vicentini Maria|260000|
|
|
||||||
RPPSS|14948084808|Renata Cristina braz crispim Marquini|40000|
|
|
||||||
RPPSS|15066683801|SILVIA DE LOURDES CASTRO DORIGAN|450000|
|
|
||||||
RPPSS|15076010850|Cristiane Roberta da Silva Santos|40000|
|
|
||||||
RPPSS|15180363845|LUCILENE DE BRITO NUNES|5500000|
|
|
||||||
RPPSS|15327351890|KETI APARECIDA SOARES BIASSI|100000|
|
|
||||||
RPPSS|15845895820|EDUARDO GEORGETTE FERNANDES|1000000|
|
|
||||||
RPPSS|15925751884|DANIELA BOBADILHA CASSEVERINI|3600000|
|
|
||||||
RPPSS|15926701805|SANDRA TEREZINHA ARTUSO AZEVEDO|130000|
|
|
||||||
RPPSS|15934524895|DANIELA CARVALHO SILVA LOTO|110000|
|
|
||||||
RPPSS|15977778899|CRISTIANA PIRASSOLI|3240000|
|
|
||||||
RPPSS|16397326837|REGINA APARECIDA MUSSATO VERZA|3920000|
|
|
||||||
RPPSS|16504025894|MILENA VOLPI FIGUEIREDO|150000|
|
|
||||||
RPPSS|16899212854|ADRIANA CRISTINA DE CARVALHO|1500000|
|
|
||||||
RPPSS|17212484806|MARIA JOSE FIDELIS BRITO|2547000|
|
|
||||||
RPPSS|18139917826|CLEONICE IZILDINHA SPERANDIO RUGERI|40000|
|
|
||||||
RPPSS|18451315844|SIMONE MANFRIN CALMON RIBEIRO|1663000|
|
|
||||||
RPPSS|18454014845|PATRICIA DE SOUZA LIMA FERNANDES|1550000|
|
|
||||||
RPPSS|18638480881|ROSIMAR CRISTINA POLI POLASTRI|4400000|
|
|
||||||
RPPSS|18694699842|ELAINE CAVALCANTE DA COSTA|3300000|
|
|
||||||
RPPSS|19091615869|ROSA MARIA GULGIERMETTI SANDRIN|40000|
|
|
||||||
RPPSS|19091825839|SILMARA TEREZINHA FINANCI|40000|
|
|
||||||
RPPSS|19644203852|DANIELA APARECIDA COSTA RODEIRO|80000|
|
|
||||||
RPPSS|20263257827|MARCELO CESAR RANGEL 000001|1250000|
|
|
||||||
RPPSS|21330186893|MARCO AURELIO VOLPI FIGUEIREDO|3300000|
|
|
||||||
RPPSS|21426111843|MARIA AMELIA SERRA NEGRA ROTELLA|2430000|
|
|
||||||
RPPSS|21441691871|ANA PAULA PIMENTEL|2230000|
|
|
||||||
RPPSS|21474314864|MARINA APARECIDA SOLER|3100000|
|
|
||||||
RPPSS|21478807873|TATIANA CURY CAVALHEIRO|40000|
|
|
||||||
RPPSS|21562118846|GIANCA SILVA MONTELEONE|40000|
|
|
||||||
RPPSS|21676005854|VANESA CRISTINA BUSNARDO GARILO|50000|
|
|
||||||
RPPSS|21898064881|GIOVANNA NOVAES CESTARI|50000|
|
|
||||||
RPPSS|21957555807|Danieli Cristina Andrade|40000|
|
|
||||||
RPPSS|22012218806|KAREN SILVIA BELTRAME|1750000|
|
|
||||||
RPPSS|22034414888|Fl�via Costa Brand�o Piva|50000|
|
|
||||||
RPPSS|22042714895|LILIANE AMARAL DE ALMEIDA|3300000|
|
|
||||||
RPPSS|22084536898|MARCELA ALEXANDRINO GENTIL|40000|
|
|
||||||
RPPSS|22135997854|FABIO EVANDRO RIVA|4000000|
|
|
||||||
RPPSS|22209848857|CRISTIANE PONSON BARONI LOPES|3150000|
|
|
||||||
RPPSS|22216654817|VIVIANE ROGANTE|3300000|
|
|
||||||
RPPSS|22217846850|FERNANDA ELISA AGUIRRE RESSUDE COR�|2200000|
|
|
||||||
RPPSS|22249145806|GISELE BITTO OLIVEIRA|2557000|
|
|
||||||
RPPSS|22314447808|CAROLINE APARECIDA WARAYA�ARIKI|3240000|
|
|
||||||
RPPSS|22319070893|QUELI ELAINE MATIAS BONFIM|1350000|
|
|
||||||
RPPSS|22481920880|SANDRA REGINA FEMENIA BUZON|50000|
|
|
||||||
RPPSS|22490928856|VANESSA EIKO IMAMURA|1295000|
|
|
||||||
RPPSS|22555844848|ALICIA DARMA HERRERA RAMIREZ|50000|
|
|
||||||
RPPSS|22561482898|SAMIRA GARCIA DE OLIVEIRA DA SILVEIRA|5100000|
|
|
||||||
RPPSS|22679192818|TATIANA RUIZ BRANCALIAO|40000|
|
|
||||||
RPPSS|22758980851|ALINE SPINDOLA PENARIOL|3920000|
|
|
||||||
RPPSS|22811518835|Patr�cia de Oliveira Santos|3250000|
|
|
||||||
RPPSS|22894375816|ANDREIA BONIFACIO DE SALLES|1400000|
|
|
||||||
RPPSS|22941788824|Beatriz Gerlak|40000|
|
|
||||||
RPPSS|23151028802|ADRIANA DA SILVA BASTOS|40000|
|
|
||||||
RPPSS|24579987861|MARIA DE LOURDES SIGULI CHAVES|130000|
|
|
||||||
RPPSS|24712740809|REGINALDO ROSSI|40000|
|
|
||||||
RPPSS|25163063855|LEANDRA PATRICIA MACHADO|1214000|
|
|
||||||
RPPSS|25233784858|KATIA CILENE ANATIR CAROLINO|2550000|
|
|
||||||
RPPSS|25236536804|MARIA DE LOURDES TOFFANELLI BETIOLI|50000|
|
|
||||||
RPPSS|25561794817|ISA PAULA POLETTI ROMANIN|40000|
|
|
||||||
RPPSS|25954649812|FABIANA CAMACHO GONCALVES RODRIGUES|700000|
|
|
||||||
RPPSS|25983884883|SIMONE FLORENTINO PEREZ|3300000|
|
|
||||||
RPPSS|26229377845|SANDRA MARA FELICIANO|2550000|
|
|
||||||
RPPSS|26714125844|VILMA MARIA VIEIRA|450000|
|
|
||||||
RPPSS|27579848880|ISAURA DE SOUZA MELO RIBEIRO|1600000|
|
|
||||||
RPPSS|27828260857|RODRIGO ARCURIO|1300000|
|
|
||||||
RPPSS|27852015824|MICHELLE CRISTIANE DOS SANTOS|250000|
|
|
||||||
RPPSS|27923902801|ARIANA CANDIDA GONCALVES RODRIGUES|40000|
|
|
||||||
RPPSS|28159991886|PAULA RODRIGUES FARIA|3120000|
|
|
||||||
RPPSS|28452896859|TAISA SOARES DA SILVA SPADA|540000|
|
|
||||||
RPPSS|28498870801|RITA DE LIMA CAPPELLANES|4813000|
|
|
||||||
RPPSS|28523071890|ELISA VALOSO|2350000|
|
|
||||||
RPPSS|28561437847|FABIANA PRIOLI DA SILVA|2800000|
|
|
||||||
RPPSS|28591223896|EDUARDO COGO ABIB|779000|
|
|
||||||
RPPSS|28695113884|JOSE CARLOS SILVEIRA JUNIOR|50000|
|
|
||||||
RPPSS|28793303866|SABRINA SPADA|3117000|
|
|
||||||
RPPSS|29137400819|EDMILSON APARECIDO BARTOLI|110000|
|
|
||||||
RPPSS|29397820168|ROMIR ALVES LEAL|300000|
|
|
||||||
RPPSS|29491344838|GRASIELA VENTURIN|40000|
|
|
||||||
RPPSS|29703693890|FERNANDA VALERIO|40000|
|
|
||||||
RPPSS|29714190886|MCHELLE LAZARO DA SILVA VIEIRA|3600000|
|
|
||||||
RPPSS|29805238873|D�bora Sumie Iwata|50000|
|
|
||||||
RPPSS|29823332878|MARION CATANZANO RAMOS|3630000|
|
|
||||||
RPPSS|30078128870|Karina Cury Orsi|3200000|
|
|
||||||
RPPSS|30150489889|Priscila Yumi Yabiku|50000|
|
|
||||||
RPPSS|30406578877|JESSICA ROBERTA MARCOS|2122000|
|
|
||||||
RPPSS|30475471814|SAMUEL CAPPELLANES|2013000|
|
|
||||||
RPPSS|30604065884|ANA CLAUDIA RODRIGUES DA SILVA|40000|
|
|
||||||
RPPSS|30655210830|PRISCILA STOCHI MIRANDA|40000|
|
|
||||||
RPPSS|30998038806|LUCELAINE ALVES DA SILVA|50000|
|
|
||||||
RPPSS|31000585859|RENATA SOUZA LAZARO DONATANGELO|216000|
|
|
||||||
RPPSS|31024810860|JULIANA ANGELICO BERTONCELLO 060846|1950000|
|
|
||||||
RPPSS|31442001828|JOSE EDUARDO PEREIRA DE CARVALHO|100000|
|
|
||||||
RPPSS|31470174863|Bianor Jesus Morante Junior|150000|
|
|
||||||
RPPSS|31669983811|ANA PAULA MANDUCCHI|330000|
|
|
||||||
RPPSS|31693766833|MICHELLA BOSSOLANI BUCK PEREIRA|2440000|
|
|
||||||
RPPSS|31699028818|MARIO ANTONIO ETTIOPI|40000|
|
|
||||||
RPPSS|31720841837|MAISA REGINA DA SILVA MAGRI|50000|
|
|
||||||
RPPSS|31981052852|JESSICA EROLES CASSILLAS|2800000|
|
|
||||||
RPPSS|31981055878|JULIANE EROLES CASSILLAS|2500000|
|
|
||||||
RPPSS|32088384847|Neuza de F�tima Barbosa Carrero|150000|
|
|
||||||
RPPSS|32101913879|JEAN DE CARVALHO BARALDI 000001|40000|
|
|
||||||
RPPSS|32172666840|MONIQUE ESPINOSA CORDEIRO|2250000|
|
|
||||||
RPPSS|32338926838|MARCELA CAMARGO COSTA|1760000|
|
|
||||||
RPPSS|32349755827|FERNANDA FRANZINI|4200000|
|
|
||||||
RPPSS|32456647882|MARIA VITORIA BARBIZAN 261394|3200000|
|
|
||||||
RPPSS|32508446831|FABRICIA JULIANA ROMERA|40000|
|
|
||||||
RPPSS|32604361892|VALDINEIA MORAES DE LIMA|200000|
|
|
||||||
RPPSS|32795801884|Nelvina Ribeiro Da Silva|50000|
|
|
||||||
RPPSS|32799255841|FERNANDA BETIOLI|240000|
|
|
||||||
RPPSS|32801168807|SIMEI GARCIA OLIVEIRA DA COSTA|2000000|
|
|
||||||
RPPSS|32829002873|MARIANA SLAZARI DAVOGLIO|3100000|
|
|
||||||
RPPSS|32842574893|ALEXANDRE CAZETTO PARDIM|50000|
|
|
||||||
RPPSS|32946947840|DEBORA ELISA MINOTTI MATOS|150000|
|
|
||||||
RPPSS|33217764803|VANESSA ROSA DA COSTA|3500000|
|
|
||||||
RPPSS|33286403806|ADILSON SELIS DA SILVA|50000|
|
|
||||||
RPPSS|33404269888|CAMILA AGUILAR ALEXANDRE SILVEIRA|50000|
|
|
||||||
RPPSS|33410993878|ANA JULIA DE SOUZA SOARES|2150000|
|
|
||||||
RPPSS|33514485895|JULIA M FIORI|50000|
|
|
||||||
RPPSS|33610388803|MARIA EDUARDA CANDIDO BATISTA|2500000|
|
|
||||||
RPPSS|33644927863|ERIKA LUCIANA BEVOLO|3000000|
|
|
||||||
RPPSS|33895275832|MILIANE MARTINS|3000000|
|
|
||||||
RPPSS|33915203882|GABRIELA SALVADOR|40000|
|
|
||||||
RPPSS|34215931835|Cristiane de F�tima da Silva�Vale|50000|
|
|
||||||
RPPSS|34306392805|Juliana Aparecida Martimiano Leonardo|50000|
|
|
||||||
RPPSS|34318009807|TATIANA JOICE MORAES|50000|
|
|
||||||
RPPSS|34370997879|FRANCIELE CAROLINE AMANCIO|2850000|
|
|
||||||
RPPSS|34402174856|SOLANGE APARECIDA BETARELLO RIOS|3550000|
|
|
||||||
RPPSS|34440527825|THAIZ FERNANDA SCARAFICI BARBOSA|800000|
|
|
||||||
RPPSS|34460401886|TAIS CAROLINA MARTINEZ DOS SANTOS|1400000|
|
|
||||||
RPPSS|34810602893|MARCELA BIZARI CARVALHO|100000|
|
|
||||||
RPPSS|34875193807|JAQUELINE GONZAGA DA SILVA|2620000|
|
|
||||||
RPPSS|35057288881|THAIS PAMELLA MADALENA|3050000|
|
|
||||||
RPPSS|35113833827|LAURA CAROLINA MANCHINI PAFARO GONCALVES|2500000|
|
|
||||||
RPPSS|35149029840|PAULINE RUTH DE LIMA|30000|
|
|
||||||
RPPSS|35255377864|ALISE FERNANDA SOARES|40000|
|
|
||||||
RPPSS|35347883898|VERENA ROGANTE GONZAGA|000|
|
|
||||||
RPPSS|35765497810|AMANDA FAVERO BORTOLIM|40000|
|
|
||||||
RPPSS|35789361897|Fernanda Alexandre do Amaral|250000|
|
|
||||||
RPPSS|36109384801|SIMONE MARTA DOS SANTOS GARCIA|50000|
|
|
||||||
RPPSS|36120680837|PAMELA ZAGATTO|1140000|
|
|
||||||
RPPSS|36362634802|ADRIANA CRISTINA DO AMARAL|100000|
|
|
||||||
RPPSS|36712891809|AURELIO AUGUSTINHO RUETE FILHO|250000|
|
|
||||||
RPPSS|36796002830|RENATA DE FAZZIO STUCHI 063643|3300000|
|
|
||||||
RPPSS|36835710802|MARILIA MARCHINI SILVA|3050000|
|
|
||||||
RPPSS|36862376870|SAMARA DAIANA ROLLA|40000|
|
|
||||||
RPPSS|36891611857|JULIANA BONORA PAULINO|515000|
|
|
||||||
RPPSS|36902313881|MARIANA GISSE PINTO|1400000|
|
|
||||||
RPPSS|36943033832|BEATRIZ APARECIDA MORETTO|3000000|
|
|
||||||
RPPSS|37277458869|CAMILA ROSA RODRIGUES|2350000|
|
|
||||||
RPPSS|37540150874|LETICIA CLEMENTE CASA GRANDE GONZALEZ|60000|
|
|
||||||
RPPSS|37592194899|TAINE BARBIZAN|50000|
|
|
||||||
RPPSS|37769876850|JULIELI DEL NERO LEPRE|3000000|
|
|
||||||
RPPSS|37878324832|BEATRIZ FERRAREZZI SACILOTTO|40000|
|
|
||||||
RPPSS|37955075889|RAYLTON KLEBER PEDRETI|50000|
|
|
||||||
RPPSS|37985087800|Ana Carolina Gomez Ferreira|15000|
|
|
||||||
RPPSS|38124272816|RAFAELA CARRETEL BELLUCCI|1600000|
|
|
||||||
RPPSS|38201772806|JACQUELINE BUENO FLOIS PEREIRA|3600000|
|
|
||||||
RPPSS|38355468805|CAMILA PITON|3100000|
|
|
||||||
RPPSS|38560706801|ANA LUCIA SEVERINO|50000|
|
|
||||||
RPPSS|38787632802|MARIA CLARA BARBIZAN|2470000|
|
|
||||||
RPPSS|39049951899|MANOELA DURAN MONTELEONE|40000|
|
|
||||||
RPPSS|39215344888|ANGELICA CRISTINA DA SILVA RAMOS|40000|
|
|
||||||
RPPSS|39349838800|ALINE LIMOLI SIOTTI|2500000|
|
|
||||||
RPPSS|39360976822|ANTONIO APPENDINO ROM�O|2210000|
|
|
||||||
RPPSS|39408974859|GABRIEL ZAMPERLINI|40000|
|
|
||||||
RPPSS|39440765836|MARCOS TULIO SASSO CEZARE 234337|50000|
|
|
||||||
RPPSS|39592945810|LETICIA MARIA FORATO|2800000|
|
|
||||||
RPPSS|39592948836|DANIELLE MAGUETAS FRASSON|1400000|
|
|
||||||
RPPSS|39782518840|SERGIA APARECIDA PAVANI|240000|
|
|
||||||
RPPSS|39941673888|MONIZE CRISTINA LOFRANO DA SILVA|50000|
|
|
||||||
RPPSS|40239491831|Carine Elaine Teixeira|40000|
|
|
||||||
RPPSS|40505960834|MARIA APARECIDA LUIZ FORTUNA 211960|115000|
|
|
||||||
RPPSS|40515101826|JESSICA FERNANDA CLAUDINO TEIXEIRA|3300000|
|
|
||||||
RPPSS|40558832857|Eveling Thais Won Ancken|350000|
|
|
||||||
RPPSS|40571844812|Giulia Serain Alberto|1763000|
|
|
||||||
RPPSS|40715174843|Raiele Ara�jo Bernardes|3440000|
|
|
||||||
RPPSS|40777528843|GABRIELA FAZIO GOMES|2500000|
|
|
||||||
RPPSS|40812426827|SARA JANE DO AMARAL|2810000|
|
|
||||||
RPPSS|40820650838|William Chagas Bodin|3250000|
|
|
||||||
RPPSS|40992827833|BRUNA ANGELA MARIA|4500000|
|
|
||||||
RPPSS|41186690801|LORAINE APARECIDA RODRIGUES COLOMBO|1400000|
|
|
||||||
RPPSS|41479945803|GUSTAVO KUMAGAI DE OLIVEIRA|1150000|
|
|
||||||
RPPSS|41540111890|AMANDA THOMAZ SALVATIERRA|3200000|
|
|
||||||
RPPSS|41565055888|Marcus Vinicius Pereira Dispatto|50000|
|
|
||||||
RPPSS|41575901803|DANIELY SALES|40000|
|
|
||||||
RPPSS|41577518845|NATALIA MIHOTO VACCARI|2060000|
|
|
||||||
RPPSS|41593131836|INAYARA DE CASSIA RISSI|2150000|
|
|
||||||
RPPSS|41609504852|WELLINGTON CESAR DA SILVA JUNIOR|1450000|
|
|
||||||
RPPSS|41946226858|Nathalia Aparecida Dias de Freitas|2415000|
|
|
||||||
RPPSS|42005326895|ANA CLARA ROBLES SANTAMARIA|1452000|
|
|
||||||
RPPSS|42031475819|LUISA SARANZA AYUSSO|120000|
|
|
||||||
RPPSS|42066193879|ISABELA FONSECA RODRIGUES|500000|
|
|
||||||
RPPSS|42097588840|ANA LETICIA PHANTASIA|50000|
|
|
||||||
RPPSS|42279802830|LAIS FERNANDA DE SOUZA|800000|
|
|
||||||
RPPSS|42724292804|ANA JULIA GIANINI|1600000|
|
|
||||||
RPPSS|42742995897|BIANCA CRISTINA GOMES CAPELETTI|2100000|
|
|
||||||
RPPSS|42790707847|LETICIA GABRIELA DEVINCOLA|50000|
|
|
||||||
RPPSS|43086496843|LEONORA BRANDAO PIVA|50000|
|
|
||||||
RPPSS|43108982893|Henrique Carnelossi De Melo|1463000|
|
|
||||||
RPPSS|43323069889|DANIELI APARECIDA DE OLIVEIRA IMAMURA|350000|
|
|
||||||
RPPSS|43496467878|MANUELLA BAISSO MAIORANO|1181000|
|
|
||||||
RPPSS|43632340838|ANA LAURA CASTRO GERLACH|800000|
|
|
||||||
RPPSS|43870182830|VITOR FRARE CASATTI|150000|
|
|
||||||
RPPSS|43887191889|LARA DOLENCE CROSARIOL|150000|
|
|
||||||
RPPSS|44066671802|ISADORA GOMES RAMOS|1600000|
|
|
||||||
RPPSS|44129911805|EMILLY GIOVANA DA SILVA|2250000|
|
|
||||||
RPPSS|44254735820|Maria Clara Cabral Brand�o|50000|
|
|
||||||
RPPSS|44480048804|Natalia Marchini|80000|
|
|
||||||
RPPSS|44774472808|CRISLEY CORREA DE FARIAS|40000|
|
|
||||||
RPPSS|44929601800|LEONARDO APARECIDO MARQUES|1400000|
|
|
||||||
RPPSS|45022157837|JULIA COSTA DE OLIVEIRA|1000000|
|
|
||||||
RPPSS|45329731801|Maiara Carolina Rangel Am�ncio|200000|
|
|
||||||
RPPSS|45440593802|MAISA THAINA DE MELO MOLIM|3000000|
|
|
||||||
RPPSS|45453794860|STHEFANY GABRIELI BARALDO DE CALLIS|1963000|
|
|
||||||
RPPSS|45551088847|ISADORA DE SOUZA MOREIRA|2290000|
|
|
||||||
RPPSS|45724543885|MILENA MARQUES CERVANTES|3250000|
|
|
||||||
RPPSS|45887176857|Giovana Santana Gon�alves|921000|
|
|
||||||
RPPSS|46158913855|TAILA JULIANA DA SILVA DOS SANTOS|3500000|
|
|
||||||
RPPSS|46373592871|Giovana Pereira da Silva|450000|
|
|
||||||
RPPSS|46727537860|GIOVANA LAZARA BARBOSA|1860000|
|
|
||||||
RPPSS|46992173898|Bruna Roberta Camargo|50000|
|
|
||||||
RPPSS|47067248809|ISABELLY TURIN LAZARINI|1000000|
|
|
||||||
RPPSS|47661768801|RAQUEL CORDOBA BOTELHO|40000|
|
|
||||||
RPPSS|47806007822|NATHALIA SIVIERO DA SILVA|3600000|
|
|
||||||
RPPSS|48003353882|Nathalia Gomes de Souza|50000|
|
|
||||||
RPPSS|48048165856|ANA JULIA DE OLIVEIRA CAMPOS|50000|
|
|
||||||
RPPSS|48085664860|MARIA EDUARDA SOLFA RIBEIRO BERNARDO|1600000|
|
|
||||||
RPPSS|48145567807|CAMILA KANESIRO CHENERI|30000|
|
|
||||||
RPPSS|48661261830|MANUELA TORRES ONDON|1750000|
|
|
||||||
RPPSS|48982412808|Isabeli Samara Rodrigues Amancio|2100000|
|
|
||||||
RPPSS|49285008810|VITORIA BERTI SANTOS|1500000|
|
|
||||||
RPPSS|49330436803|Julia Ferreira|1400000|
|
|
||||||
RPPSS|50399917896|BEATRIZ ATHAYDES SILVA|120000|
|
|
||||||
RPPSS|51126823880|CLARA UBEDA GARCIA|200000|
|
|
||||||
RPPSS|51293533823|JAMILE LUCIO DO SANTOS|1400000|
|
|
||||||
RPPSS|51314439898|LARISSA PIGONI BOSCHEZZE|1280000|
|
|
||||||
RPPSS|53781862852|BEATRIZ MICHELAN|40000|
|
|
||||||
RPPSS|54112613849|GLADIS APARECIDA ANDALO DOS SANTOS|300000|
|
|
||||||
RPPSS|54735681817|LUCAS BERTELLINI PIROLA|2281000|
|
|
||||||
RPPSS|55316836855|LETICIA BEATRIZ COSTA|1100000|
|
|
||||||
RPPSS|55979308857|MURILO PIMENTA GARBIM|40000|
|
|
||||||
RPPSS|57038141899|ANA LUIZA ZUIM LAMBERTE|40000|
|
|
||||||
RPPSS|57064880881|LARA IZILDA NOGUEIRA|1850000|
|
|
||||||
RPPSS|59061106826|VALENTINA BASTOS COSTA|150000|
|
|
||||||
RPPSS|62106449887|JOSE ANSELMO LUCIANO|500000|
|
|
||||||
RPPSS|64791076320|LEIDIANE REGIA DE SOUSA|3000000|
|
|
||||||
RPPSS|73702692800|SONIA TAYAR BENEDITO JORGE|1400000|
|
|
||||||
RPPSS|73734853834|LAETE ROSA|40000|
|
|
||||||
RPPSS|73923443900|Soleni Borsatto Massoco|200000|
|
|
||||||
RPPSS|78460298868|IVONETE VOLPI NEZINHO|150000|
|
|
||||||
RPPSS|78558395853|PEDRO ANTONIO ARTUZO|250000|
|
|
||||||
RPPSS|78719690800|LEDA MARIA FREITAS CHIODINI|300000|
|
|
||||||
RPPSS|84687436153|SIMONE DE JESUS BATISTA|3850000|
|
|
||||||
RPPSS|88861660878|NEUSA MIGUEL|2200000|
|
|
||||||
RPPSS|93940084891|CARMEM SILVIA FIORIN FERNANDEZ|1750000|
|
|
||||||
FIMDmed|
|
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
# DMED - Comparador TXT vs XLS
|
||||||
|
|
||||||
|
Biblioteca Rust para processar e comparar arquivos DMED (TXT) com relatórios Prosoft (XLS).
|
||||||
|
|
||||||
|
## 📋 Funcionalidades
|
||||||
|
|
||||||
|
### 1️⃣ Processador TXT (DMED)
|
||||||
|
- Lê arquivos TXT do formato DMED
|
||||||
|
- Extrai registros RPPSS (CPF, Nome, Valor)
|
||||||
|
- Pula as 4 primeiras linhas automaticamente
|
||||||
|
- Converte valores (últimos 2 dígitos são decimais)
|
||||||
|
|
||||||
|
### 2️⃣ Processador XLS (Prosoft)
|
||||||
|
- Lê arquivos XLS e XLSX (detecção automática)
|
||||||
|
- Extrai CPF (coluna E) e Valor (coluna H)
|
||||||
|
- Remove formatação de CPF (pontos e hífens)
|
||||||
|
- **Agrupa por CPF** e **soma valores** de múltiplas notas
|
||||||
|
- Filtra apenas CPF (11 dígitos), descarta CNPJ
|
||||||
|
|
||||||
|
### 3️⃣ Comparador
|
||||||
|
- Compara valores entre TXT e XLS
|
||||||
|
- Identifica divergências
|
||||||
|
- Gera relatório detalhado
|
||||||
|
- Estatísticas completas
|
||||||
|
|
||||||
|
## 🚀 Como usar
|
||||||
|
|
||||||
|
### Instalação
|
||||||
|
|
||||||
|
Adicione ao seu `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
dmed = { path = "../dmed" }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Como biblioteca
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use dmed::{processar_arquivo, xls::processar_xls};
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
// Processa TXT
|
||||||
|
let pessoas_txt = processar_arquivo("LfsIssDmed068325.txt")?;
|
||||||
|
|
||||||
|
// Processa XLS
|
||||||
|
let pessoas_xls = processar_xls("RELATORIO_PROSOFT_MOD_51.xls")?;
|
||||||
|
|
||||||
|
// Comparar, buscar, etc.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Executar exemplos
|
||||||
|
|
||||||
|
#### 1. Comparador básico (só TXT)
|
||||||
|
```bash
|
||||||
|
cargo run --example comparador
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Comparador completo (TXT + XLS)
|
||||||
|
```bash
|
||||||
|
# Coloque o arquivo RELATORIO_PROSOFT_MOD_51.xls na pasta do projeto
|
||||||
|
cargo run --example comparador_completo
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Main (demonstração)
|
||||||
|
```bash
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 Estrutura do projeto
|
||||||
|
|
||||||
|
```
|
||||||
|
dmed/
|
||||||
|
├── src/
|
||||||
|
│ ├── lib.rs # API principal
|
||||||
|
│ ├── xls.rs # Processador XLS
|
||||||
|
│ └── main.rs # Exemplo de uso
|
||||||
|
├── examples/
|
||||||
|
│ ├── comparador.rs # Exemplo com TXT
|
||||||
|
│ └── comparador_completo.rs # Exemplo TXT vs XLS
|
||||||
|
├── Cargo.toml
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 API Pública
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct Pessoa {
|
||||||
|
pub cpf: String, // CPF (11 dígitos, sem formatação)
|
||||||
|
pub nome: String, // Nome completo
|
||||||
|
pub valor: f64, // Valor em reais
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Funções
|
||||||
|
|
||||||
|
#### Processamento TXT
|
||||||
|
```rust
|
||||||
|
// Processa arquivo TXT
|
||||||
|
pub fn processar_arquivo(caminho: &str) -> io::Result<Vec<Pessoa>>
|
||||||
|
|
||||||
|
// Processa string (útil para testes)
|
||||||
|
pub fn processar_conteudo(conteudo: &str) -> io::Result<Vec<Pessoa>>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Processamento XLS
|
||||||
|
```rust
|
||||||
|
// Processa arquivo XLS/XLSX (módulo xls)
|
||||||
|
pub fn processar_xls(caminho: &str) -> io::Result<Vec<Pessoa>>
|
||||||
|
```
|
||||||
|
**Nota:** Aceita tanto `.xls` quanto `.xlsx` - a detecção é automática!
|
||||||
|
|
||||||
|
#### Utilidades
|
||||||
|
```rust
|
||||||
|
// Converte para JSON
|
||||||
|
pub fn para_json(pessoas: &[Pessoa]) -> Result<String, serde_json::Error>
|
||||||
|
|
||||||
|
// Busca por CPF
|
||||||
|
pub fn buscar_por_cpf<'a>(pessoas: &'a [Pessoa], cpf: &str) -> Option<&'a Pessoa>
|
||||||
|
|
||||||
|
// Filtra por valor mínimo
|
||||||
|
pub fn filtrar_por_valor_minimo(pessoas: &[Pessoa], valor_minimo: f64) -> Vec<&Pessoa>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Formato dos arquivos
|
||||||
|
|
||||||
|
### TXT (DMED)
|
||||||
|
```
|
||||||
|
Dmed|2026|2025|N||| <- Linha 1 (pular)
|
||||||
|
RESPO|...|... <- Linha 2 (pular)
|
||||||
|
DECPJ|...|... <- Linha 3 (pular)
|
||||||
|
PSS| <- Linha 4 (pular)
|
||||||
|
RPPSS|00276808860|MARCO ANTONIO MACHADO|2200000| <- Processar
|
||||||
|
RPPSS|00633999032|ELISANDRA BARBOSA TORRES|600000| <- Processar
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Campos:**
|
||||||
|
- Campo 1: `RPPSS` (filtro)
|
||||||
|
- Campo 2: CPF (11 dígitos)
|
||||||
|
- Campo 3: Nome
|
||||||
|
- Campo 4: Valor (últimos 2 dígitos = decimais)
|
||||||
|
|
||||||
|
### XLS (Prosoft)
|
||||||
|
|
||||||
|
| A | B | C | D | E (CPF) | F | G | H (Valor) |
|
||||||
|
|---|---|---|---|---------|---|---|-----------|
|
||||||
|
| ... | ... | ... | ... | 123.456.789-01 | ... | ... | 1500.50 |
|
||||||
|
| ... | ... | ... | ... | 123.456.789-01 | ... | ... | 500.00 |
|
||||||
|
|
||||||
|
- Linhas 1-3: Identificação (pular)
|
||||||
|
- Linha 4: Cabeçalho (pular)
|
||||||
|
- Coluna E: CPF/CNPJ (manter apenas CPF)
|
||||||
|
- Coluna H: Valores (somar por CPF)
|
||||||
|
|
||||||
|
## 🧪 Testes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 Exemplo de saída
|
||||||
|
|
||||||
|
```
|
||||||
|
═══════════════════════════════════════════════
|
||||||
|
COMPARADOR DMED (TXT) vs PROSOFT (XLS)
|
||||||
|
═══════════════════════════════════════════════
|
||||||
|
|
||||||
|
📄 Processando arquivo TXT (DMED)...
|
||||||
|
✓ 336 registros encontrados
|
||||||
|
|
||||||
|
📊 Processando arquivo XLS (Prosoft)...
|
||||||
|
✓ 245 registros únicos encontrados
|
||||||
|
|
||||||
|
═══════════════════════════════════════════════
|
||||||
|
ANÁLISE
|
||||||
|
═══════════════════════════════════════════════
|
||||||
|
|
||||||
|
📊 Estatísticas:
|
||||||
|
Total de CPFs únicos: 400
|
||||||
|
Presentes em ambos: 181
|
||||||
|
Apenas no TXT: 155
|
||||||
|
Apenas no XLS: 64
|
||||||
|
|
||||||
|
📈 Divergências de valores:
|
||||||
|
Total: 12 registros com diferenças
|
||||||
|
|
||||||
|
═══════════════════════════════════════════════
|
||||||
|
TOP 10 DIVERGÊNCIAS
|
||||||
|
═══════════════════════════════════════════════
|
||||||
|
|
||||||
|
1. CPF: 00276808860
|
||||||
|
Nome: MARCO ANTONIO MACHADO
|
||||||
|
TXT (DMED): R$ 22000.00
|
||||||
|
XLS (Prosoft): R$ 22500.00
|
||||||
|
Diferença: R$ 500.00 (+2.3%)
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📄 Licença
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
## 🤝 Contribuindo
|
||||||
|
|
||||||
|
Pull requests são bem-vindos!
|
||||||
Binary file not shown.
@@ -0,0 +1,13 @@
|
|||||||
|
#[cfg(windows)]
|
||||||
|
fn main() {
|
||||||
|
if std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
|
||||||
|
let mut res = winres::WindowsResource::new();
|
||||||
|
res.set_icon("icon.ico"); // Caminho do seu arquivo .ico
|
||||||
|
res.compile().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn main() {
|
||||||
|
// Não faz nada em outros sistemas
|
||||||
|
}
|
||||||
Vendored
BIN
Binary file not shown.
@@ -7,7 +7,7 @@ fn main() -> std::io::Result<()> {
|
|||||||
// Processa dois arquivos diferentes
|
// Processa dois arquivos diferentes
|
||||||
println!("=== COMPARADOR DE ARQUIVOS DMED ===\n");
|
println!("=== COMPARADOR DE ARQUIVOS DMED ===\n");
|
||||||
|
|
||||||
let arquivo1 = "LfsIssDmed068325.txt";
|
let arquivo1 = "LfsIssDmed008425.txt";
|
||||||
let pessoas1 = processar_arquivo(arquivo1)?;
|
let pessoas1 = processar_arquivo(arquivo1)?;
|
||||||
|
|
||||||
println!("Arquivo 1: {} registros", pessoas1.len());
|
println!("Arquivo 1: {} registros", pessoas1.len());
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
// Comparador completo: TXT (DMED) vs XLS (Prosoft)
|
||||||
|
|
||||||
|
use dmed::{processar_arquivo, xls::processar_xls, Pessoa};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Divergencia {
|
||||||
|
cpf: String,
|
||||||
|
nome: String,
|
||||||
|
valor_txt: f64,
|
||||||
|
valor_xls: f64,
|
||||||
|
diferenca: f64,
|
||||||
|
percentual: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
println!("═══════════════════════════════════════════════");
|
||||||
|
println!(" COMPARADOR DMED (TXT) vs PROSOFT (XLS)");
|
||||||
|
println!("═══════════════════════════════════════════════\n");
|
||||||
|
|
||||||
|
// Processa arquivo TXT (DMED)
|
||||||
|
println!("📄 Processando arquivo TXT (DMED)...");
|
||||||
|
let arquivo_txt = "LfsIssDmed008425.txt";
|
||||||
|
let pessoas_txt = processar_arquivo(arquivo_txt)?;
|
||||||
|
println!(" ✓ {} registros encontrados\n", pessoas_txt.len());
|
||||||
|
|
||||||
|
// Processa arquivo XLS (Prosoft)
|
||||||
|
println!("📊 Processando arquivo XLS (Prosoft)...");
|
||||||
|
let arquivo_xls = "RELATORIO_PROSOFT_MOD_51.xls";
|
||||||
|
let pessoas_xls = match processar_xls(arquivo_xls) {
|
||||||
|
Ok(p) => {
|
||||||
|
println!(" ✓ {} registros únicos encontrados\n", p.len());
|
||||||
|
p
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!(" ✗ Erro ao processar XLS: {}", e);
|
||||||
|
println!(" ℹ️ Coloque o arquivo RELATORIO_PROSOFT_MOD_51.xls na pasta do projeto\n");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cria mapas para comparação
|
||||||
|
let mapa_txt: HashMap<&str, &Pessoa> = pessoas_txt
|
||||||
|
.iter()
|
||||||
|
.map(|p| (p.cpf.as_str(), p))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mapa_xls: HashMap<&str, &Pessoa> = pessoas_xls
|
||||||
|
.iter()
|
||||||
|
.map(|p| (p.cpf.as_str(), p))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Conjunto de todos os CPFs
|
||||||
|
let todos_cpfs: HashSet<&str> = mapa_txt.keys()
|
||||||
|
.chain(mapa_xls.keys())
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
println!("═══════════════════════════════════════════════");
|
||||||
|
println!(" ANÁLISE");
|
||||||
|
println!("═══════════════════════════════════════════════\n");
|
||||||
|
|
||||||
|
// Estatísticas
|
||||||
|
let cpfs_ambos = todos_cpfs.iter()
|
||||||
|
.filter(|cpf| mapa_txt.contains_key(**cpf) && mapa_xls.contains_key(**cpf))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
let cpfs_so_txt = todos_cpfs.iter()
|
||||||
|
.filter(|cpf| mapa_txt.contains_key(**cpf) && !mapa_xls.contains_key(**cpf))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
let cpfs_so_xls = todos_cpfs.iter()
|
||||||
|
.filter(|cpf| !mapa_txt.contains_key(**cpf) && mapa_xls.contains_key(**cpf))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
println!("📊 Estatísticas:");
|
||||||
|
println!(" Total de CPFs únicos: {}", todos_cpfs.len());
|
||||||
|
println!(" Presentes em ambos: {}", cpfs_ambos);
|
||||||
|
println!(" Apenas no TXT: {}", cpfs_so_txt);
|
||||||
|
println!(" Apenas no XLS: {}", cpfs_so_xls);
|
||||||
|
|
||||||
|
// Encontra divergências
|
||||||
|
let mut divergencias = Vec::new();
|
||||||
|
let margem_tolerancia = 0.01; // R$ 0,01
|
||||||
|
|
||||||
|
for cpf in todos_cpfs.iter() {
|
||||||
|
if let (Some(p_txt), Some(p_xls)) = (mapa_txt.get(*cpf), mapa_xls.get(*cpf)) {
|
||||||
|
let diferenca = (p_txt.valor - p_xls.valor).abs();
|
||||||
|
|
||||||
|
if diferenca > margem_tolerancia {
|
||||||
|
let percentual = if p_xls.valor != 0.0 {
|
||||||
|
((p_txt.valor - p_xls.valor) / p_xls.valor) * 100.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
divergencias.push(Divergencia {
|
||||||
|
cpf: cpf.to_string(),
|
||||||
|
nome: p_txt.nome.clone(),
|
||||||
|
valor_txt: p_txt.valor,
|
||||||
|
valor_xls: p_xls.valor,
|
||||||
|
diferenca,
|
||||||
|
percentual,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordena divergências por diferença (maior primeiro)
|
||||||
|
divergencias.sort_by(|a, b| b.diferenca.abs().partial_cmp(&a.diferenca.abs()).unwrap());
|
||||||
|
|
||||||
|
println!("\n📈 Divergências de valores:");
|
||||||
|
println!(" Total: {} registros com diferenças\n", divergencias.len());
|
||||||
|
|
||||||
|
if !divergencias.is_empty() {
|
||||||
|
println!("═══════════════════════════════════════════════");
|
||||||
|
println!(" TOP 10 DIVERGÊNCIAS");
|
||||||
|
println!("═══════════════════════════════════════════════\n");
|
||||||
|
|
||||||
|
for (i, div) in divergencias.iter().take(10).enumerate() {
|
||||||
|
println!("{}. CPF: {}", i + 1, div.cpf);
|
||||||
|
if !div.nome.is_empty() {
|
||||||
|
println!(" Nome: {}", div.nome);
|
||||||
|
}
|
||||||
|
println!(" TXT (DMED): R$ {:>12.2}", div.valor_txt);
|
||||||
|
println!(" XLS (Prosoft): R$ {:>12.2}", div.valor_xls);
|
||||||
|
println!(" Diferença: R$ {:>12.2} ({:+.1}%)", div.diferenca, div.percentual);
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salva relatório completo
|
||||||
|
salvar_relatorio(&divergencias, &mapa_txt, &mapa_xls, cpfs_so_txt, cpfs_so_xls)?;
|
||||||
|
} else {
|
||||||
|
println!(" ✓ Todos os valores conferem!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPFs que estão apenas em um dos arquivos
|
||||||
|
if cpfs_so_txt > 0 {
|
||||||
|
println!("⚠️ CPFs apenas no TXT (primeiros 5):");
|
||||||
|
for cpf in todos_cpfs.iter().filter(|c| mapa_txt.contains_key(**c) && !mapa_xls.contains_key(**c)).take(5) {
|
||||||
|
if let Some(p) = mapa_txt.get(*cpf) {
|
||||||
|
println!(" {} - {} - R$ {:.2}", cpf, p.nome, p.valor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
if cpfs_so_xls > 0 {
|
||||||
|
println!("⚠️ CPFs apenas no XLS (primeiros 5):");
|
||||||
|
for cpf in todos_cpfs.iter().filter(|c| !mapa_txt.contains_key(**c) && mapa_xls.contains_key(**c)).take(5) {
|
||||||
|
if let Some(p) = mapa_xls.get(*cpf) {
|
||||||
|
println!(" {} - R$ {:.2}", cpf, p.valor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn salvar_relatorio(
|
||||||
|
divergencias: &[Divergencia],
|
||||||
|
mapa_txt: &HashMap<&str, &Pessoa>,
|
||||||
|
mapa_xls: &HashMap<&str, &Pessoa>,
|
||||||
|
cpfs_so_txt: usize,
|
||||||
|
cpfs_so_xls: usize,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
let mut arquivo = File::create("relatorio_divergencias.txt")?;
|
||||||
|
|
||||||
|
writeln!(arquivo, "RELATÓRIO DE DIVERGÊNCIAS - DMED vs PROSOFT")?;
|
||||||
|
writeln!(arquivo, "Data: {}", chrono::Local::now().format("%d/%m/%Y %H:%M:%S"))?;
|
||||||
|
writeln!(arquivo, "=")?;
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
|
||||||
|
writeln!(arquivo, "RESUMO:")?;
|
||||||
|
writeln!(arquivo, "Total de divergências: {}", divergencias.len())?;
|
||||||
|
writeln!(arquivo, "CPFs apenas no TXT: {}", cpfs_so_txt)?;
|
||||||
|
writeln!(arquivo, "CPFs apenas no XLS: {}", cpfs_so_xls)?;
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
|
||||||
|
writeln!(arquivo, "DETALHAMENTO DAS DIVERGÊNCIAS:")?;
|
||||||
|
writeln!(arquivo, "{:-<100}", "")?;
|
||||||
|
|
||||||
|
for (i, div) in divergencias.iter().enumerate() {
|
||||||
|
writeln!(arquivo, "\n{}. CPF: {}", i + 1, div.cpf)?;
|
||||||
|
if !div.nome.is_empty() {
|
||||||
|
writeln!(arquivo, " Nome: {}", div.nome)?;
|
||||||
|
}
|
||||||
|
writeln!(arquivo, " TXT (DMED): R$ {:>12.2}", div.valor_txt)?;
|
||||||
|
writeln!(arquivo, " XLS (Prosoft): R$ {:>12.2}", div.valor_xls)?;
|
||||||
|
writeln!(arquivo, " Diferença: R$ {:>12.2} ({:+.1}%)", div.diferenca, div.percentual)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("✓ Relatório completo salvo em relatorio_divergencias.txt");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Script de teste rápido - execute com: cargo run --example teste_xls
|
||||||
|
|
||||||
|
use dmed::xls::processar_xls;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("=== TESTE DO PROCESSADOR XLS/XLSX ===\n");
|
||||||
|
|
||||||
|
let arquivo = "RELATORIO_PROSOFT_MOD_51.xls";
|
||||||
|
|
||||||
|
println!("📂 Tentando abrir: {}", arquivo);
|
||||||
|
println!("ℹ️ Suporta .xls e .xlsx (detecção automática)\n");
|
||||||
|
|
||||||
|
match processar_xls(arquivo) {
|
||||||
|
Ok(pessoas) => {
|
||||||
|
println!("✓ Arquivo processado com sucesso!");
|
||||||
|
println!("Total de registros únicos: {}\n", pessoas.len());
|
||||||
|
|
||||||
|
println!("=== Primeiros 10 registros ===");
|
||||||
|
for (i, pessoa) in pessoas.iter().take(10).enumerate() {
|
||||||
|
println!("{}. CPF: {} - R$ {:.2}",
|
||||||
|
i + 1,
|
||||||
|
pessoa.cpf,
|
||||||
|
pessoa.valor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estatísticas
|
||||||
|
let total_valor: f64 = pessoas.iter().map(|p| p.valor).sum();
|
||||||
|
let maior_valor = pessoas.iter()
|
||||||
|
.max_by(|a, b| a.valor.partial_cmp(&b.valor).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("\n=== Estatísticas ===");
|
||||||
|
println!("Total de valores: R$ {:.2}", total_valor);
|
||||||
|
println!("Maior valor: R$ {:.2} (CPF: {})", maior_valor.valor, maior_valor.cpf);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("✗ Erro ao processar arquivo: {}", e);
|
||||||
|
println!("\n💡 Dica:");
|
||||||
|
println!(" 1. Certifique-se que o arquivo existe na pasta do projeto");
|
||||||
|
println!(" 2. Nome esperado: RELATORIO_PROSOFT_MOD_51.xls");
|
||||||
|
println!(" 3. Formato: Excel .xls ou .xlsx (ambos são suportados)");
|
||||||
|
println!(" 4. Mesmo que a extensão seja .xls, pode ser .xlsx internamente");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+465
@@ -0,0 +1,465 @@
|
|||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
use eframe::egui;
|
||||||
|
use dmed::{processar_arquivo, xls::processar_xls, Pessoa};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn main() -> eframe::Result {
|
||||||
|
let options = eframe::NativeOptions {
|
||||||
|
viewport: egui::ViewportBuilder::default()
|
||||||
|
.with_inner_size([950.0, 850.0])
|
||||||
|
.with_title("DMED - Comparador TXT vs XLS"),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
eframe::run_native(
|
||||||
|
"DMED Comparador",
|
||||||
|
options,
|
||||||
|
Box::new(|_cc| Ok(Box::new(ComparadorApp::default()))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ComparadorApp {
|
||||||
|
arquivo_txt: Option<String>,
|
||||||
|
arquivo_xls: Option<String>,
|
||||||
|
resultado: String,
|
||||||
|
divergencias: Vec<Divergencia>,
|
||||||
|
cpfs_so_txt: Vec<CpfExclusivo>,
|
||||||
|
cpfs_so_xls: Vec<CpfExclusivo>,
|
||||||
|
processando: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct CpfExclusivo {
|
||||||
|
cpf: String,
|
||||||
|
nome: String,
|
||||||
|
valor: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Divergencia {
|
||||||
|
cpf: String,
|
||||||
|
nome: String,
|
||||||
|
valor_txt: f64,
|
||||||
|
valor_xls: f64,
|
||||||
|
diferenca: f64,
|
||||||
|
percentual: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for ComparadorApp {
|
||||||
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.heading("📊 Comparador DMED (TXT) vs Prosoft (XLS)");
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
// Seleção de arquivos
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("📄 Arquivo TXT (DMED):");
|
||||||
|
if ui.button("📁 Selecionar").clicked() {
|
||||||
|
if let Some(path) = rfd::FileDialog::new()
|
||||||
|
.add_filter("Arquivos TXT", &["txt"])
|
||||||
|
.pick_file()
|
||||||
|
{
|
||||||
|
self.arquivo_txt = Some(path.display().to_string());
|
||||||
|
self.resultado.clear();
|
||||||
|
self.divergencias.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref path) = self.arquivo_txt {
|
||||||
|
ui.label(format!("✓ {}", path));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(5.0);
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("📊 Arquivo XLS (Prosoft):");
|
||||||
|
if ui.button("📁 Selecionar").clicked() {
|
||||||
|
if let Some(path) = rfd::FileDialog::new()
|
||||||
|
.add_filter("Arquivos Excel", &["xls", "xlsx"])
|
||||||
|
.pick_file()
|
||||||
|
{
|
||||||
|
self.arquivo_xls = Some(path.display().to_string());
|
||||||
|
self.resultado.clear();
|
||||||
|
self.divergencias.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref path) = self.arquivo_xls {
|
||||||
|
ui.label(format!("✓ {}", path));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
ui.separator();
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
// Botão de comparar
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let pode_comparar = self.arquivo_txt.is_some() &&
|
||||||
|
self.arquivo_xls.is_some() &&
|
||||||
|
!self.processando;
|
||||||
|
|
||||||
|
if ui.add_enabled(pode_comparar, egui::Button::new("🔍 Comparar Arquivos"))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.comparar();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.processando {
|
||||||
|
ui.spinner();
|
||||||
|
ui.label("Processando...");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
ui.separator();
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
// Resultados
|
||||||
|
if !self.resultado.is_empty() {
|
||||||
|
ui.label(&self.resultado);
|
||||||
|
ui.add_space(10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tabela de divergências
|
||||||
|
if !self.divergencias.is_empty() {
|
||||||
|
ui.heading(format!("📋 Divergências de valores: {}", self.divergencias.len()));
|
||||||
|
ui.add_space(5.0);
|
||||||
|
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.max_height(200.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
egui::Grid::new("divergencias_grid")
|
||||||
|
.striped(true)
|
||||||
|
.min_col_width(100.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
// Cabeçalho
|
||||||
|
ui.strong("CPF");
|
||||||
|
ui.strong("Nome");
|
||||||
|
ui.strong("TXT (R$)");
|
||||||
|
ui.strong("XLS (R$)");
|
||||||
|
ui.strong("Diferença (R$)");
|
||||||
|
ui.strong("Diferença (%)");
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
// Dados
|
||||||
|
for div in &self.divergencias {
|
||||||
|
ui.label(&div.cpf);
|
||||||
|
ui.label(&div.nome);
|
||||||
|
ui.label(format!("{:.2}", div.valor_txt));
|
||||||
|
ui.label(format!("{:.2}", div.valor_xls));
|
||||||
|
|
||||||
|
let cor = if div.diferenca > 0.0 {
|
||||||
|
egui::Color32::RED
|
||||||
|
} else {
|
||||||
|
egui::Color32::GREEN
|
||||||
|
};
|
||||||
|
ui.colored_label(cor, format!("{:.2}", div.diferenca));
|
||||||
|
ui.colored_label(cor, format!("{:+.1}%", div.percentual));
|
||||||
|
ui.end_row();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPFs apenas no TXT
|
||||||
|
if !self.cpfs_so_txt.is_empty() {
|
||||||
|
ui.separator();
|
||||||
|
ui.add_space(5.0);
|
||||||
|
ui.heading(format!("⚠️ CPFs apenas no TXT: {}", self.cpfs_so_txt.len()));
|
||||||
|
ui.label("Estes CPFs estão no DMED mas não aparecem no Prosoft");
|
||||||
|
ui.add_space(5.0);
|
||||||
|
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.max_height(150.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
egui::Grid::new("cpfs_txt_grid")
|
||||||
|
.striped(true)
|
||||||
|
.min_col_width(100.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.strong("CPF");
|
||||||
|
ui.strong("Nome");
|
||||||
|
ui.strong("Valor (R$)");
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
for cpf in &self.cpfs_so_txt {
|
||||||
|
ui.label(&cpf.cpf);
|
||||||
|
ui.label(&cpf.nome);
|
||||||
|
ui.label(format!("{:.2}", cpf.valor));
|
||||||
|
ui.end_row();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPFs apenas no XLS
|
||||||
|
if !self.cpfs_so_xls.is_empty() {
|
||||||
|
ui.separator();
|
||||||
|
ui.add_space(5.0);
|
||||||
|
ui.heading(format!("⚠️ CPFs apenas no XLS: {}", self.cpfs_so_xls.len()));
|
||||||
|
ui.label("Estes CPFs estão no Prosoft mas não aparecem no DMED");
|
||||||
|
ui.add_space(5.0);
|
||||||
|
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.max_height(150.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
egui::Grid::new("cpfs_xls_grid")
|
||||||
|
.striped(true)
|
||||||
|
.min_col_width(100.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.strong("CPF");
|
||||||
|
ui.strong("Valor (R$)");
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
for cpf in &self.cpfs_so_xls {
|
||||||
|
ui.label(&cpf.cpf);
|
||||||
|
ui.label(format!("{:.2}", cpf.valor));
|
||||||
|
ui.end_row();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Botão para exportar (mostra se tiver qualquer resultado)
|
||||||
|
if !self.divergencias.is_empty() || !self.cpfs_so_txt.is_empty() || !self.cpfs_so_xls.is_empty() {
|
||||||
|
ui.separator();
|
||||||
|
ui.add_space(5.0);
|
||||||
|
|
||||||
|
// Botão para exportar
|
||||||
|
if ui.button("💾 Exportar Relatório Completo").clicked() {
|
||||||
|
if let Some(path) = rfd::FileDialog::new()
|
||||||
|
.set_file_name("relatorio_completo.txt")
|
||||||
|
.save_file()
|
||||||
|
{
|
||||||
|
if let Err(e) = self.exportar_relatorio(&path.display().to_string()) {
|
||||||
|
self.resultado = format!("❌ Erro ao exportar: {}", e);
|
||||||
|
} else {
|
||||||
|
self.resultado = format!("✅ Relatório exportado para: {}", path.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComparadorApp {
|
||||||
|
fn comparar(&mut self) {
|
||||||
|
self.processando = true;
|
||||||
|
self.resultado.clear();
|
||||||
|
self.divergencias.clear();
|
||||||
|
self.cpfs_so_txt.clear();
|
||||||
|
self.cpfs_so_xls.clear();
|
||||||
|
|
||||||
|
let txt_path = self.arquivo_txt.as_ref().unwrap().clone();
|
||||||
|
let xls_path = self.arquivo_xls.as_ref().unwrap().clone();
|
||||||
|
|
||||||
|
// Processa arquivos
|
||||||
|
let pessoas_txt = match processar_arquivo(&txt_path) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
self.resultado = format!("❌ Erro ao processar TXT: {}", e);
|
||||||
|
self.processando = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let pessoas_xls = match processar_xls(&xls_path) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
self.resultado = format!("❌ Erro ao processar XLS: {}", e);
|
||||||
|
self.processando = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cria mapas
|
||||||
|
let mapa_txt: HashMap<&str, &Pessoa> = pessoas_txt
|
||||||
|
.iter()
|
||||||
|
.map(|p| (p.cpf.as_str(), p))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mapa_xls: HashMap<&str, &Pessoa> = pessoas_xls
|
||||||
|
.iter()
|
||||||
|
.map(|p| (p.cpf.as_str(), p))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Estatísticas
|
||||||
|
let cpfs_ambos = mapa_txt.keys()
|
||||||
|
.filter(|cpf| mapa_xls.contains_key(**cpf))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
let cpfs_so_txt = mapa_txt.keys()
|
||||||
|
.filter(|cpf| !mapa_xls.contains_key(**cpf))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
let cpfs_so_xls = mapa_xls.keys()
|
||||||
|
.filter(|cpf| !mapa_txt.contains_key(**cpf))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
// Encontra divergências
|
||||||
|
let margem_tolerancia = 0.01;
|
||||||
|
|
||||||
|
for (cpf, p_txt) in &mapa_txt {
|
||||||
|
if let Some(p_xls) = mapa_xls.get(*cpf) {
|
||||||
|
let diferenca = p_txt.valor - p_xls.valor;
|
||||||
|
|
||||||
|
if diferenca.abs() > margem_tolerancia {
|
||||||
|
let percentual = if p_xls.valor != 0.0 {
|
||||||
|
(diferenca / p_xls.valor) * 100.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
self.divergencias.push(Divergencia {
|
||||||
|
cpf: cpf.to_string(),
|
||||||
|
nome: p_txt.nome.clone(),
|
||||||
|
valor_txt: p_txt.valor,
|
||||||
|
valor_xls: p_xls.valor,
|
||||||
|
diferenca,
|
||||||
|
percentual,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// CPF só está no TXT
|
||||||
|
self.cpfs_so_txt.push(CpfExclusivo {
|
||||||
|
cpf: cpf.to_string(),
|
||||||
|
nome: p_txt.nome.clone(),
|
||||||
|
valor: p_txt.valor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPFs que estão só no XLS
|
||||||
|
for (cpf, p_xls) in &mapa_xls {
|
||||||
|
if !mapa_txt.contains_key(*cpf) {
|
||||||
|
self.cpfs_so_xls.push(CpfExclusivo {
|
||||||
|
cpf: cpf.to_string(),
|
||||||
|
nome: String::new(), // XLS não tem nome
|
||||||
|
valor: p_xls.valor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordena por diferença absoluta
|
||||||
|
self.divergencias.sort_by(|a, b| {
|
||||||
|
b.diferenca.abs().partial_cmp(&a.diferenca.abs()).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ordena CPFs exclusivos por CPF
|
||||||
|
self.cpfs_so_txt.sort_by(|a, b| a.cpf.cmp(&b.cpf));
|
||||||
|
self.cpfs_so_xls.sort_by(|a, b| a.cpf.cmp(&b.cpf));
|
||||||
|
|
||||||
|
// Resultado
|
||||||
|
self.resultado = format!(
|
||||||
|
"📊 Estatísticas:\n\
|
||||||
|
• Total TXT: {} registros\n\
|
||||||
|
• Total XLS: {} registros únicos\n\
|
||||||
|
• CPFs em ambos: {}\n\
|
||||||
|
• Apenas no TXT: {}\n\
|
||||||
|
• Apenas no XLS: {}\n\
|
||||||
|
• Divergências: {} CPFs com diferenças",
|
||||||
|
pessoas_txt.len(),
|
||||||
|
pessoas_xls.len(),
|
||||||
|
cpfs_ambos,
|
||||||
|
cpfs_so_txt,
|
||||||
|
cpfs_so_xls,
|
||||||
|
self.divergencias.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
self.processando = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exportar_relatorio(&self, caminho: &str) -> std::io::Result<()> {
|
||||||
|
use std::io::Write;
|
||||||
|
let mut arquivo = std::fs::File::create(caminho)?;
|
||||||
|
|
||||||
|
writeln!(arquivo, "╔═══════════════════════════════════════════════════════════════════════════════╗")?;
|
||||||
|
writeln!(arquivo, "║ RELATÓRIO DE COMPARAÇÃO - DMED vs PROSOFT ║")?;
|
||||||
|
writeln!(arquivo, "╚═══════════════════════════════════════════════════════════════════════════════╝")?;
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
|
||||||
|
// Resumo
|
||||||
|
writeln!(arquivo, "📊 RESUMO:")?;
|
||||||
|
writeln!(arquivo, " • Total de divergências de valores: {}", self.divergencias.len())?;
|
||||||
|
writeln!(arquivo, " • CPFs apenas no TXT: {}", self.cpfs_so_txt.len())?;
|
||||||
|
writeln!(arquivo, " • CPFs apenas no XLS: {}", self.cpfs_so_xls.len())?;
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
writeln!(arquivo, "{}", "=".repeat(120))?;
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
|
||||||
|
// Divergências de valores
|
||||||
|
if !self.divergencias.is_empty() {
|
||||||
|
writeln!(arquivo, "🔍 DIVERGÊNCIAS DE VALORES ({} registros)", self.divergencias.len())?;
|
||||||
|
writeln!(arquivo, "{}", "-".repeat(120))?;
|
||||||
|
writeln!(arquivo, "{:<15} {:<40} {:>15} {:>15} {:>15} {:>10}",
|
||||||
|
"CPF", "Nome", "TXT (R$)", "XLS (R$)", "Diferença (R$)", "Dif (%)")?;
|
||||||
|
writeln!(arquivo, "{}", "-".repeat(120))?;
|
||||||
|
|
||||||
|
for div in &self.divergencias {
|
||||||
|
writeln!(arquivo, "{:<15} {:<40} {:>15.2} {:>15.2} {:>15.2} {:>10.1}%",
|
||||||
|
div.cpf,
|
||||||
|
div.nome,
|
||||||
|
div.valor_txt,
|
||||||
|
div.valor_xls,
|
||||||
|
div.diferenca,
|
||||||
|
div.percentual)?;
|
||||||
|
}
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
writeln!(arquivo, "{}", "=".repeat(120))?;
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPFs apenas no TXT
|
||||||
|
if !self.cpfs_so_txt.is_empty() {
|
||||||
|
writeln!(arquivo, "⚠️ CPFs APENAS NO TXT ({} registros)", self.cpfs_so_txt.len())?;
|
||||||
|
writeln!(arquivo, " Estes CPFs estão no arquivo DMED mas não aparecem no Prosoft")?;
|
||||||
|
writeln!(arquivo, "{}", "-".repeat(120))?;
|
||||||
|
writeln!(arquivo, "{:<15} {:<50} {:>15}", "CPF", "Nome", "Valor (R$)")?;
|
||||||
|
writeln!(arquivo, "{}", "-".repeat(120))?;
|
||||||
|
|
||||||
|
for cpf in &self.cpfs_so_txt {
|
||||||
|
writeln!(arquivo, "{:<15} {:<50} {:>15.2}",
|
||||||
|
cpf.cpf,
|
||||||
|
cpf.nome,
|
||||||
|
cpf.valor)?;
|
||||||
|
}
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
writeln!(arquivo, "{}", "=".repeat(120))?;
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPFs apenas no XLS
|
||||||
|
if !self.cpfs_so_xls.is_empty() {
|
||||||
|
writeln!(arquivo, "⚠️ CPFs APENAS NO XLS ({} registros)", self.cpfs_so_xls.len())?;
|
||||||
|
writeln!(arquivo, " Estes CPFs estão no Prosoft mas não aparecem no DMED")?;
|
||||||
|
writeln!(arquivo, "{}", "-".repeat(120))?;
|
||||||
|
writeln!(arquivo, "{:<15} {:>15}", "CPF", "Valor (R$)")?;
|
||||||
|
writeln!(arquivo, "{}", "-".repeat(120))?;
|
||||||
|
|
||||||
|
for cpf in &self.cpfs_so_xls {
|
||||||
|
writeln!(arquivo, "{:<15} {:>15.2}",
|
||||||
|
cpf.cpf,
|
||||||
|
cpf.valor)?;
|
||||||
|
}
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
writeln!(arquivo, "{}", "=".repeat(120))?;
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(arquivo)?;
|
||||||
|
writeln!(arquivo, "Fim do relatório")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-9
@@ -2,6 +2,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
pub mod xls;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct Pessoa {
|
pub struct Pessoa {
|
||||||
pub cpf: String,
|
pub cpf: String,
|
||||||
@@ -35,10 +37,19 @@ impl Pessoa {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Processa um arquivo DMED e retorna um vetor de pessoas
|
/// Processa um arquivo DMED e retorna um vetor de pessoas
|
||||||
|
/// Tenta ler em UTF-8, se falhar tenta Windows-1252 (comum em arquivos brasileiros)
|
||||||
pub fn processar_arquivo(caminho: &str) -> io::Result<Vec<Pessoa>> {
|
pub fn processar_arquivo(caminho: &str) -> io::Result<Vec<Pessoa>> {
|
||||||
let conteudo = fs::read_to_string(caminho)?;
|
// Primeiro tenta UTF-8
|
||||||
|
match fs::read_to_string(caminho) {
|
||||||
|
Ok(conteudo) => processar_conteudo(&conteudo),
|
||||||
|
Err(_) => {
|
||||||
|
// Se falhar, tenta ler como bytes e converter de Windows-1252
|
||||||
|
let bytes = fs::read(caminho)?;
|
||||||
|
let (conteudo, _, _) = encoding_rs::WINDOWS_1252.decode(&bytes);
|
||||||
processar_conteudo(&conteudo)
|
processar_conteudo(&conteudo)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Processa o conteúdo de um arquivo DMED (útil para testes ou strings)
|
/// Processa o conteúdo de um arquivo DMED (útil para testes ou strings)
|
||||||
pub fn processar_conteudo(conteudo: &str) -> io::Result<Vec<Pessoa>> {
|
pub fn processar_conteudo(conteudo: &str) -> io::Result<Vec<Pessoa>> {
|
||||||
@@ -77,11 +88,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pessoa_from_line() {
|
fn test_pessoa_from_line() {
|
||||||
let linha = "RPPSS|00276808860|MARCO ANTONIO MACHADO|2200000|";
|
let linha = "RPPSS|00000000000|DR ALGUEM|2200000|";
|
||||||
let pessoa = Pessoa::from_line(linha).unwrap();
|
let pessoa = Pessoa::from_line(linha).unwrap();
|
||||||
|
|
||||||
assert_eq!(pessoa.cpf, "00276808860");
|
assert_eq!(pessoa.cpf, "00000000000");
|
||||||
assert_eq!(pessoa.nome, "MARCO ANTONIO MACHADO");
|
assert_eq!(pessoa.nome, "DR ALGUEM");
|
||||||
assert_eq!(pessoa.valor, 22000.0);
|
assert_eq!(pessoa.valor, 22000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,16 +105,16 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_processar_conteudo() {
|
fn test_processar_conteudo() {
|
||||||
let conteudo = r#"Dmed|2026|2025|N|||
|
let conteudo = r#"Dmed|2026|2025|N|||
|
||||||
RESPO|21681939835|Anelisa Pirola Chalegre|17|35316200|205||anelisa@edegarpirolacontabilidade.com.br|
|
RESPO|00000000000|Funcionario do ano|17|00000000|205||fun@cionario.com.br|
|
||||||
DECPJ|03364887000195|CLINICA DE CIRURGIA PLASTICA DR VIDAL LTDA|1|||01877281867|N|||
|
DECPJ|00000000000000|CLINICA DE CIRURGIA PLASTICA|1|||00000000000|N|||
|
||||||
PSS|
|
PSS|
|
||||||
RPPSS|00276808860|MARCO ANTONIO MACHADO|2200000|
|
RPPSS|00000000000|DR ALGUEM|2200000|
|
||||||
RPPSS|00633999032|ELISANDRA BARBOSA TORRES|600000|
|
RPPSS|00000000000|SOCIO DO ALGUEM|600000|
|
||||||
FIMDmed|"#;
|
FIMDmed|"#;
|
||||||
|
|
||||||
let pessoas = processar_conteudo(conteudo).unwrap();
|
let pessoas = processar_conteudo(conteudo).unwrap();
|
||||||
assert_eq!(pessoas.len(), 2);
|
assert_eq!(pessoas.len(), 2);
|
||||||
assert_eq!(pessoas[0].cpf, "00276808860");
|
assert_eq!(pessoas[0].cpf, "00000000000");
|
||||||
assert_eq!(pessoas[1].valor, 6000.0);
|
assert_eq!(pessoas[1].valor, 6000.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
use calamine::{Reader, Xlsx, Xls, open_workbook, Data};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io;
|
||||||
|
use crate::Pessoa;
|
||||||
|
|
||||||
|
/// Processa um arquivo XLS/XLSX do Prosoft e retorna pessoas agrupadas por CPF
|
||||||
|
/// Tenta primeiro XLSX (mesmo com extensão .xls), depois XLS se falhar
|
||||||
|
pub fn processar_xls(caminho: &str) -> io::Result<Vec<Pessoa>> {
|
||||||
|
// Tenta abrir como XLSX primeiro (muitos sistemas exportam XLSX com extensão .xls)
|
||||||
|
let planilha = match open_workbook::<Xlsx<_>, _>(caminho) {
|
||||||
|
Ok(mut workbook) => {
|
||||||
|
workbook.worksheet_range_at(0)
|
||||||
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Planilha não encontrada"))?
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// Se falhar, tenta abrir como XLS
|
||||||
|
let mut workbook: Xls<_> = open_workbook(caminho)
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData,
|
||||||
|
format!("Erro ao abrir arquivo: {}. Certifique-se que é um arquivo Excel válido (.xls ou .xlsx).", e)))?;
|
||||||
|
|
||||||
|
workbook.worksheet_range_at(0)
|
||||||
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Planilha não encontrada"))?
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// HashMap para agrupar valores por CPF
|
||||||
|
let mut valores_por_cpf: HashMap<String, (String, f64)> = HashMap::new();
|
||||||
|
|
||||||
|
// Processa linhas (pula as 3 primeiras + 1 de cabeçalho = começa na linha 4, índice 3)
|
||||||
|
let linhas: Vec<_> = planilha.rows().collect();
|
||||||
|
|
||||||
|
for (idx, linha) in linhas.iter().enumerate() {
|
||||||
|
// Pula as 4 primeiras linhas (0, 1, 2 = identificação, 3 = cabeçalho)
|
||||||
|
if idx < 4 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coluna E (índice 4) = CPF/CNPJ
|
||||||
|
// Coluna H (índice 7) = Valor
|
||||||
|
if linha.len() > 7 {
|
||||||
|
let cpf_cnpj = extrair_texto(&linha[4]);
|
||||||
|
let valor = extrair_valor(&linha[7]);
|
||||||
|
|
||||||
|
// Limpa CPF/CNPJ removendo . - /
|
||||||
|
let documento_limpo = limpar_documento(&cpf_cnpj);
|
||||||
|
|
||||||
|
// Verifica se é CPF (11 caracteres após limpeza)
|
||||||
|
if documento_limpo.len() == 11 {
|
||||||
|
// Acumula valores para o mesmo CPF
|
||||||
|
valores_por_cpf
|
||||||
|
.entry(documento_limpo.clone())
|
||||||
|
.and_modify(|(_, v)| *v += valor)
|
||||||
|
.or_insert((cpf_cnpj.clone(), valor));
|
||||||
|
}
|
||||||
|
// CNPJ tem 14 caracteres - descartamos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converte HashMap em Vec<Pessoa>
|
||||||
|
let mut pessoas: Vec<Pessoa> = valores_por_cpf
|
||||||
|
.into_iter()
|
||||||
|
.map(|(cpf, (_original, valor))| Pessoa {
|
||||||
|
cpf,
|
||||||
|
nome: String::new(), // XLS não tem nome, deixa vazio
|
||||||
|
valor,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Ordena por CPF para facilitar comparação
|
||||||
|
pessoas.sort_by(|a, b| a.cpf.cmp(&b.cpf));
|
||||||
|
|
||||||
|
Ok(pessoas)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extrai texto de uma célula do Excel
|
||||||
|
fn extrair_texto(celula: &Data) -> String {
|
||||||
|
match celula {
|
||||||
|
Data::String(s) => s.clone(),
|
||||||
|
Data::Float(f) => format!("{:.0}", f),
|
||||||
|
Data::Int(i) => i.to_string(),
|
||||||
|
_ => String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extrai valor numérico de uma célula do Excel
|
||||||
|
fn extrair_valor(celula: &Data) -> f64 {
|
||||||
|
match celula {
|
||||||
|
Data::Float(f) => *f,
|
||||||
|
Data::Int(i) => *i as f64,
|
||||||
|
Data::String(s) => {
|
||||||
|
// Tenta converter string para número (remove separadores)
|
||||||
|
s.replace(".", "")
|
||||||
|
.replace(",", ".")
|
||||||
|
.parse::<f64>()
|
||||||
|
.unwrap_or(0.0)
|
||||||
|
}
|
||||||
|
_ => 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove caracteres especiais do CPF/CNPJ
|
||||||
|
fn limpar_documento(doc: &str) -> String {
|
||||||
|
doc.chars()
|
||||||
|
.filter(|c| c.is_ascii_digit())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_limpar_documento() {
|
||||||
|
assert_eq!(limpar_documento("123.456.789-01"), "12345678901");
|
||||||
|
assert_eq!(limpar_documento("12.345.678/0001-90"), "12345678000190");
|
||||||
|
assert_eq!(limpar_documento("00276808860"), "00276808860");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extrair_valor() {
|
||||||
|
assert_eq!(extrair_valor(&Data::Float(1500.50)), 1500.50);
|
||||||
|
assert_eq!(extrair_valor(&Data::Int(1000)), 1000.0);
|
||||||
|
assert_eq!(extrair_valor(&Data::String("1.500,50".to_string())), 1500.50);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user