#include #include #include #include #include #include #include #include "../cg/cglib01.h" #include "../ca/calib01.h" #include "../ca/calib02.h" #include "pd6342.h" #include "pd6342300a.h" #include "../ca/movana.h" #include "../ca/rmovana.h" #include "../ca/rip.h" #include "../ca/rrip.h" //-------------------------------------------------------------------- // MASCHERA //-------------------------------------------------------------------- class TRib_movanal_msk : public TAnal_report_mask { protected: virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); public: TRib_movanal_msk(); }; bool TRib_movanal_msk::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case F_DATAINI: case F_DATAFIN: if (e == fe_close) { const int anno = get_int(F_ANNO); TEsercizi_contabili esc; //..le date devono essere incluse nell'esercizio selezionato! const TDate data(o.get()); if (!data.empty() && esc.date2esc(data) != anno) return error_box(TR("La data deve appartenere all'anno selezionato")); } break; default: break; } return TAnal_report_mask::on_field_event(o, e, jolly); } TRib_movanal_msk::TRib_movanal_msk() : TAnal_report_mask("pd6342300a") { } class TCSV_recset : public TCSV_recordset { public: // bool save_as_html(const char* path); TCSV_recset(TAssoc_array & calc); }; TCSV_recset::TCSV_recset(TAssoc_array & calc) : TCSV_recordset("CSV(;)\n") { set_separator(';'); TAssoc_array kcol; TString_array rowkeys; TString_array colkeys; FOR_EACH_ASSOC_OBJECT(calc, obj, key, item) { rowkeys.add(key); TAssoc_array * row = (TAssoc_array *) item; FOR_EACH_ASSOC_OBJECT((*row), rowobj, colkey, rowitem) kcol.add(colkey); } rowkeys.sort(); kcol.get_keys(colkeys); colkeys.sort(); destroy_column(); const int ncols = colkeys.items(); const int nrows = rowkeys.items(); TString colname(20); create_column("Key"); new_rec(""); for (int i= 1; i <= ncols; i++) { colname = colkeys.row(i - 1); colname.replace(',', ' '); colname.strip_double_spaces(); create_column(colname, _realfld); set(i, colname); } for (int i= 0; i < nrows; i++) { new_rec(""); TString rowkey = rowkeys.row(i); TAssoc_array * row = (TAssoc_array *) calc.objptr(rowkey); rowkey.replace(',', ' '); rowkey.strip_double_spaces(); set(0, rowkey); if (row != NULL) { for (int j= 1; j <= ncols; j++) { const real * val = (const real *) row->objptr(colkeys.row(j - 1)); if (val != NULL) set(j, *val); } } } } //-------------------------------------------------------------------- // APPLICAZIONE //-------------------------------------------------------------------- class TRib_movanal_app : public TSkeleton_application { TCache_ripartizioni _cache_rip; bool _definitivo; TAssoc_array _calc; TArray _excl; protected: virtual const char* extra_modules() const { return "ca"; } virtual void main_loop(); virtual bool create(); virtual bool destroy(); bool elabora_righe(TAnal_mov& anal_mov, TRecord_array& input_rows); bool ripartizione(const TAnal_ripartizioni_batch& rrip, const TRectype& rec, const TToken_string& path_item, TRecord_array& output_rows, TArray & output_paths); bool pareggio(TAnal_mov& anal_mov, const TAnal_ripartizioni_batch& rrip, const TRectype& rec, const TToken_string& path, TRecord_array& output_rows, TArray & output_paths); public: bool elabora_movimento(TAnal_mov& anal_mov, const bool esplodi); TSystemisamfile& movana() { return *(TSystemisamfile*)_excl.objptr(LF_MOVANA); } }; bool TRib_movanal_app::pareggio(TAnal_mov& anal_mov, const TAnal_ripartizioni_batch& rrip, const TRectype& rec, const TToken_string& path, TRecord_array& output_rows, TArray & output_paths) { bool ho_pareggiato = false; TImporto totdoc(anal_mov.get_char(MOVANA_SEZIONE), anal_mov.get_real(MOVANA_TOTDOC)); const TImporto imp_riga(rec.get_char(RMOVANA_SEZIONE), rec.get_real(RMOVANA_IMPORTO)); totdoc -= imp_riga; totdoc.normalize(); anal_mov.put(MOVANA_TOTDOC, totdoc.valore()); anal_mov.put(MOVANA_SEZIONE, totdoc.sezione()); //aggiunge la riga originale alle righe di output (e' un pareggio) const int original_nriga = output_rows.rows() + 1; TRectype* newrec = new TRectype(rec); newrec->put(RMOVANA_NUMRIG, original_nriga); output_rows.add_row(newrec); //swappa la sezione e la manda al ripartitore in modo da generare righe con la sezione rovesciata rispetto a quella.. //..originale ed ottenere cosi' il pareggio TRectype swaprec(rec); swaprec.put(RMOVANA_SEZIONE, imp_riga.sezione() == 'D' ? 'A' : 'D'); ho_pareggiato = ripartizione(rrip, swaprec, path, output_rows, output_paths); //elimina il codcontoori da tutte le righe aggiunte per il pareggio for (int i = output_rows.rows(); i > original_nriga; i--) output_rows.row(i, false).zero(RMOVANA_CODCONTORI); return ho_pareggiato; } bool TRib_movanal_app::ripartizione(const TAnal_ripartizioni_batch& rrip, const TRectype& rec, const TToken_string& path_item, TRecord_array& output_rows, TArray& output_paths) { bool ho_ripartito = false; // Importo totale da distribuire arrotondato ai decimali della valuta di conto const real importo = rec.get_real(RMOVANA_IMPORTO); const char sez = rec.get_char(RMOVANA_SEZIONE); TGeneric_distrib distrib(importo, TCurrency::get_firm_dec()); TToken_string rowkey("", ','); rowkey.add(rec.get(RMOVANA_CODCCOSTO)); rowkey.add(rec.get(RMOVANA_CODCMS)); rowkey.add(rec.get(RMOVANA_CODFASE)); TToken_string browkey = rowkey; TAssoc_array* row = (TAssoc_array*)_calc.objptr(rowkey); if (row == NULL) _calc.add(rowkey, row = new TAssoc_array); real* value = (real*)row->objptr(browkey); if (value == NULL) row->add(rowkey, value = new real); *value = *value + (sez == 'D' ? importo : -importo); browkey.add("+"); row = (TAssoc_array*)_calc.objptr(browkey); if (row == NULL) _calc.add(browkey, row = new TAssoc_array); value = (real *) row->objptr(rowkey); if (value == NULL) row->add(rowkey, value = new real); *value = *value + (sez == 'D' ? -importo : importo); // browkey.rtrim(2); // Calcolo tutte le percentuali da ripartire const int righe_ripartizione = rrip.rows(); for (int i = 1; i <= righe_ripartizione; i++) { const real importanza_riga = rrip[i].get_real(RRIP_RIPARTO); distrib.add(importanza_riga); } for (int i = 1; i <= righe_ripartizione; i++) { const real imp = distrib.get(); // Legge la quota da distribuire if (!imp.is_zero()) { TRectype nuorec(rec); //poi copia i valori dei campi cdc,cms,fsc,in quelli di tipo ori (nello stesso record) if (rec.get(RMOVANA_CODCCORI).blank() && rec.get(RMOVANA_CODCMSORI).blank() && rec.get(RMOVANA_CODFASEORI).blank()) // RMOVANA_CODCONTORI è vuoto nel caso di pareggio { ca_copia_campo(rec, RMOVANA_CODCCOSTO, nuorec, RMOVANA_CODCCORI); ca_copia_campo(rec, RMOVANA_CODCMS, nuorec, RMOVANA_CODCMSORI); ca_copia_campo(rec, RMOVANA_CODFASE, nuorec, RMOVANA_CODFASEORI); ca_copia_campo(rec, RMOVANA_CODCONTO, nuorec, RMOVANA_CODCONTORI); } else { ca_copia_campo(rec, RMOVANA_CODCCORI, nuorec, RMOVANA_CODCCORI); ca_copia_campo(rec, RMOVANA_CODCCORI, nuorec, RMOVANA_CODCMSORI); ca_copia_campo(rec, RMOVANA_CODCCORI, nuorec, RMOVANA_CODFASEORI); ca_copia_campo(rec, RMOVANA_CODCCORI, nuorec, RMOVANA_CODCONTORI); } //e mette nei campi std i valori che trova nelle righe ripartizione ca_copia_campo(rrip[i], RRIP_CODCOSTO, nuorec, RMOVANA_CODCCOSTO); ca_copia_campo(rrip[i], RRIP_CODCMS, nuorec, RMOVANA_CODCMS); ca_copia_campo(rrip[i], RRIP_CODFASE, nuorec, RMOVANA_CODFASE); ca_copia_campo(rrip[i], RRIP_CODCONTO, nuorec, RMOVANA_CODCONTO); TToken_string ripkey("", ','); ripkey.add(nuorec.get(RMOVANA_CODCCOSTO)); ripkey.add(nuorec.get(RMOVANA_CODCMS)); ripkey.add(nuorec.get(RMOVANA_CODFASE)); // ripkey.add(nuorec.get(RMOVANA_CODCONTO)); const int rows = output_rows.rows(); int r = -1; for (int j = 1; r < 0 && j <= rows; j++) { if (ca_can_pack_rows(nuorec, output_rows[j])) r = j; } if (r < 0) { nuorec.put(RMOVANA_NUMRIG, rows + 1); nuorec.put(RMOVANA_IMPORTO, imp); //e la mette nella nuova riga output_rows.add_row(nuorec); TToken_string * rowpath = (TToken_string *) output_paths.objptr(r); if (rowpath == NULL) output_paths.add(rowpath = new TToken_string, r); rowpath->add(path_item); } else { output_rows[r].add(RMOVANA_IMPORTO, imp); } const bool subtract = rowkey != ripkey; real * value = (real *) row->objptr(ripkey); if (value == NULL) row->add(ripkey, value = new real); *value = *value + (sez == 'D' ? imp : -imp); if ( subtract) { row = (TAssoc_array *)_calc.objptr(ripkey); if (row == NULL) _calc.add(ripkey, row = new TAssoc_array); real * value = (real *) row->objptr(ripkey); if (value == NULL) row->add(ripkey, value = new real); *value = *value + (sez == 'D' ? -imp : imp); } ho_ripartito = true; } //if(imp!=ZERO)... } //for(i=1;i<=righe_ripartizione... return ho_ripartito; } bool TRib_movanal_app::elabora_righe(TAnal_mov& anal_mov, TRecord_array& input_rows) { bool ho_cambiato_qualchecosa = false; const int annoes = anal_mov.get_int(MOVANA_ANNOES); const char tipomov = anal_mov.get_char(MOVANA_TIPOMOV); TRecord_array output_rows = input_rows; output_rows.destroy_rows(); TArray input_paths; TArray output_paths; for (int loop = 1; loop <= 20; loop++) { bool modified = false; const int nrows = input_rows.rows(); for (int r = 1; r <= nrows; r++) { const TRectype& rec = input_rows.row(r); TAnal_bill zio(rec); const int rmovana_indbil = zio.indicatore_bilancio(); //ripartizione batch: passa il conto perche' per prima cosa provera' una ripartizione di tipo 'P' con chiave 3; se non.. //..ci riuscira', provera' da solo (metodi della TCache_ripartizioni) le ripartizioni di tipo 'B' con chiave 4. const TAnal_ripartizioni_batch& rrip = _cache_rip.righe(zio, annoes, rmovana_indbil, tipomov); if (input_paths.objptr(r) == NULL) input_paths.add(new TToken_string, r); TToken_string & input_path = (TToken_string &) input_paths[r]; const char tiporip = rrip.tiporip(); //ci sono righe di ripartizione const int righe_ripartizione = rrip.rows(); bool ripartisci = righe_ripartizione > 0; //se ci sono righe di ripartizione/pareggio si va a ripartire! TToken_string path_item("", ','); path_item.add(rec.get(RMOVANA_CODCCOSTO)); path_item.add(rec.get(RMOVANA_CODCMS)); path_item.add(rec.get(RMOVANA_CODFASE)); path_item.add(rec.get(RMOVANA_CODCONTO)); if (ripartisci) ripartisci = input_path.find(path_item) < 0; input_path.add(path_item); if (ripartisci) { if (input_rows.rows() + righe_ripartizione > 999) break; switch (tiporip) { //procedura di ripartizione batch 'B' originale; se tiporip=='P' invece ci vuole il pareggio del movana case 'B': modified |= ripartizione(rrip, rec, path_item, output_rows, output_paths); break; case 'P': modified |= pareggio(anal_mov, rrip, rec, input_path, output_rows, output_paths); break; default: break; } } else //if(ripartisci... nessuna riga di ripartizione->aggiungo la riga input all'output { TRectype* newrec = new TRectype(rec); newrec->put(RMOVANA_NUMRIG, output_rows.rows() + 1); output_rows.add_row(newrec); } } //for(int r=1; r<=nrows... if (modified) { ho_cambiato_qualchecosa = true; input_rows = output_rows; input_paths = output_paths; output_rows.destroy_rows(); output_paths.destroy(); } else break; } return ho_cambiato_qualchecosa; } bool TRib_movanal_app::elabora_movimento(TAnal_mov& anal_mov, const bool esplodi) { TRecord_array& input_rows = anal_mov.body(); //record_array con le righe del mov_anal (INPUT) bool do_rewrite = false; //Per prima cosa prende le righe del movimento su RMOVANA e le ricompatta.. if (esplodi) { //Imploditore //..poi lo riesplode in tutte le righe che possono nascere secondo le regole delle ripartizioni! //Esploditore do_rewrite = elabora_righe(anal_mov, input_rows); if (_definitivo) //se l'elaborazione e' definitiva... { anal_mov.put(MOVANA_BLOCCATO, 'X'); //..mette bloccato = X nella testata del movimento do_rewrite = true; } } else { //Imploditore TRecord_array compact_rows = input_rows; //record array con le righe compattate da creare con la.. compact_rows.destroy_rows(); //..implode_rows(); intanto le azzera per sicurezza do_rewrite = ca_implode_rows(input_rows, compact_rows); for (int r = 1; r <= compact_rows.rows(); r++) { TRectype& rec = compact_rows[r]; rec.zero(RMOVANA_CODCCORI); rec.zero(RMOVANA_CODCMSORI); rec.zero(RMOVANA_CODFASEORI); rec.zero(RMOVANA_CODCONTORI); } if (do_rewrite) { input_rows = compact_rows; // rimette i record compattati negli originali anal_mov.update_totdoc(); //aggiorna il totale movana (necessarip per ripartizioni a pareggio, di sicurezza per le altre) } } return do_rewrite; //se ha elaborato delle righe e/o e' una elaborazione definitiva, riscrive la.. } static bool ripartisci_callback(const TRelation& rel, void* pJolly) { TRib_movanal_app& app = *(TRib_movanal_app*)pJolly; const long numreg = rel.curr().get_long(MOVANA_NUMREG); TAnal_mov anal_mov(numreg); //se va tutto bene riscrive l'intero movimento analitico con conseguente ricalcolo saldi app.elabora_movimento(anal_mov, false); if (app.elabora_movimento(anal_mov, true)) anal_mov.rewrite(app.movana()); return true; } static bool compatta_callback(const TRelation& rel, void* pJolly) { TRib_movanal_app& app = *(TRib_movanal_app*)pJolly; const long numreg = rel.curr().get_long(MOVANA_NUMREG); TAnal_mov anal_mov(numreg); //se va tutto bene riscrive l'intero movimento analitico con conseguente ricalcolo saldi if (app.elabora_movimento(anal_mov, false)) anal_mov.rewrite(app.movana()); return true; } void TRib_movanal_app::main_loop() { TRib_movanal_msk mask; while (mask.run() == K_ENTER) { //deve scandire il file MOVANA con chiave 2 (per data e numero di registrazione) TRelation rel_movana(LF_MOVANA); TRectype darec(LF_MOVANA), arec(LF_MOVANA); const TDate dal = mask.get_date(F_DATAINI); darec.put(MOVANA_DATACOMP, dal); const TDate al = mask.get_date(F_DATAFIN); arec.put(MOVANA_DATACOMP, al); _cache_rip.set_esercizio(mask.get_int(F_ANNO)); const TString& tipo = mask.get(F_CLASSEMOV); TString filter; if (tipo.blank()) filter = "BLOCCATO!=\"X\""; else { if (tipo == "N") filter = "(BLOCCATO!=\"X\")&&(TIPOMOV==\"\")"; else filter = "(BLOCCATO!=\"X\")&&(TIPOMOV!=\"\")"; } TCursor cur_movana(&rel_movana, filter, 2, &darec, &arec); const long items = cur_movana.items(); if (items > 0) { bool run = yesno_box(FR("Si desidera elaborare %ld movimenti?"), items); //e' una compattazione? const bool compattazione = mask.get_bool(F_COMPATTA); //se e' un ripartizione potrebbe essere definitiva! if (!compattazione) { // avvisa l'utente scapestrato che se fa una ripartizione definitiva // blocchera' i movimenti che processa e non potra' piu' tornare indietro _definitivo = mask.get_bool(F_DEFINITIVO); if (run && _definitivo) run = yesno_box(TR("E' stata selezionata l'elaborazione definitiva\nSi desidera proseguire?")); } //Presa la decisione si parte! Tenetevi forte... if (run) { if (compattazione) cur_movana.scan(compatta_callback, this, TR("Compattamento movimenti...")); else cur_movana.scan(ripartisci_callback, this, TR("Ripartizione movimenti...")); TFilename fname(mask.get(F_PATH)); fname.add(mask.get(F_NAME)); if (fname.full()) { TCSV_recset recset(_calc); recset.save_as(fname, fmt_unknown); } } //if(run)... } else message_box(TR("Non ci sono movimenti da elaborare nel periodo selezionato")); } } bool TRib_movanal_app::create() { int lognum[] = { LF_MOVANA, LF_RMOVANA, LF_PCONANA, LF_SALDANA, 0 }; for (int i = 0; lognum[i]; i++) { const int num = lognum[i]; TSystemisamfile* sif = new TSystemisamfile(num); _excl.add(sif, num); if (sif->open(_excllock) != NOERR) return error_box(TR("Il file dei movimenti analitici è gia' in uso")); } return TSkeleton_application::create(); } bool TRib_movanal_app::destroy() { _excl.destroy(); return TSkeleton_application::destroy(); } int pd6342300(int argc, char* argv[]) { TRib_movanal_app app; app.run(argc, argv, TR("Ripartizione movimenti di analitica (Habilita)")); return 0; }