#include #include "../ve/velib.h" #include "../ve/velib04.h" #include "calib01.h" #include "calib02.h" #include "ca3883.h" #include "movana.h" #include "rmovana.h" #include "panapdc.h" #include "pconana.h" #include "commesse.h" #include "fasi.h" #include "saldana.h" //////////////////////////////////////////////////////////////////////////////////////// // Classe per ricavare l'indicatore di bilancio dei conti analitici riclassificati e non //////////////////////////////////////////////////////////////////////////////////////// TObject* TIndbil_cache::key2obj(const char* key) { TString80 conto = key; if (_usepdcc && conto.len() == 12 && real::is_natural(conto)) { TLocalisamfile panapdc(LF_PANAPDC); panapdc.setkey(2); const int gr = atoi(conto.left(3)); const int co = atoi(conto.mid(3,3)); const long so = atol(conto.mid(6,6)); TRectype& panapdrec = panapdc.curr(); int i; for (i = 2; i >= 0; i--) { panapdrec.zero(); panapdrec.put(PANAPDC_GRUPPO, gr); if (i > 0) panapdrec.put(PANAPDC_CONTO, co); if (i == 2) panapdrec.put(PANAPDC_SOTTOCONTO, so); //occhio al prefisso! panapdrec.put(PANAPDC_CODCONTO, _prefix); if (panapdc.read(_isgteq) == NOERR) { bool found = panapdrec.get_int(PANAPDC_GRUPPO) == gr; if (found) found = panapdrec.get_int(PANAPDC_CONTO) == (i > 0 ? co : 0); if (found) found = panapdrec.get_long(PANAPDC_SOTTOCONTO) == (i > 1 ? so : 0L); if (found && _prefix.full()) found = panapdrec.get(PANAPDC_CODCONTO).starts_with(_prefix); if (found) { conto = panapdrec.get(PANAPDC_CODCONTO); if (cache().get(LF_PCONANA, conto).empty()) error_box(FR("Non esiste piu' il conto %s cui era legato %d %d %ld"), (const char*)conto, gr, co, so); else break; } } } //se non trova il corrispondente conto analitico azzera il conto per il prossimo giro if (i < 0) conto.cut(0); } //conto analitico TAnal_bill bill(conto); int indbil = bill.indicatore_bilancio(); TToken_string* ib = new TToken_string; *ib << indbil; ib->add(conto); return ib; } int TIndbil_cache::get_indbil(const TString& conto, TString& conto_anale) { int indicatore = 0; TToken_string* ib = (TToken_string*)objptr(conto); if (ib != NULL) { indicatore = ib->get_int(0); ib->get(1, conto_anale); } return indicatore; } void TIndbil_cache::set_prefix(const char* prefix) { if (_prefix != prefix) { _prefix = prefix; destroy(); } } TIndbil_cache::TIndbil_cache() { TConfig& cfg = ca_config(); _usepdcc = cfg.get_bool("UsePdcc"); } ///////////////////////////////////////////////////////////////////////////////// //Classe di report speciale con numero colonne adattabile in base ad un parametro ///////////////////////////////////////////////////////////////////////////////// void TCRPA_report::merge_array(TString_array& c, TString_array& t, TString_array& codici, TString_array& testate) const { while (c.items() > 0) { TObject* cod = c.remove(0,true); TObject* tes = t.remove(0,true); codici.TArray::add(cod); testate.TArray::add(tes); } } void TCRPA_report::analize_pconana_structure (const TString& prefix, const int depth, TString_array& codici, TString_array& testate) const { //cerca quale e' la lunghezza della stringa conto da considerare in base alla depth scelta TConfig& cfg = ca_config(); const TMultilevel_code_info& pconana_info = ca_multilevel_code_info(LF_PCONANA); const long total_length = pconana_info.total_len(depth); //scandisce il piano dei conti analitico alla ricerca dei conti di lunghezza pari a.. //..quella appena ricavata TISAM_recordset recset("USE PCONANA\nSELECT LEN(CODCONTO)=#LUN\nFROM CODCONTO=#PREFIX\nTO CODCONTO=#PREFIX"); recset.set_var("#LUN", TVariant(total_length)); recset.set_var("#PREFIX", TVariant(prefix)); const int prefix_length = prefix.len(); TString_array codici_c, testate_c, codici_r, testate_r; //riempie gli array con i codici conto di lunghezza opportuna e relative testate TString80 codice; for (bool ok = recset.move_first(); ok; ok = recset.move_next()) { const TString& codconto = recset.get(PCONANA_CODCONTO).as_string(); const TAnal_bill zio(codconto); const int indbil = zio.indicatore_bilancio(); //solo Costi e Ricavi! if (indbil == 3 || indbil == 4) { codice.cut(0) << "#RECORD." << codconto.mid(prefix_length); const TString& testata = zio.testata(); if (indbil == 3) { codici_c.add(codice); testate_c.add(testata); } else { codici_r.add(codice); testate_r.add(testata); } } } if (codici_r.items() == 1) //se il campo da stampare e' uno solo->e' il totale del livello { codici_r.destroy(); testate_r.destroy(); } codici_r.add("#RICAVI"); testate_r.add(TR("RICAVI")); //..analogo per i Costi if (codici_c.items() == 1) { codici_c.destroy(); testate_c.destroy(); } codici_c.add("#COSTI"); testate_c.add(TR("COSTI")); //condensa gli array di Costo e Ricavo in un unico array che servira' di base per la stampa merge_array(codici_r, testate_r, codici, testate); merge_array(codici_c, testate_c, codici, testate); } //metodo per il riempimento e lo spostamento dei campi della sola sezione F4 (totali di commessa) void TCRPA_report::offset_and_fill_sectionf4(TReport_section& rep_sect, const int model_id) { //campo modello TReport_field& rep_field = *rep_sect.find_field(model_id); //nuovo campo TReport_field* new_field = (TReport_field*)rep_field.dup(); //lo aggiunge alla sezione rep_sect.add(new_field); //gli da' l'id (e' il terzultimo campo) TReport_section& b1 = section('B', 1); const int body_fields_number = b1.items(); TReport_field& fld_costi = b1.field(body_fields_number - 3); new_field->set_id(fld_costi.id()); //posiziona tutti i campi della sezione for (int i = 0; i < rep_sect.items(); i++) { //c'e' il campo con il mio numero (id) in questa sezione? TReport_field& brock = rep_sect.field(i); if (brock.id() >= model_id) { //se c'e' cerco nel body il mio omonumero; se lo trovo mi allineo come lui //Es.: campi testata di pagina compaiono e si allineano in base ai campi body (fase,cdc ecc.) TReport_field* champ = b1.find_field(brock.id()); if (champ != NULL) brock.set_column(champ->get_rect().left()); } } } //metodo per il riempimento e lo spostamento dei campi dinamici void TCRPA_report::offset_and_fill_columns(TReport_section& rep_sect, const TString_array& str_arr, const int model_id, const bool show_fasi, const bool show_cdc) { //solo nel caso sia richiesta la sparizione di un campo... if (!show_fasi || !show_cdc) { if (rep_sect.type() == 'B') //campi del body { //gestione campi a sinistra del campo modello //la gestione dei campi a sinistra e' guidata dalle scelte utente sulla maschera TReport_field* fld_fase = rep_sect.find_field(3); if (fld_fase != NULL) //in caso non esista il campo non deve fare nulla (report riassuntivi x fase/cdc) { TReport_field* fld_cdc = rep_sect.find_field(4); TReport_field* fld_perc = rep_sect.find_field(5); int x0 = fld_fase->get_rect().left(); //mostra le fasi if (show_fasi) x0 = fld_fase->get_rect().right(); else fld_fase->deactivate(); //era meglio hide() che deactivate() ma non bastava! if (fld_cdc != NULL) { if (show_cdc) { fld_cdc->set_column(x0); x0 = fld_cdc->get_rect().right(); } else fld_cdc->deactivate(); } if (fld_perc != NULL) { //campo percentuale fld_perc->set_column(x0); x0 = fld_perc->get_rect().right(); } //campo modello rep_sect.find_field(model_id)->set_column(x0); } //if(fld_fase!=NULL.. } else //campi non del body { TReport_section& b1 = section('B', 1); //sistema i campi tipo testata for (int i = 0; i < rep_sect.items(); i++) { //c'e' il campo con il mio numero (id) in questa sezione? TReport_field& brock = rep_sect.field(i); const int id = brock.id(); if (id > 0) { //se c'e' cerco nel body il mio omonumero; se lo trovo mi mostro e allineo come lui //Es.: campi testata di pagina compaiono e si allineano in base ai campi body (fase,cdc ecc.) TReport_field* champ = b1.find_field(id); if (champ != NULL) { brock.activate(champ->active()); brock.set_column(champ->get_rect().left()); } } //if(id>0.. } //for(int i=0;... } //else di if(rep_sect.type()=='B'... } //if(!show_fasi||!show_cdc... TReport_field& rep_field = *rep_sect.find_field(model_id); //prende il rettangolo del campo modello.. const TRectangle& rep_field_rect = rep_field.get_rect(); //deve spostare i campi a destra della colonna modello //si memorizza i campi a destra del campo modello per poterli mettere a destra di tutti.. //..i campi che saranno generati TPointer_array campi_a_destra; for (int j = 0; j < rep_sect.items(); j++) { const TReport_field& campo = rep_sect.field(j); //solo i campi con un identificatore vanno spostati; gli altri sono parte dello sfondo if (campo.id() > model_id) { const TRectangle& rct = campo.get_rect(); //se il campo e' a destra del modello lo aggiunge all'array dei campi_a_destra if (rct.left() >= rep_field_rect.right()) campi_a_destra.add(campo); } } //duplica il campo modello e riempie i duplicati con i valori degli array //serve il tipo di sezione poiche' gli header vanno trattati diversamente dai body const char sect_type = rep_sect.type(); //ciclo su tutti gli elementi dell'array con i valori da settare nei nuovi campi for (int i = 0; i < str_arr.items(); i++) { //crea il nuovo campo i-esimo, copia del modello, spostato a destra rispetto al precedente (i-1)esimo.. //.., con id(i) = id(i-1) + 1 TReport_field* new_field = i == 0 ? &rep_field : (TReport_field*)rep_field.dup(); if (i > 0) { rep_sect.add(new_field); new_field->set_pos(rep_field_rect.left() + rep_field_rect.width() * i, rep_field_rect.top()); new_field->set_id(rep_field.id() + i); } switch (sect_type) { case 'H': //gli header devono stampare l'intestazione { new_field->set_picture(str_arr.row(i)); } break; case 'B': //i body devono stampare i valori e sommarli ai totali nei footer { new_field->set_field(str_arr.row(i)); TString ps = "MESSAGE ADD,F3."; ps << new_field->id(); if (find_section('F', 4) != NULL) ps << "|ADD,F4." << new_field->id(); new_field->set_postscript(ps); } break; case 'F': //i footer devono calcolarsi i totali;ma i footer di totale fasi (4) non devono.. //..fare assolutamente nulla! if (rep_sect.level() > 0 && rep_sect.level() < 4) { //crea il campo sul report con tanto di id,postscript ecc... new_field->set_field(""); switch (rep_sect.level()) { case 1: { //caso particolare del footer di report con i totali alla Adolf! //Dalla matematica secondo Adolf: //(a) TotGen = TotCmsNor + TotCmsSupp //(b) DiffGen = TotCmsSupp - TotCmsNor //->(c) DiffGen = TotGen - 2*TotCmsNor //i campi del totale generale si sommano ai campi delle differenze di Adolf /**CAZZONE** tentata per utilizzare la (c) ma non funziona:questi non servono a una ceppa di minchia new_field->set_field(""); TString ps = "MESSAGE ADD,F"; ps << (rep_sect.level()) << '.' << (new_field->id() + 200); new_field->set_prescript(ps);*/ //sub_new_field e' il campo SOTTO il new_field che appare solo nei totali di sezione 1 (F1) //e' il campo che va nella riga delle Differenze TReport_field* sub_new_field = (TReport_field*)new_field->dup(); rep_sect.add(sub_new_field); sub_new_field->offset(TPoint(0, 250)); sub_new_field->set_id(new_field->id() + 200); sub_new_field->set_groups("90"); sub_new_field->set_prescript(""); } break; case 2: { //ogni campo di footer F2 deve sommarsi a quello piu' esterno F1 TString ps = "MESSAGE ADD,F"; ps << (rep_sect.level() - 1) << '.' << new_field->id(); //in caso di Commesse Supporto (LEVEL=4) il totale si deve sommare nelle differenze generali ps << "\n#101 @\n"; ps << "4 = IF\n"; ps << " MESSAGE ADD,F1." << (new_field->id() + 200) << "\n"; ps << "THEN"; //setta il postscript al new_field new_field->set_postscript(ps); //SUB_new_field, ovvero gestione dei campi adolfici dei totali e delle differenze //Gestione totali delle commesse normali (Adolf!) //Alla fine di tutte le 4 sezioni di commesse normali ci va il totale delle medesime; questo.. //..totale va stampato prima della sezione con le commesse di appoggio //sub_new_field e' il campo SOTTO il new_field che appare solo nei totali di sezione 2 (F2) //e' il campo con il totale delle commesse normali TReport_field* sub_new_field = (TReport_field*)new_field->dup(); rep_sect.add(sub_new_field); sub_new_field->offset(TPoint(0, 250)); sub_new_field->set_id(new_field->id() + 200); sub_new_field->set_groups("90"); sub_new_field->set_postscript(""); //il totale delle commesse normali si sottrae nelle differenze generali TString ps_diff; //ps_diff << "#THIS @\n2\n*\n!\n"; **CAZZONE** tentata per applicare la (c);non funzia ps_diff << "MESSAGE SUB,F1." << sub_new_field->id(); sub_new_field->set_postscript(ps_diff); } break; case 3: { //ogni campo di footer F3 deve sommarsi a quello piu' esterno F2 TString ps = "MESSAGE ADD,F"; ps << (rep_sect.level() - 1) << '.' << new_field->id(); //deve anche sommarsi al totale delle Commesse Normali (che viene stampato al passaggio tra.. //..LEVEL=3 e LEVEL=4) ps << "\nMESSAGE ADD,F2." << (new_field->id() + 200); //setta il postscript al new_field new_field->set_postscript(ps); } break; default: break; } //switch(rep_sect.level()) } break; default: break; } //switch (sect_type) } //for(inti=0;idup(); rep_sect.add(sub_new_field_margine); sub_new_field_margine->offset(TPoint(0, 250)); sub_new_field_margine->set_id(369); sub_new_field_margine->set_groups("90"); sub_new_field_margine->set_postscript(""); } //ordina i campi davvero sul report per id rep_sect.sort(); } bool TCRPA_report::generate_columns (TString_array& codici, TString_array& testate, const int model_id, const bool show_fasi, const bool show_cdc) { //sezioni del report da modificare TReport_section& b1 = section('B', 1); //controllo dell'esistenza dei campi modello da replicare e loro duplicazione e riempimento! TReport_field* b1_model = b1.find_field(model_id); if (b1_model == NULL) return false; offset_and_fill_columns(b1, codici, model_id, show_fasi, show_cdc); //testate //la testata di pagina deve invece riempire le intestazioni delle colonne generate TReport_section& h0 = section('H', 0); TReport_field* h0_model = h0.find_field(model_id); if (h0_model == NULL) warning_box(TR("Manca l'intestazione della colonna modello (H0.%d)"),model_id); else offset_and_fill_columns(h0, testate, model_id, show_fasi, show_cdc); //le testate di sezione devono resettare i campi totale dei corrispondenti footers for (int j = 4; j > 0; j--) { TReport_section& head = section('H', j); if (head.items() > 0) { TString ps(256); ps = "MESSAGE "; //i campi dei totali da resettare sono tanti quante le colonne generate //k=0 per includere la colonna 69!!! usata nel caso i ricavi siano solo di livello 1 (depth=1) for (int k = 0; k <= codici.items(); k++) { if (k > 0) ps << '|'; ps << "RESET,F" << head.level() << '.' << (k+model_id); } head.set_prescript(ps); } } //footers for (int i = find_max_level('F') - 1; i > 0; i--) { TReport_section& foot = section('F', i); TReport_field* foot_model = foot.find_field(model_id); if (foot_model == NULL) warning_box(TR("Manca la colonna modello (F%d.%d)"), i, model_id); else offset_and_fill_columns(foot, codici, model_id, show_fasi, show_cdc); } //gestione della sezione con i totali di commessa (solo se esiste, visto che non c'e' in tutti i report) TReport_section* f4 = find_section('F', 4); if (f4 != NULL) offset_and_fill_sectionf4(*f4, model_id); #ifdef DBG save("cazzone.rep"); #endif return true; } TCRPA_report::TCRPA_report (const char* rep_name, const TString& prefix, const int depth, const bool show_fasi, const bool show_cdc) { //che report usare? load (rep_name); //array contenenti i conti analitici e le loro descrizioni di testata che diventeranno.. //.colonne del report dopo lungo e periglioso travaglio TString_array codici, testate; //per prima cosa si deve analizzare la struttura del piano conti.. //..da stampare fino al livello richiesto! analize_pconana_structure (prefix, depth, codici, testate); //poi vanno generate le colonne del report corrispondenti alla struttura analizzata generate_columns (codici, testate, 69, show_fasi, show_cdc); } /////////////////////////////////////////////////////////////////// // roba del recordset /////////////////////////////////////////////////////////////////// const TFixed_string CMS_DEL_CAZZO("@@@@@@@@@@@@@@@@@@@@"); static int righe_compare(const TObject** o1, const TObject** o2) { TAssoc_array& a1 = *(TAssoc_array*)*o1; TAssoc_array& a2 = *(TAssoc_array*)*o2; int cmp = 0; const int l1 = atoi(*(TString*)a1.objptr("LEVEL")); const int l2 = atoi(*(TString*)a2.objptr("LEVEL")); cmp = l1 - l2; if (cmp == 0) { const TString& c1 = *(TString*)a1.objptr("CODCMS"); const TString& c2 = *(TString*)a2.objptr("CODCMS"); cmp = c1.compare(c2); if (cmp == 0) { const TString& f1 = *(TString*)a1.objptr("FASE"); const TString& f2 = *(TString*)a2.objptr("FASE"); cmp = f1.compare(f2); if (cmp == 0) { const TString& d1 = *(TString*)a1.objptr("CDC"); const TString& d2 = *(TString*)a2.objptr("CDC"); cmp = d1.compare(d2); } else { const int z1 = c1.starts_with("detr_"); const int z2 = c2.starts_with("detr_"); if (z1 || z2) cmp = z1 - z2; } } } return cmp; } ///////////////////////////////////////////////////////////////////////////////// //Classe di recordset speciale per stampe con saldana (ca3800, ca3900) ///////////////////////////////////////////////////////////////////////////////// bool TPrint_saldana_recordset::move_to(TRecnotype pos) { _curr = pos; return pos >= 0 && pos < items(); } void TPrint_saldana_recordset::parse_bill(const TString& bill, TString& conto) const { TConfig& cfg = ca_config(); const TMultilevel_code_info& pconana_info = ca_multilevel_code_info(LF_PCONANA); const int pconana_levels = pconana_info.levels(); const int prefix = cfg.get_int("PdcPrefix")-1; //lunghezza dell'eventuale prefisso di gruppo e conto const int prefix_len = (prefix >= 0) ? pconana_info.total_len(prefix) : 0; //quanto e' lungo il codice del conto da considerare? E' il livello - il prefisso const int conto_end = pconana_info.total_len(_depth) - prefix_len; //stringa con cio' che segue il prefisso ed e' richiesto conto = bill.mid(prefix_len, conto_end); } int TPrint_saldana_recordset::estrai_saldi(const TRectype& saldana, const int indbil, TImporto& saldo, TImporto& saldop, const bool cms_detraz) const { int flag = 0; saldo.reset(); saldop.reset(); //stampa bilancio di commessa ca3800 (_tipo=8) if (_tipo == 8) { switch (_tipostima) { case 'R': //CostiConsuntivi RicaviConsuntivi (Cc/Rc) { const TImporto imp_saldo(saldana.get_char(SALDANA_SEZIONE), saldana.get_real(SALDANA_SALDO)); if (!imp_saldo.is_zero()) { if (indbil == 3) //indbil=3 costi { saldo = imp_saldo; flag = 1; } else //indbil=4 ricavi { saldop = imp_saldo; flag = 2; } } } break; case 'C': //CostiConsuntivi RicaviPreventivi (Cc/Rp) { if (indbil == 3) //costi { const TImporto imp_saldo(saldana.get_char(SALDANA_SEZIONE), saldana.get_real(SALDANA_SALDO)); saldo = imp_saldo; flag |= saldo.is_zero() ? 0 : 1; } else //indbil=4 ricavi { const TImporto imp_saldop(saldana.get_char(SALDANA_SEZIONEP), saldana.get_real(SALDANA_SALDOP)); const TImporto imp_saldov(saldana.get_char(SALDANA_SEZIONEV), saldana.get_real(SALDANA_SALDOV)); saldop = imp_saldop; saldop += imp_saldov; flag |= saldop.is_zero() ? 0 : 2; } } break; case 'T': //CostiPreventivi RicaviPreventivi (Cc/Rp) = Tempo //nel caso stia calcolando le detrazioni di anni precedenti (cms_detraz) e si sia scelto di farlo.. //..usando i consuntivi (Adolf rikiesten)... if (cms_detraz && _tipodetr) { const TImporto imp_saldo(saldana.get_char(SALDANA_SEZIONE), saldana.get_real(SALDANA_SALDO)); saldo = imp_saldo; flag |= saldo.is_zero() ? 0 : 1; } else //in tutti gli altri casi vanno bene i preventivi { const TImporto imp_saldop(saldana.get_char(SALDANA_SEZIONEP), saldana.get_real(SALDANA_SALDOP)); const TImporto imp_saldov(saldana.get_char(SALDANA_SEZIONEV), saldana.get_real(SALDANA_SALDOV)); saldop = imp_saldop; saldop += imp_saldov; flag |= saldop.is_zero() ? 0 : 2; } break; } //switch (_tipostima)... } //if(_tipo=8... // stampa stima ricavi ca3900 (_tipo=9) else { const TImporto imp_saldo(saldana.get_char(SALDANA_SEZIONE), saldana.get_real(SALDANA_SALDO)); saldo = imp_saldo; flag |= saldo.is_zero() ? 0 : 1; const TImporto imp_saldop(saldana.get_char(SALDANA_SEZIONEP), saldana.get_real(SALDANA_SALDOP)); const TImporto imp_saldov(saldana.get_char(SALDANA_SEZIONEV), saldana.get_real(SALDANA_SALDOV)); saldop = imp_saldop; saldop += imp_saldov; flag |= saldop.is_zero() ? 0 : 2; /* correzione per ottenere la sezione in base all'indicatore di bilancio inutile per ora... const char sezione_positiva = (indbil == 2 || indbil == 3) ? 'D' : 'A'; saldo.normalize(sezione_positiva); saldop.normalize(sezione_positiva);*/ } return flag; } TAssoc_array& TPrint_saldana_recordset::get_row(TAssoc_array& cms, const char* chiave, const int indice, const TString& codcms, const TString& fase, const TString& cdc, const TString& descrizione) const { //cerca se la commessa (e l'eventuale fase) esistono gia' nell'assocarray delle commesse TAssoc_array* riga_array = (TAssoc_array*)cms.objptr(chiave); //se non esiste la crea! if (riga_array == NULL) { riga_array = new TAssoc_array; TString4 str_indice; //l'indice va stringato per l'assoc_array str_indice << indice; riga_array->add("LEVEL", str_indice); riga_array->add("CODCMS", codcms); if (_tipo == 8) { riga_array->add("FASE", fase); riga_array->add("CDC", cdc); } riga_array->add("DESCRIZ", descrizione); //inizializza i campi importo sicuramente presenti (modifica DECISIVA) //senza questa inizializzazione, nel caso trovasse campi nulli (in realta' 0), il bastardo.. //..anziche' considerarli 0 ci mette l'ultimo valore != che ha in memoria nella stessa posizione.. //..facendo sballare tutti i conti! //bilancio commessa ca3800 (_tipo=8) if (_tipo == 8) { riga_array->add("RICAVI", EMPTY_STRING); riga_array->add("COSTI", EMPTY_STRING); } //stima ricavi ca3900 (_tipo=9) else { riga_array->add("COS_BDG", EMPTY_STRING); riga_array->add("COS_MAT", EMPTY_STRING); riga_array->add("RIC_BDG", EMPTY_STRING); riga_array->add("RIC_MAT", EMPTY_STRING); } //aggiunge la riga all'array-one cms.add(chiave, riga_array); } return *riga_array; } //for ca3800 only! void TPrint_saldana_recordset::aggiorna_importo(TAssoc_array& riga_array, const TString& livello, const int indbil, const TRectype& saldana, const bool inverti, const bool cms_detraz) const { TString* str_imp = (TString*)riga_array.objptr(livello); if (str_imp == NULL) { str_imp = new TString; riga_array.add(livello, str_imp); } //dare o avere? const char sezione = indbil == 3 ? 'D' : 'A'; TImporto imp(sezione, real(*str_imp)); //ci sono tutti i tipi di saldo, ma solo quelli che rientrano nei parametri iniziali.. //..verranno considerati (_tipostima,indbil) TImporto imp_saldo, imp_saldop; estrai_saldi(saldana, indbil, imp_saldo, imp_saldop, cms_detraz); if (inverti) // Devo sottrarre l'importo = gli scambio la sezione { imp_saldo.swap_section(); imp_saldop.swap_section(); } imp += imp_saldo; imp += imp_saldop; imp.normalize(sezione); *str_imp = imp.valore().string(); } //for ca3800 only! real TPrint_saldana_recordset::calcola_avanzamento_tempo() const { real avanzamento; const TDate& datafinesc = _esc[_anno].fine(); TDate dataini, datafine; const TString& codfase = get("FASE").as_string(); const TString& codcdc = get("CDC").as_string(); //fasi e cdc non specificati if (codfase.empty()) { const TString& codcms = get("CODCMS").as_string(); const TRectype& rec_commesse = cache().get(LF_COMMESSE, codcms); dataini = rec_commesse.get(COMMESSE_DATAINIZIO); //attenzione alle commesse prorogate di nascosto.. if (rec_commesse.get_bool(COMMESSE_PROROGA) && rec_commesse.get_date(COMMESSE_DATAPROR).ok()) datafine = rec_commesse.get(COMMESSE_DATAPROR); else datafine = rec_commesse.get(COMMESSE_DATAFINE); } else //specificata la fase { const TRectype& rec_fasi = cache().get(LF_FASI, codfase); dataini = rec_fasi.get(FASI_DATAINIZIO); datafine = rec_fasi.get(FASI_DATAFINE); } //Si prende SOLO la Fine Esercizio come riferimento di avanzamento (in realta' si.. //..potrebbe usare today al posto di datafinesc se si volesse la situazione ad oggi,.. //..ma i calcoli degli importi sarebbero da rivedere). Quindi: //se la commessa finisce prima della fine dell'esercizio if (datafine < datafinesc) avanzamento = CENTO; //se invece prosegue anche dopo la fine dell'esercizio... else { const long time_gone = datafinesc - dataini; const long durata = datafine - dataini; avanzamento = ((real)(time_gone * CENTO) / durata); } return avanzamento; } //for ca3900 only! void TPrint_saldana_recordset::incrementa_importo(TAssoc_array& riga_array, const TString& livello, const int indbil, const TImporto& saldo, const bool inverti) const { TString* str_imp = (TString*)riga_array.objptr(livello); if (str_imp == NULL) { str_imp = new TString; riga_array.add(livello, str_imp); } //dare o avere? const char sezione = indbil == 3 ? 'D' : 'A'; TImporto imp(sezione, real(*str_imp)); if (inverti) // Devo sottrarre l'importo = gli scambio la sezione imp -= saldo; else imp += saldo; imp.normalize(sezione); *str_imp = imp.valore().string(); } //metodo usato solo per la stima ricavi ca3900 (_tipo=9) void TPrint_saldana_recordset::aggiorna_importi(TAssoc_array& riga_array, const int indbil, const TRectype& saldana, const bool inverti) const { TString8 livello; TImporto imp_saldo, imp_saldop; estrai_saldi(saldana, indbil, imp_saldo, imp_saldop, false); //saldi normali:Maturato if (!imp_saldo.is_zero()) { livello.format("%s_MAT", indbil == 3 ? "COS" : "RIC"); incrementa_importo(riga_array, livello, indbil, imp_saldo, inverti); } //saldi preventivi:Budget if (!imp_saldop.is_zero()) { livello.format("%s_BDG", indbil == 3 ? "COS" : "RIC"); incrementa_importo(riga_array, livello, indbil, imp_saldop, inverti); } } //calcola la durata di una commessa e le sue date di inizio e fine long TPrint_saldana_recordset::durata_commessa(const TRectype& rec_commesse, TDate& dataini, TDate& datafine) const { //date iniziale e finale commessa dataini = rec_commesse.get_date(COMMESSE_DATAINIZIO); datafine = rec_commesse.get_date(COMMESSE_DATAFINE); //per la data fine deve tener conto di eventuali proroghe.. if (rec_commesse.get_bool(COMMESSE_PROROGA)) { const TDate datapror = rec_commesse.get_date(COMMESSE_DATAPROR); if (datapror.ok()) datafine = datapror; } //se almeno una delle due date risultasse incompleta.. if (!dataini.ok() || !datafine.ok()) { //prende l'anno TEsercizi_contabili esc; int anno = rec_commesse.get_int(COMMESSE_ANNO); //se non trova un anno valido.. if (anno <= 1900) { //prova con dataini, se è buona... if (dataini.ok()) anno = esc.date2esc(dataini); else { //allora prova con datafine, se è buona... if (datafine.ok()) anno = esc.date2esc(datafine); else anno = esc.first(); //se nessuna data è buona mette l'anno = al primo esercizio } } //se entrambe le date fossero vuote, visto che comunque adesso l'anno ce l'ha, le può ricavare.. if (!dataini.ok() && !datafine.ok()) esc.code2range(anno, dataini, datafine); else //solo una data o nessuna data non buona { TDate dummy; //dataini viene messa = ad inizio anno commessa if (!dataini.ok()) esc.code2range(anno, dataini, dummy); //datafine viene messa = alla data di scadenza dell'ultimo esercizio valido + 1 anno (mantiene corrette le sezioni di stampa) if (!datafine.ok()) esc.code2range(esc.last() + 1, dummy, datafine); } } //if (!dataini.ok() || !datafine.ok()) return datafine - dataini + 1; } //trova la sezione del report in cui ricade in base alle date int TPrint_saldana_recordset::ricava_sezione_di_stampa(const TRectype& rec_commesse, const TDate& datainiesc, const TDate& datafinesc) const { int indice = -1; //data iniziale commessa TDate dataini, datafine; durata_commessa(rec_commesse, dataini, datafine); //Le commesse vengono suddivise in varie sezioni in base alle loro date di inizio/fine/proroga.. //..e al fatto che possono essere di appoggio (Adolf rikiesten!) secondo lo schema: // _ 0 terminano nell'anno selezionato / iniziate nel passato // _ 1 terminano nell'anno selezionato / iniziate nell'anno selezionato // _ 2 terminano nel futuro / iniziate nel passato // _ 3 terminano nel futuro / iniziate nell'anno selezionato // _ 4 di appoggio / terminano nell'anno selezionato / iniziate nell'anno selezionato // (indice 1 ma di appoggio in pratica) //e' inutile considerare le commesse terminate prima dell'esercizio selezionato.. //..cioe' nel passato oppure che iniziano nel futuro! if (datafine >= datainiesc && dataini <= datafinesc) { //indice e' il parametro che stabilisce in quale sezione del report viene stampata la commessa //se la commessa termina prima della fine dell'esercizio selezionato -> indice 0, altrimenti.. //..indice 2 indice = datafine <= datafinesc ? 0 : 2; //se poi la commessa inizia dopo la data inizio esercizio selezionato -> l'indice va incrementato.. //..di una unita' (viene spostata nella sezione di stampa successiva) if (dataini >= datainiesc) indice++; //controlla se la commessa e' di appoggio e di indice 1 (inizia e finisce nell'anno selezionato) //in questo caso la commessa ha indice 4 e va nella sezione distaccata in fondo al report (Adolf!) if (indice == 1) { const bool cms_appoggio = rec_commesse.get_bool(COMMESSE_APPOGGIO); if (cms_appoggio) indice = 4; } } return indice; } TString TPrint_saldana_recordset::ricava_chiave_cdc_fase(const TString& codcms, const TString& fase, const TString& cdc) const { //ci sono filtri o raggruppamenti per fase o centro di costo? TString chiave = codcms; //selezione fasi solo su bilancio commessa (_tipo=8) if (_tipo == 8 && (_tipostampa >= 1 && _tipostampa <= 3)) { //se vuole entrambi i livelli di dettaglio occorre sapere chi e' il babbo delle fasi o dei cdc if (_tipostampa == 3) { //le fasi sono figlie dei cdc? if (ca_multilevel_code_info(LF_FASI).parent() == LF_CDC) chiave << '|' << cdc << '|' << fase; else //in ogni altro caso (figlie delle commesse o di nessuno...) chiave << '|' << fase << '|' << cdc; } else //livelli di dettaglio semplici { if (_tipostampa == 1) chiave << '|' << fase; //dettaglia x fase else chiave << '|' << cdc; //dettaglia x cdc } } return chiave; } const TDate TPrint_saldana_recordset::calcola_min_datacomp(const TDate& datainiesc, const TDate& datafinesc) const { TDate data_min_comp = datainiesc; TISAM_recordset recset("USE COMMESSE"); const TRectype& rec_commessa = recset.cursor()->curr(); for (bool ok = recset.move_first(); ok; ok = recset.move_next()) { TDate dataini, datafine; durata_commessa(rec_commessa, dataini, datafine); if (datafine >= datainiesc && dataini < data_min_comp) data_min_comp = dataini; } return data_min_comp; } //aggiorna l'arrayone cms con i saldana void TPrint_saldana_recordset::parse_saldana(TAssoc_array* cms, const TDate& datainiesc, const TDate& datafinesc, const TString& query) { TISAM_recordset saldana_set(query); const TRectype& saldana = saldana_set.cursor()->curr(); const long saldana_items = saldana_set.items(); TProgind pi(saldana_items, "Scansione saldi...", true, true); for (bool ok = saldana_set.move_first(); ok; ok = saldana_set.move_next()) { //progind tanto per gradire if (!pi.addstatus(1)) break; const TString& codconto = saldana.get(SALDANA_CONTO); //trova l'indicatore di bilancio TString80 conto_anale; const int indbil = _indicatori.get_indbil(codconto, conto_anale); //solo i Costi(3) ed i Ricavi(4) devono essere considerati per la stampa if (indbil == 3 || indbil == 4) { //gruppo e conto servono solo per il caso _tipo=8 ma vanno dichiarati e ricavati.. //..qui in modo che siano a disposizione delle commesse del cazzo poco sotto const char* gruppo = indbil == 3 ? "COSTI" : "RICAVI"; TString80 conto; parse_bill(conto_anale, conto); TImporto saldo, saldop; const int flag = estrai_saldi(saldana, indbil, saldo, saldop, false); //estrae i dati di commessa e le date relative;le date servono successivamente per stabilire... //..le sezioni in cui compariranno le commesse nella stampa const TString& codcms = saldana.get(SALDANA_COMMESSA); const TRectype& rec_commesse = cache().get(LF_COMMESSE, codcms); //indice: indica la sezione di report in cui apparariranno i valori! const int indice = ricava_sezione_di_stampa(rec_commesse, datainiesc, datafinesc); //e' inutile considerare le commesse terminate prima dell'esercizio selezionato.. //..cioe' nel passato oppure che iniziano nel futuro! Solo le commesse con indice non.. //..negativo sopravvivono! if (indice >= 0) { //ci sono filtri o raggruppamenti per fase o centro di costo? const TString& fase = saldana.get(SALDANA_FASE); const TString& cdc = saldana.get(SALDANA_COSTO); //ricava la chiave completa per l'assoc_array comprendendo eventuali fasi e/o cdc TString chiave = ricava_chiave_cdc_fase(codcms, fase, cdc); //riempie le righe degli array da mandare poi in stampa //dapprima le righe normali.. //bilancio di commessa ca3800 (_tipo=8) if (_tipo == 8) { TAssoc_array& riga_array = get_row(cms[indice], chiave, indice, codcms, fase, cdc, rec_commesse.get(COMMESSE_DESCRIZ)); //aggiunge gli importi e normalizza aggiorna_importo(riga_array, gruppo, indbil, saldana); aggiorna_importo(riga_array, conto, indbil, saldana); //aggiorna totali della commessa (importi per codcms senza tener conto di fase o cdc) TAssoc_array& riga_tot = get_row(_totali[indice], codcms, indice, codcms, EMPTY_STRING, EMPTY_STRING, gruppo); aggiorna_importo(riga_tot, gruppo, indbil, saldana); } //stima ricavi ca3900 (_tipo=9) else { TAssoc_array& riga_array = get_row(cms[indice], chiave, indice, codcms, EMPTY_STRING, EMPTY_STRING, rec_commesse.get(COMMESSE_DESCRIZ)); //aggiunge gli importi e normalizza aggiorna_importi(riga_array, indbil, saldana); } } //if (datafine >= datainiesc &&... //RIGHE COMMESSA DETRAZIONE: esistono solo se la commessa e' iniziata prima dell'anno //selezionato e se ha a che fare con quello in corso,quindi l'indice e' 0 o 2 //indice è stato ricavato un tot di righe addietro... if (indice == 0 || indice == 2) { //aggiorna il record speciale con la somma dei saldi con anno anteriore a quello.. //..selezionato sulla maschera (CRPA request) const int anno = saldana.get_int(SALDANA_ANNO); if (anno < _anno) { TString16 cazzo_cod; cazzo_cod.format("detr_al_%04d", _anno - 1); TString cazzo_descr = "DETRAZIONE PER COMPETENZA FINO AL "; cazzo_descr << (_anno - 1); TAssoc_array& riga_array = get_row(cms[indice], CMS_DEL_CAZZO, indice, cazzo_cod, EMPTY_STRING, EMPTY_STRING, cazzo_descr); if (_tipo == 8) { //cms_detr con tipo detrazione a consuntivo o preventivo in base al valore di _tipodetr aggiorna_importo(riga_array, gruppo, indbil, saldana, true, _tipodetr); aggiorna_importo(riga_array, conto, indbil, saldana, true, _tipodetr); } else { aggiorna_importi(riga_array, indbil, saldana, true); } } //if(anno<_anno... } //if(dataini= 0) vengono considerate if (indice >= 0) { //ci sono filtri o raggruppamenti per fase o centro di costo? const TString& fase = rmovana.get(RMOVANA_CODFASE); const TString& cdc = rmovana.get(RMOVANA_CODCCOSTO); //se la riga è risultata buona ricava la chiave completa per l'assoc_array comprendendo eventuali.. //..fasi e/o cdc TString chiave = ricava_chiave_cdc_fase(codcms, fase, cdc); //calcola la "durata complessiva" della riga, ovvero l'intervallo di tempo per cui essa risulta valida //la data fine ci fa sudare un pò //di base è pari a datafcomp di testata (che è sempre esistente, mal che vada è uguale a datacomp) TDate datafcomp = movana.get_date(MOVANA_DATAFCOMP); //se invece è un movimento cazzuto con il calcolo automatico della data di morte (dataexpire)... if (movana.get_bool(MOVANA_AUTOFCOMP)) { //data del cazzo che serve per non rovinare datacomp, che è la data sulla riga, non quella iniziale di cms TDate datainicms; durata_commessa(rec_commesse, datainicms, datafcomp); } //se siamo in presenza di un movimento senza datafcomp (inserito precdentemente alla nascita di datafcomp.. //..per tutti i movimenti, come nel gestore dei movana mette datafcomp = datacomp; if (!datafcomp.ok()) datafcomp = datacomp; //finalmente la tanto agognata vita della riga di movana! //si aggiunge 1 perchè se un movimento ha datacomp=datafcomp (come in generale accade) in realtà.. //..il poveretto vive un giorno, non 0 const long vita_totale_riga = datafcomp - datacomp + 1; //riproporzionamento dell'importo di riga in base alla frazione di vita di riga interessata real importo = rmovana.get_real(RMOVANA_IMPORTO); if (vita_totale_riga > 1) { //trova la frazione di vita della riga che interessa l'esercizio corrente const TDate inizio_inter = fnc_max(datainiesc, datacomp); const TDate fine_inter = fnc_min(_datacalcolo, datafcomp); //si aggiunge anche qui 1 per gli stessi motivi della vita_totale const long vita_frazione_riga = fine_inter - inizio_inter + 1; //il riproporzionamento vale solo per i movimenti che durano più di 1 giorno e con una vita_frazione.. //..> vita_totale (il contrario sarebbe un errore!). I movimenti tutti nel futuro risultano con.. //..vita_frazione negativa e quindi non sono considerati //Anche le commesse tutte nel futuro rispetto a datacalcolo danno origine a una vita_frazione negativa.. //..parando il culo al povero programma if (vita_frazione_riga > 1 && vita_frazione_riga < vita_totale_riga) { importo = vita_frazione_riga * importo / vita_totale_riga; importo.round(2); } } //dopo tutto sto panegirico sul riproporzionamento finalmente si passa ad aggiornare gli array di stampa.. //..con gli importi aggiustati if (!importo.is_zero()) { //importo della riga già eventualmente riproporzionato const TImporto importo_riga(rmovana.get_char(RMOVANA_SEZIONE), importo); //riga array da aggiungere agli arrayoni da mandare in stampa TAssoc_array& riga_array = get_row(cms[indice], chiave, indice, codcms, fase, cdc, rec_commesse.get(COMMESSE_DESCRIZ)); const TString& codconto = rmovana.get(RMOVANA_CODCONTO); //trova l'indicatore di bilancio TString80 conto_anale; const int indbil = _indicatori.get_indbil(codconto, conto_anale); //solo i Costi(3) ed i Ricavi(4) devono essere considerati per la stampa if (indbil == 3 || indbil == 4) { //gruppo e conto servono solo per il caso _tipo=8 ma vanno dichiarati e ricavati.. //..qui in modo che siano a disposizione delle commesse del cazzo poco sotto const char* gruppo = indbil == 3 ? "COSTI" : "RICAVI"; TString80 conto; parse_bill(conto_anale, conto); TRectype saldana(LF_SALDANA); saldana.put(SALDANA_ANNO, _anno); saldana.put(SALDANA_CONTO, conto_anale); saldana.put(SALDANA_COSTO, cdc); saldana.put(SALDANA_COMMESSA, codcms); saldana.put(SALDANA_FASE, fase); switch(tipomov) { case 'P': saldana.put(SALDANA_SEZIONEP, importo_riga.sezione()); saldana.put(SALDANA_SALDOP, importo_riga.valore()); break; case 'V': saldana.put(SALDANA_SEZIONEV, importo_riga.sezione()); saldana.put(SALDANA_SALDOV, importo_riga.valore()); break; default: saldana.put(SALDANA_SEZIONE, importo_riga.sezione()); saldana.put(SALDANA_SALDO, importo_riga.valore()); break; } //aggiunge gli importi e normalizza aggiorna_importo(riga_array, gruppo, indbil, saldana); aggiorna_importo(riga_array, conto, indbil, saldana); } } } //if(indice>=0)... } //aggiorna l'arrayone cms con le rmovana void TPrint_saldana_recordset::parse_rmovana(TAssoc_array* cms, const TDate& datainiesc, const TDate& datafinesc) { const TDate min_datacomp = calcola_min_datacomp(datainiesc, datafinesc); //cerca tutte le rmovana che soddisfano gli innumerevoli parametri TString query; query << "USE RMOVANA KEY 3\n"; switch (_tipostampa) { case 1: case 4: if (_fase.full()) query << "SELECT CODFASE=\"" << _fase << "\"\n"; break; case 2: case 8: if (_cdc.full()) query << "SELECT CODCCOSTO=\"" << _cdc << "\"\n"; break; case 3: if (_fase.full() || _cdc.full()) { if (_fase.full() && _cdc.full()) query << "SELECT (CODFASE=\"" << _fase << "\")&&(CODCCOSTO=\"" << _cdc << "\")\n"; else { if (_fase.full()) query << "SELECT CODFASE=\"" << _fase << "\"\n"; else query << "SELECT CODCCOSTO=\"" << _cdc << "\"\n"; } } break; } query << "JOIN MOVANA INTO NUMREG==NUMREG\nFROM DATACOMP=#MINDATACOMP\nTO DATACOMP=#DATACALCOLO"; TISAM_recordset recset(query); recset.set_var("#DATACALCOLO", _datacalcolo); recset.set_var("#MINDATACOMP", min_datacomp); const TRecnotype recset_items = recset.items(); //se ci sono movana che non expirano prima dell'inizio esercizio e la cui datacomp è anteriore a datacalcolo... if (recset_items > 0) { //record correntemente in esame di rmovana e movana const TRectype& rmovana = recset.cursor()->curr(); const TRectype& movana = recset.cursor()->curr(LF_MOVANA); //simpatica progind per intrattenere l'utonto TProgind pi(recset_items, "Scansione movimenti...", true, true); for (bool ok = recset.move_first(); ok; ok = recset.move_next()) //giro sui vari rmovana... { //aggiornamento progind intrattenitiva if (!pi.addstatus(1)) break; //metodo per generare i saldi alla data della riga analitica in base alla vita della riga elabora_rmovana(cms, datainiesc, datafinesc, movana, rmovana); } //for(bool ok=recset.move_first()... } //if(recset_items>0... } int TPrint_saldana_recordset::numerazioni_ordini(TString_array& num_ordini, TString_array& tip_ordini) const { //i documenti che vanno presi in cosiderazione sono quelli che generano un IMPEGNATO secondo le auree regole del.. //..nostro invincibile Adolf! //quindi dapprima trova i TIPI ordine, poi le NUMERAZIONI che li contengono; alla fine passa entrambi gli array num_ordini.destroy(); tip_ordini.destroy(); //cominciamo con i tipi TISAM_recordset tipi_recset("USE %TIP\nSELECT I1=3"); for (bool ok = tipi_recset.move_first(); ok; ok = tipi_recset.move_next()) //giro sui vari tipi ordine { const TString4 tipo = tipi_recset.cursor()->curr().get("CODTAB"); tip_ordini.add(tipo); } //e adesso cerca le numerazioni che contengono tipi ordine TISAM_recordset num_recset("USE %NUM"); for (bool ok = num_recset.move_first(); ok; ok = num_recset.move_next()) //giro sui vari tipi numerazione { const TString4 codtab = num_recset.get("CODTAB").as_string(); const TCodice_numerazione& numerazione = cached_numerazione(codtab); for (int t = numerazione.ntipi_doc() - 1; t >= 0; t--) { const TString& tipo_doc = numerazione.tipo_doc(t); if (tip_ordini.find(tipo_doc) >= 0) { num_ordini.add(codtab); break; } } //for (int t = codnum.. } //for (bool ok = num_recset... return num_ordini.items(); } //aggiorna l'arrayone cms con le righedoc non ancora contabilizzate void TPrint_saldana_recordset::parse_rdoc(TAssoc_array* cms, const TDate& datainiesc, const TDate& datafinesc) { //solo le numerazioni con almeno un tipo documento ordine interessano TString_array num_ordini, tip_ordini; const int numerazioni_tipo_ordine = numerazioni_ordini(num_ordini, tip_ordini); for (int n = 0; n < numerazioni_tipo_ordine; n++) { TString4 codnum = num_ordini.row(n); //cerca tutte le righe documento che soddisfano i numerosi (fin troppo) parametri //joina DOC perchè abbiamo un decisivo filtro sul range di date TString query; query << "USE RDOC KEY 3\n"; query << "SELECT (BETWEEN(DOC.DATADOC, #DATAINIES, #DATACALCOLO))&&(RIGAEVASA!=\"X\")"; //filtro su cdc/cms/fasi (fatto come per saldi e rmovana; purtroppo non si può metodizzare perchè in tutti i files.. //..i nomi dei campi sono diversi! switch (_tipostampa) { case 1: case 4: if (_fase.full()) query << "&&(FASCMS=\"" << _fase << "\")\n"; break; case 2: case 8: if (_cdc.full()) query << "&&(CODCOSTO=\"" << _cdc << "\")\n"; break; case 3: if (_fase.full() || _cdc.full()) { if (_fase.full() && _cdc.full()) query << "&&(FASCMS=\"" << _fase << "\")&&(CODCOSTO=\"" << _cdc << "\")\n"; else { if (_fase.full()) query << "&&(FASCMS=\"" << _fase << "\")\n"; else query << "&&(CODCOSTO=\"" << _cdc << "\")\n"; } } break; } query << "JOIN DOC INTO PROVV=='D' ANNO==ANNO CODNUM==CODNUM\nFROM PROVV='D' ANNO=#ANNO CODNUM=#CODNUM\nTO PROVV='D' ANNO=#ANNO CODNUM=#CODNUM"; TISAM_recordset recset(query); recset.set_var("#ANNO", (long)_datacalcolo.year()); recset.set_var("#CODNUM", codnum); recset.set_var("#DATAINIES", datainiesc); recset.set_var("#DATACALCOLO", _datacalcolo); const TRecnotype recset_items = recset.items(); //simpatica progind per intrattenere l'utonto TProgind pi(recset_items, "Scansione documenti...", true, true); //memorizza l'ultimo doc per evitare doppioni in caso di doc con più righe (rielaborerebbe.. //..lo stesso documento tante volte quante sono le sue righe!) TString old_key; //misterioso oggetto necessario per contabilizzare il documento in osservazione TContabilizzazione_analitica cont_anal; for (bool ok = recset.move_first(); ok; ok = recset.move_next()) //giro sulle varie rdoc... { //aggiornamento progind intrattenitiva if (!pi.addstatus(1)) break; const TRectype& curr_doc = recset.cursor()->curr(LF_DOC); //controlla se il documento contenente la riga è davvero un ordine ricontrollando il suo tipo nell'array.. //..con i tipi validi; se non lo fosse...ciao ciao rigadocumento! const TString& tipodoc = curr_doc.get(DOC_TIPODOC); if (tip_ordini.find(tipodoc) < 0) continue; //ovviamente non è finita qui... //la riga documento non deve risultare contabilizzata! infatti, se lo fosse, i suoi valori apparirebbero.. //..nella sezione delle rmovana const long numregca = curr_doc.get_long(DOC_NUMREGCA); //se la rigadoc non è stata contabilizzata.. if (numregca == 0) { //magico trucco per non dover ricontabilizzare lo stesso documento tante volte quante sono le righe consecutive const TString curr_key = curr_doc.build_key(); if (curr_key == old_key) continue; else old_key = curr_key; //crea il documento virtuale in memoria; se non è ordine oppure lo è ma è già evaso, lo saltiamo!!! ole'! TDocumento doc(curr_doc); if (!doc.is_ordine() || doc.is_evaso()) continue; //il documento virtuale è adesso un ordine non evaso //tarocchiamo il documento prendendo solo le righe non evase (almeno una ci sarà, perchè la query del recordset.. //..richiedeva che la rigadoc fosse non evasa, quindi quella riga nel doc esiste, magari in compagnia di altre) for (int r = doc.body().last_row(); r > 0; r = doc.body().pred_row(r)) { TRiga_documento& rigadoc = doc[r]; if (!rigadoc.is_evasa()) { //trasforma tutte le righe a valore, assegnando al prezzo il valore del residuo const real valore = rigadoc.valore(true); const real residuo = rigadoc.valore(false); //ovviamente questo è vero se il residuo è inferiore al valore; se fosse uguale sarebbe riga ancora da evadere,.. //..se fosse maggiore sarebbe riga del cazzo if (residuo < valore) { rigadoc.put(rigadoc.tipo().quant(), UNO); rigadoc.put(RDOC_PREZZO, residuo); } } else doc.body().destroy_row(r, true); //se la riga fosse evasa la salta e compatta il documento } //movana nevessario per la contabilizzazione analitica del documento TAnal_mov movana; //finalmente contabilizza il documento in memoria cont_anal.elabora(doc, 0, NULL, false, movana, false); movana.put(MOVANA_TIPOMOV, 'P'); //controlla che la riga in esame abbia realmente la cms/cdc/fas indicata nel filtro; //procedimento necessario per evitare di prendere righe appartenenti a docs elaborati //perchè contenenti 1 riga con cms corretta ed altre righe con cms sbagliate for (int j = 1; j <= movana.rows(); j++) { const TRectype& rmovana = movana.body()[j]; if (_cdc.not_empty() && rmovana.get(RMOVANA_CODCCOSTO) != _cdc) continue; if (_fase.not_empty() && rmovana.get(RMOVANA_CODFASE) != _fase) continue; //metodo per il calcolo importo riga riproporzionato //metodo per generare i saldi alla data della riga analitica in base alla vita della riga elabora_rmovana(cms, datainiesc, datafinesc, movana, rmovana); } //for int j... } } //for (bool ok = recset.... } //for (int n = 0; n < numerazioni... } //metodo principale di riempimento degli array da mandare in stampa void TPrint_saldana_recordset::create_lines_to_print(const TString& query) { //ricava date limite di esercizio TEsercizi_contabili esc; TDate datainiesc, datafinesc; esc.code2range(_anno, datainiesc, datafinesc); //array con le commesse divise sui vari livelli di sezione di stampa TAssoc_array cms[NUM_LEVELS]; // 1) // calcolo dei saldi alla data fine esercizio (vale per entrambi i tipi di stampa, ovvero _tipo=8 e _tipo=9) parse_saldana(cms, datainiesc, datafinesc, query); // 2) // calcolo dei saldi ad una data diversa da una data di fine esercizio (saldi alla data di adolfica richiesta) // serve solo per il bilancio di commessa (_tipo = 8) if (_tipo == 8) { if ((_datacalcolo < datafinesc) && !_vitaintera) { // 2_a) calcolo dei saldi da righe movimenti analitici e quindi senza saldana parse_rmovana(cms, datainiesc, datafinesc); // 2_b) calcolo dei saldi da documenti non ancora contabilizzati e quindi senza movana e saldana parse_rdoc(cms, datainiesc, datafinesc); } //if((_datacalcoloremove_obj(); if (strcmp(k, CMS_DEL_CAZZO) == 0) cazzo_cms = obj; else _righe.add(obj); // Copia nella destinazione la riga corrente e la toglie dall'originale } if (cazzo_cms != NULL) _righe.add(cazzo_cms); } //for(int... _righe.sort(righe_compare); //sorting delle commesse } void TPrint_saldana_recordset::requery() { TString query; query = "USE SALDANA"; //vale sia per la 8 che per la 9 //se ca3800... if (_tipo == 8) { switch (_tipostampa) { case 1: case 4: if (_fase.full()) query << " SELECT FASE=\"" << _fase << "\""; break; case 2: case 8: if (_cdc.full()) query << " SELECT COSTO=\"" << _cdc << "\""; break; case 3: if (_fase.full() || _cdc.full()) { if (_fase.full() && _cdc.full()) query << " SELECT (FASE=\"" << _fase << "\")&&(COSTO=\"" << _cdc << "\")"; else { if (_fase.full()) query << " SELECT FASE=\"" << _fase << "\""; else query << " SELECT COSTO=\"" << _cdc << "\""; } } break; } //stampa standard non a vita intera per la 3800 if (!_vitaintera) { TEsercizi_contabili esc; TDate datainiesc, datafinesc; esc.code2range(_anno, datainiesc, datafinesc); query << "\nTO ANNO="; if (_datacalcolo == datafinesc) query << _anno; else query << (_anno - 1); } } //if(_tipo==8.. if (_tipo == 9) { query << "\nTO ANNO=" << _anno; //non vanno considerati saldi di esercizi futuri a quello scelto!!! } //crea effettivamente la stampa !!! create_lines_to_print(query); } const TVariant& TPrint_saldana_recordset::get(unsigned int column) const { return NULL_VARIANT; } const TVariant& TPrint_saldana_recordset::get(const char* column_name) const { if (_curr >= 0 && _curr < items()) { if (*column_name == '#') column_name++; TVariant& var = get_tmp_var(); if (strcmp(column_name, "ANNO") == 0) { var.set(_anno); } else if (strcmp(column_name, "TIPOSTIMA") == 0) { switch (_tipostima) { case 'T': var.set("T"); break; case 'C': var.set("C"); break; case 'R': var.set("R"); break; default: break; } } else if (strcmp(column_name, "DEPTH") == 0) { var.set(_depth); } else if (strcmp(column_name, "TIPODETR") == 0) { var.set(_tipodetr == true ? "X" : ""); } else if (strcmp(column_name, "VITAINTERA") == 0) { var.set(_vitaintera == true ? "SI" : "NO"); } else if (strcmp(column_name, "STAMPAFASI") == 0) { var.set((_tipostampa == 1 || _tipostampa == 3) ? "X" : ""); } else if (strcmp(column_name, "FASESPEC") == 0) { if (_tipostampa == 1 || _tipostampa == 3 || _tipostampa == 4) var.set(_fase); else var.set(EMPTY_STRING); } else if (strcmp(column_name, "STAMPACDC") == 0) { var.set((_tipostampa == 2 || _tipostampa == 3) ? "X" : ""); } else if (strcmp(column_name, "CDCSPEC") == 0) { if (_tipostampa == 2 || _tipostampa == 3 || _tipostampa == 8) var.set(_cdc); else var.set(EMPTY_STRING); } else if (strcmp(column_name, "AVANZAMENTO") == 0) { //Calcola lo stato di avanzamento della commessa in caso di stima x Tempo real avanzamento; if (_tipostima == 'T') { avanzamento = calcola_avanzamento_tempo(); } else //se stima e' di tipo Costi/Ricavi invece....Allah!! { TAssoc_array& riga = (TAssoc_array&)_righe[_curr]; const real val = get("RICAVI").as_real(); if (!val.is_zero()) { int indice = get("LEVEL").as_int(); const TString& codcms = get("CODCMS").as_string(); TAssoc_array& riga_tot = get_row((TAssoc_array&)_totali[indice], codcms, indice, codcms, EMPTY_STRING, EMPTY_STRING, "RICAVI"); const TString* stric = (const TString*)riga_tot.objptr("RICAVI"); if (stric && stric->full()) { const real totric = *stric; if (!totric.is_zero()) avanzamento = val * CENTO / totric; } } } var.set(avanzamento); } else //if(strcmp(column_name,AVANZAMENTO... { TAssoc_array& riga = (TAssoc_array&)_righe[_curr]; if (strncmp(column_name, "RECORD.", 7) == 0) column_name += 7; if (_forza_maturato && strcmp(column_name, "COS_MAT") == 0) { const int indice = get("LEVEL").as_int(); if (indice <= 1) column_name = "COS_BDG"; } const TString* val = (TString*)riga.objptr(column_name); if (val) var.set(*val); else var.set_null(); } return var; } return NULL_VARIANT; } void TPrint_saldana_recordset::set_filter(const TMask& msk, const TString& curr_sublevel) { _fase = _cdc = ""; _forza_maturato = false; //tira su un po' di parametri dalla maschera... _anno = msk.get_int(F_ESERCIZIO); _datacalcolo = msk.get_date(F_FINE_ES); //prende anche il prefix TString80 prefix; for (short id = F_PRE1; id <= F_PRE3 && msk.id2pos(id) > 0; id++) prefix << msk.get(id); _indicatori.set_prefix(prefix); //solo per ca3800 if (_tipo == 8) { _tipostima = msk.get(F_TIPOSTIMA)[0]; _vitaintera = msk.get_bool(F_VITAINTERA); _depth = msk.get_int(F_DEPTH); _tipodetr = msk.get_bool(F_TIPODETR); _tipostampa = msk.get_int(F_TIPOSTAMPA); //in base al tipo di stampa selezionata setta un po' di parametri switch (_tipostampa) { case 1: _fase = msk.get(F_FASE); _cdc.cut(0); break; case 2: _cdc = msk.get(F_CDC); _fase.cut(0); break; case 3: _fase = msk.get(F_FASE); _cdc = msk.get(F_CDC); break; case 4: _fase = curr_sublevel; break; case 8: _cdc = curr_sublevel; break; default: break; } } //if(tipo==8.. if (_tipo == 9) { _forza_maturato = msk.get_bool(F_FORZA_MATURATO); } }