#include #include #include #include #include #include #include "../ca/pconana.h" #include "../ca/movana.h" #include "../ca/rmovana.h" #include "../ca/calib01.h" #include "../ca/calib02.h" #include "ps1001200a.h" #define DESCR_TESTATA TR("Movimento di budget importato") #define DESCR_RIGA TR("Riga movimento di budget importato") //--------------------------------- // TAutomask //--------------------------------- class TBudget_Import_mask : public TAutomask { protected: bool on_field_event(TOperable_field& o, TField_event e, long jolly); bool select_file_ext(const char* ext, TFilename& filename); public: TBudget_Import_mask(); virtual ~TBudget_Import_mask(){}; }; TBudget_Import_mask::TBudget_Import_mask() :TAutomask ("ps1001200a") { } bool TBudget_Import_mask::select_file_ext(const char* ext, TFilename& filename) { TArray_sheet as(-1, -1, 72, 20, TR("Selezione file"), "File@48"); TFilename path = get(F_PATH); path.add("*"); path.ext(ext); list_files(path, as.rows_array()); TFilename name; FOR_EACH_ARRAY_ROW(as.rows_array(), i, row) { name = *row; *row = name.name(); } bool ok = as.run() == K_ENTER; if (ok) { filename = as.row(as.selected()); } return ok; } bool TBudget_Import_mask::on_field_event(TOperable_field& f, TField_event e, long jolly) { switch (f.dlg()) { //giochetto per avere la lista dei files validi nella directory di trasferimento! case F_NAMEFILE: if (e == fe_button) { TFilename name; if (select_file_ext("csv", name)) f.set(name); if (f.get() == get(F_LASTFILE)) return error_box(FR("Questo file l'hai gia' importato!\nCaro %s ti consiglio di NON proseguire!"), (const char*)user()); } break; case F_KILLOLD: if (e == fe_close) { if (f.get().full()) { return yesno_box(FR("Ehi %s, hai scelto di eliminare tutti i record di tipo Preventivo\nnei files dei movimenti e dei saldi!\nSei sicuro di voler proseguire?"), (const char*)user()); } } break; default: break; } return true; } //-------------------------------- // TFile_text //-------------------------------- class TBudget_Import_file: public TFile_text { protected: virtual void validate(TCursor& cur,TRecord_text &rec, TToken_string &val, TString& str); public: TBudget_Import_file(const TString& file_name); virtual ~TBudget_Import_file() { } }; TBudget_Import_file::TBudget_Import_file(const TString& file_name) : TFile_text(file_name, "ps1001200.ini") { } void TBudget_Import_file::validate(TCursor& cur,TRecord_text &rec, TToken_string &s, TString& str) { const TString code(s.get(0)); TString valore = str; if (code == "_UPPERCASE") { valore.upper(); } else NFCHECK("Macro non definita: %s", (const char *)code); str = valore; } //---------------------------------- // TSkeleton_application //---------------------------------- class TBudget_Import : public TSkeleton_application { TBudget_Import_mask* _msk; TBudget_Import_file* _trasfile; TConfig* _configfile; TRelation* _relmovana; TString _lastfile; TString4 _codcaus; virtual const char * extra_modules() const {return "ba";} protected: void mask2ini(); void ini2mask(); bool transfer(); void elabora_input(const TFilename& file, TLog_report& log); void movana_killer(const TAssoc_array& elenco, const bool kill_all); void transfer_movimento(const TArray& righe, TLog_report& log); const TString& ana2bill(const TString& contone) const; const TDate str2date(const TString& stringdata); long get_next_key(); public: virtual bool create(); virtual bool destroy(); virtual void main_loop(); TBudget_Import() {} }; long TBudget_Import::get_next_key() { TLocalisamfile& lfile = _relmovana->lfile(); long numreg = 1L ; if (!lfile.empty()) { lfile.zero() ; lfile.setkey(1) ; lfile.read(_isgteq); if (lfile.good()) //se e' tutto ok,si posiziona sull'ultimo record lfile.last(); if (lfile.good()) numreg += lfile.get_long(MOVANA_NUMREG); } return numreg; } const TString& TBudget_Import::ana2bill(const TString& contone) const { TToken_string key; key = contone; key.add("1"); const TRectype& rec = cache().get(LF_PANAPDC, key); if (rec.empty()) return EMPTY_STRING; TString& tmp = get_tmp_string(); tmp.format("%03d%03d%06ld", rec.get_int("GRUPPO"), rec.get_int("CONTO"), rec.get_long("SOTTOCONTO")); return tmp; } const TDate TBudget_Import::str2date(const TString& stringdata) { const int stringdata_len = stringdata.len(); TString4 gg = stringdata.left(2); TString4 mm = stringdata.mid(3,2); TString4 yy; if (stringdata_len == 8) //se la data ha 8 caratteri (es. 12/06/08)... { yy = stringdata.right(2); yy.insert("20", 0); } else //se invece ne ha 10 (es.12/06/2008)... yy = stringdata.right(4); const TDate data(atoi(gg),atoi(mm),atoi(yy)); return data; } //metodo di bassa lega che scrive veramente i movimenti analitici sui file movana e rmovana void TBudget_Import::transfer_movimento(const TArray& righe, TLog_report& log) { const TRecord_text& curr = (const TRecord_text&)righe[0]; //TESTATA //------- //formato definitivo del tracciato record dei CSV da importare (vedi anche il file crpa1.ini): //codice commessa|fase|conto|importo|inizio cms|fine cms TString codcms = curr.get(0); codcms.replace('_', '/'); //controllo commesse const TRectype& rec_cms = cache().get(LF_COMMESSE, codcms); if (rec_cms.empty()) { TString errore; errore << "La commessa " << codcms << " non esiste"; log.log(2, errore); } //Controllo del formato date! //Preso atto che il nostro invincibile fuhrer cambia formato date ad ogni importazione, proviamo a.. //..rendere piu' intelligente il povero programma const TDate dataini = str2date(curr.get(4)); const int annoes = dataini.year(); const TDate datafine = str2date(curr.get(5)); //cerca il primo posto libero in movana long numreg = get_next_key(); //crea il movimento di prima nota da aggiungere a movana TAnal_mov movana(numreg); movana.zero(); //azzera per sicurezza //sbatte i dati nei campi movana.put(MOVANA_NUMREG, numreg); movana.put(MOVANA_ANNOES, annoes); movana.put(MOVANA_DATAREG, dataini); movana.put(MOVANA_DATACOMP, dataini); movana.put(MOVANA_DATAFCOMP, datafine); movana.put(MOVANA_DESCR, DESCR_TESTATA); movana.put(MOVANA_CODCAUS, _codcaus); movana.put(MOVANA_TIPOMOV, 'P'); //RIGHE //----- TImporto totdoc; for (int i = 0; i < righe.items(); i++) { const TRecord_text& curr = (const TRecord_text&)righe[i]; const TString codconto = curr.get(2); const real soldini = curr.get(3); //controllo fase e/o sede TString fase, sede; if (_msk->get(F_CRPA_DIN)[0] == 'C') { //controllo fasi (ricordiamo che le fasi sono legate alle commesse nel crpa) fase = curr.get(1); if (fase.full()) { TToken_string chiave = codcms; chiave.add(fase); const TRectype& rec_fase = cache().get(LF_FASI, chiave); if (rec_fase.empty()) { TString errore; errore << "La fase " << fase << " non esiste legata alla commessa " << codcms << ""; log.log(2, errore); } } //fase.full... } else //if(_msk->get_char(F_CRPA_DIN... { sede = curr.get(1); if (sede.full()) { const TRectype& rec_sede = cache().get(LF_CDC, sede); if (rec_sede.empty()) { TString errore; errore << "La sede " << sede << " non esiste"; log.log(2, errore); } } } //controllo conti TString query; TString tmp_codconto; tmp_codconto << "'" << codconto << "'"; query << "USE PCONANA KEY 1 \n"; query << "SELECT CODCONTO[10,15]=" << tmp_codconto; TISAM_recordset pconana(query); const TRecnotype items = pconana.items(); if (items <= 0) { TString errore; errore << "Il sottoconto " << codconto << " non esiste"; log.log(2, errore); } if (items > 1) { TString errore; errore << "Esiste piu' di un sottoconto " << codconto ; log.log(1, errore); } pconana.move_last(); //si posiziona sul record corretto const TString& contone = pconana.get(PCONANA_CODCONTO).as_string(); const TString contcon = ana2bill(contone); if (contcon.empty()) { TString errore; errore << "Il conto analitico " << contone << " non corrisponde ad alcun conto contabile"; log.log(2, errore); } //deve stabilire se la sezione e' D o A in base all'indbil del conto analitico TAnal_bill anazio(contone); const int indbil = anazio.indicatore_bilancio(); char sezione = 'D'; if (indbil > 0) sezione = (indbil == 1 || indbil == 3) ? 'D' : 'A'; else { TString errore; errore << "Il conto analitico " << contone << " non ha un indicatore di bilancio"; log.log(2, errore); } //incrementa l'importo totlae del documento in base a quanto trovato su questa riga TImporto imp_riga(sezione, soldini); totdoc += imp_riga; //controllo sulle date const TDate curr_dataini = str2date(curr.get(4)); const TDate curr_datafine = str2date(curr.get(5)); if (curr_dataini != dataini) { TString errore; errore << "La commessa " << codcms << " ha movimenti con date inizio competenza incongruenti"; log.log(1, errore); } if (curr_datafine != datafine) { TString errore; errore << "La commessa " << codcms << " ha movimenti con date fine competenza incongruenti"; log.log(1, errore); } TRectype& rec_rmovana = movana.new_row(); rec_rmovana.put(RMOVANA_ANNOES, annoes); rec_rmovana.put(RMOVANA_NUMREG, numreg); rec_rmovana.put(RMOVANA_NUMRIG, i+1); rec_rmovana.put(RMOVANA_SEZIONE, sezione); rec_rmovana.put(RMOVANA_DATACOMP, dataini); rec_rmovana.put(RMOVANA_CODCMS, codcms); rec_rmovana.put(RMOVANA_CODFASE, fase); rec_rmovana.put(RMOVANA_CODCCOSTO, sede); rec_rmovana.put(RMOVANA_CODCONTO, contcon); rec_rmovana.put(RMOVANA_DESCR, DESCR_RIGA); rec_rmovana.put(RMOVANA_IMPORTO, soldini); } //scrive testata e riga e aggiorna i saldi (write() magggica!) movana.put(MOVANA_TOTDOC, totdoc.valore()); movana.put(MOVANA_SEZIONE, totdoc.sezione()); TLocalisamfile testate(LF_MOVANA); movana.write(testate); } //metodo di eliminazione dei movana void TBudget_Import::movana_killer(const TAssoc_array& elenco, const bool kill_all) { //accoppa i movimenti preventivi che hanno queste commesse TRelation relmovana(LF_MOVANA); TCursor curmovana(&relmovana, "TIPOMOV=='P'"); const TRectype& recmovana = relmovana.curr(); const long nrec = curmovana.items(); curmovana.freeze(); TProgind pi(nrec, "Eliminazione movimenti preventivi in corso..."); TLocalisamfile movana(LF_MOVANA); //questo serve alla remove() for (curmovana = 0; curmovana.pos() < nrec; ++curmovana) { pi.addstatus(1); //si possono uccidere solo i movimenti importati in automatico const TString& descr = curmovana.curr().get(MOVANA_DESCR); if (descr == DESCR_TESTATA) { TAnal_mov anal_kill(curmovana.curr()); const int original_rows = anal_kill.rows(); int residual_rows = kill_all ? 0 : original_rows; //necessario perche', in caso di kill_all=true (genocidio) //se e' un diradamento selettivo in base al codice commessa... if (!kill_all) { TString codcms; for (int i = original_rows; i > 0; i--) { codcms = anal_kill.body().row(i).get(RMOVANA_CODCMS); if (elenco.is_key(codcms)) { anal_kill.body().destroy_row(i, true); residual_rows--; } } } //controlla quante righe sono rimaste: se nessuna->accoppa il movimento, senno' lo riscrive con le.. //..righe compattate (aggiunto il caso del movimento senza righe) if (residual_rows < original_rows || original_rows == 0) { if (residual_rows == 0) anal_kill.remove(movana); //la remove() accoppa anche le righe collegate else anal_kill.rewrite(movana); //non deve eliminare il movimento ma solo riscriverlo compattato.. } //..senza le righe eliminate } //if(descr==... } //for(curmovana=0... } //metodo di medio livello che gestisce l'ordine delle chiamate dei metodi per l'elaborazione dei record void TBudget_Import::elabora_input(const TFilename& file, TLog_report& log) { //array con le commesse da accoppare TAssoc_array movimenti, commesse; //per cominciare apre il file di input const long dimension = fsize(file); if (dimension > 0) { _trasfile->open(file,'r'); TRecord_text curr; TToken_string key; TString codcms; TProgind pi(dimension, TR("Lettura file di input in corso...")); //crea un assoc_array di array (e la madona!!!) in cui la chiave e' il codice commessa|dataini|datafine.. //..e gli elementi sono array con le righe del file di input (con la stessa key ma fase\conto etc. diversi) while (_trasfile->read(curr) == NOERR) { pi.setstatus((size_t)_trasfile->read_file()->tellg()); codcms = curr.get(0); codcms.replace('_', '/'); key = codcms; //date const TString16 str_dataini = curr.get(4); key.add(str_dataini); const TString16 str_datafine = curr.get(5); key.add(str_datafine); TArray* righe = (TArray*)movimenti.objptr(key); if (righe == NULL) { righe = new TArray; movimenti.add(key, righe); commesse.add(codcms); //commesse e' l'assoc_array di supporto che si usa nella cancellazione } righe->add(curr); } //per finire chiude il file di input che sara' poi riaperto per la vera importazione _trasfile->close(); } //accoppa i movana con commesse da importare movana_killer(commesse, false); //la causale!!! _codcaus = _msk->get(F_COD_CAUS); //scrittura dei record memorizzati sui files di testata e righe dei movimenti analitici const int items = movimenti.items(); TProgind pi(items, "Generazione movimenti preventivi in corso..."); FOR_EACH_ASSOC_OBJECT(movimenti, obj, key, itm) { pi.addstatus(1); TArray* righe = (TArray*)itm; transfer_movimento(*righe, log); } } bool TBudget_Import::transfer() { //azzeramento preventivo di TUTTI i movimenti, righe e saldi di tipo P if (_msk->get_bool(F_KILLOLD)) { TAssoc_array assoc_civetta; movana_killer(assoc_civetta, true); } //file da trasferire //costruire il nome del file con path TFilename file = _msk->get(F_PATH); file.add(_msk->get(F_NAMEFILE)); file.ext("csv"); _trasfile = new TBudget_Import_file(file); int err = NOERR; TRecord_text curr; //log degli errori di trasferimento con soppressione degli errori ripetuti! TLog_report log("Errori"); log.kill_duplicates(); //metodo che fa veramente la eliminazione e la scrittura elabora_input(file, log); delete _trasfile; log.preview(); _lastfile = _msk->get(F_NAMEFILE); return true; } void TBudget_Import::mask2ini() { //aggiorna l'ultimo file inviato su msk e ini _msk->set(F_LASTFILE, _lastfile); _configfile->set_paragraph("BUDGET"); _configfile->set("PATH", _msk->get(F_PATH)); _configfile->set("LASTFILE", _lastfile); _msk->set(F_COD_CAUS, _codcaus); _configfile->set("CODCAUS", _msk->get(F_COD_CAUS)); _configfile->set("CRPADIN", _msk->get(F_CRPA_DIN)); } void TBudget_Import::ini2mask() { //carica i parametri del file di configurazione _configfile->set_paragraph("BUDGET"); _msk->set(F_PATH, _configfile->get("PATH")); _lastfile = _configfile->get("LASTFILE"); _msk->set(F_LASTFILE, _lastfile); _codcaus = _configfile->get("CODCAUS"); _msk->set(F_COD_CAUS, _codcaus); _msk->set(F_CRPA_DIN, _configfile->get("CRPADIN")); } bool TBudget_Import::create() { _configfile = new TConfig("ps1001200conf.ini"); _msk = new TBudget_Import_mask(); open_files(LF_MOVANA, LF_RMOVANA, LF_PCONANA, 0); _relmovana = new TRelation(LF_MOVANA); return TSkeleton_application::create(); } bool TBudget_Import::destroy() { delete _msk; delete _configfile; return TApplication::destroy(); } void TBudget_Import::main_loop() { KEY tasto; ini2mask(); tasto = _msk->run(); if (tasto == K_ENTER) { if (transfer()) message_box(TR("Importazione movimenti completata")); } mask2ini(); } int ps1001200(int argc, char* argv[]) { TBudget_Import a; a.run(argc, argv, TR("Importazione movimenti di budget")); return true; }