#include "ve3800.h" #include #include #include #include #include #include #include #include #include "../cg/cglib01.h" #include "velib07.h" /////////////////////////////////////////////////////////// // MASCHERA /////////////////////////////////////////////////////////// class TStatistiche_ve_mask : public TAutomask { protected: virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); public: TStatistiche_ve_mask(); virtual ~TStatistiche_ve_mask() {} }; TStatistiche_ve_mask::TStatistiche_ve_mask() : TAutomask("ve3800") { } bool TStatistiche_ve_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case F_ANNO: if (e == fe_modify && !o.empty()) { TEsercizi_contabili esc; TDate inies, fines; if (esc.code2range(atoi(o.get()), inies, fines)) { set(F_DADATA, inies); set(F_ADATA, fines); } } break; case F_DADATA: case F_ADATA: 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 true; } ///////////////////////////////////////////////////////////// // REPORT ///////////////////////////////////////////////////////////// class TStatistiche_ve_report : public TReport { protected: virtual bool use_mask() { return false; } public: TStatistiche_ve_report() {} }; ///////////////////////////////////////////////////////////// // CSV RECORDSET ///////////////////////////////////////////////////////////// class TStatistiche_ve_csv_recordset : public TCSV_recordset { real _tot[13]; //array con i totali per mese (e anno) protected: long trova_riga(const TToken_string& key, const char tipo_stat, const char tipo_dettaglio); public: TStatistiche_ve_csv_recordset(const TMask& mask); //virtual const TVariant& get(const char* column_name) const; //virtual const TVariant& get(unsigned int column) const { return TCSV_recordset::get(column); } void aggiungi_ai_totali(const TRiga_documento& riga, const char tipo_dati); bool aggiungi_riga(TDocument_recordset& righe, const char tipo_dati, const char tipo_stat, const char tipo_dettaglio); //const TRiga_documento& riga, const char tipo_dati); void compila_intestazione(); void calcola_percentuali(); }; TStatistiche_ve_csv_recordset::TStatistiche_ve_csv_recordset(const TMask& mask) : TCSV_recordset("CSV(\"\t\")") //tab separated { FOR_EACH_MASK_FIELD(mask, i, field) { const TFieldref* f = field->field(); if (f != NULL) { const TString& name = f->name(); set_var(name, field->get(), true); } } } /*const TVariant& TStatistiche_ve_csv_recordset::get(const char* field_name) const { if (*field_name == '#') return get_var(field_name); return TRecordset::get(field_name); }*/ void TStatistiche_ve_csv_recordset::compila_intestazione() { insert_rec(0); //riempie i campi del primo record del csv in modo da avere l'intestazione (0=A...29=AD in excel) set(0, "CODART"); set(1, "DESCRIZIONE"); set(2, "GENNAIO"); set(3, "%GEN"); set(4, "FEBBRAIO"); set(5, "%FEB"); set(6, "MARZO"); set(7, "%MAR"); set(8, "APRILE"); set(9, "%APR"); set(10, "MAGGIO"); set(11, "%MAG"); set(12, "GIUGNO"); set(13, "%GIU"); set(14, "LUGLIO"); set(15, "%LUG"); set(16, "AGOSTO"); set(17, "%AGO"); set(18, "SETTEMBRE"); set(19, "%SET"); set(20, "OTTOBRE"); set(21, "%OTT"); set(22, "NOVEMBRE"); set(23, "%NOV"); set(24, "DICEMBRE"); set(25, "%DIC"); set(26, "ANNO"); set(27, "%ANNO"); set(28, "GRMERC"); set(29, "CODCLI"); } //metodo plutonico di ricerca dicotomica su una colonna di csv_recordset long TStatistiche_ve_csv_recordset::trova_riga(const TToken_string& key, const char tipo_stat, const char tipo_dettaglio) { long first = 0; long last = items() - 1; long riga = -1; while(first <= last) { const long guess = (first + last) / 2; move_to(guess); //const TString& guess_codart = get(0).as_string(); //ori //const int diff = guess_codart.compare(codart); //ori TToken_string guess_key; switch (tipo_stat) { case 'A': //articolo guess_key.add(get(0).as_string()); break; case 'C': //cliente-codart codart-cliente { switch (tipo_dettaglio) { case 'A': { guess_key.add(get(0).as_string()); //colonna 0=A codart TString8 codcf = get(29).as_string(); codcf.right_just(6); guess_key.add(codcf); //colonna 29=AD codcf } break; case 'C': guess_key = get(29).as_string(); //colonna 29=AD codcf guess_key.right_just(6); guess_key.add(get(0).as_string()); //colonna 0=A codart break; case 'G': guess_key = get(29).as_string(); //colonna 29=AD codcf guess_key.right_just(6); guess_key.add(get(28).as_string()); //colonna 28=AC grmerc break; case 'M': { guess_key.add(get(28).as_string()); //colonna 28=AC grmerc TString8 codcf = get(29).as_string(); codcf.right_just(6); guess_key.add(codcf); //colonna 29=AD codcf } break; default: guess_key = get(29).as_string(); //colonna 29=AD codcf guess_key.right_just(6); break; } //switch(tipo_dettaglio... } //case 'C'... break; case 'G': //grmerc-codart guess_key.add(get(28).as_string()); //colonna 28=AC grmerc guess_key.add(get(0).as_string()); break; default: break; } //switch(tipo_stat... const int diff = guess_key.compare(key); if (diff == 0) { riga = guess; break; } if (diff > 0) { last = guess - 1; } else { first = guess + 1; } } return riga; } /////////////////////////////////////////////// // METODI DI COMPARE /////////////////////////////////////////////// //funzione di ordinamento per il campo codart (campo 0 sul csv) //è una stringa semplice static int compare_csv_rows_codart(const TObject** o1, const TObject** o2) { TToken_string& s1 = *(TToken_string*)*o1; TToken_string& s2 = *(TToken_string*)*o2; const TString80 c1 = s1.get(0); const TString80 c2 = s2.get(0); int cmp = c1.compare(c2); return cmp; } //funzione di ordinamento per il campo coscf (campo 29 sul csv) //è un long static int compare_csv_rows_codcf(const TObject** o1, const TObject** o2) { TToken_string& s1 = *(TToken_string*)*o1; TToken_string& s2 = *(TToken_string*)*o2; long c1 = s1.get_long(29); long c2 = s2.get_long(29); int cmp = c1 - c2; return cmp; } //funzione di ordinamento per il campo grmerc/codart (campi 28/0 sul csv) //sono 2 stringhe static int compare_csv_rows_grmerc_codart(const TObject** o1, const TObject** o2) { TToken_string& s1 = *(TToken_string*)*o1; TToken_string& s2 = *(TToken_string*)*o2; TToken_string c1; c1.add(s1.get(28)); c1.add(s1.get(0)); TToken_string c2; c2.add(s2.get(28)); c2.add(s2.get(0)); int cmp = c1.compare(c2); return cmp; } //funzione di ordinamento per il campo codcf/codart (campi 29/0 sul csv) //è un long + una stringa static int compare_csv_rows_codcf_codart(const TObject** o1, const TObject** o2) { TToken_string& s1 = *(TToken_string*)*o1; TToken_string& s2 = *(TToken_string*)*o2; long c1 = s1.get_long(29); long c2 = s2.get_long(29); int cmp = c1 - c2; if (cmp == 0) { const TString80 a1 = s1.get(0); const TString80 a2 = s2.get(0); cmp = a1.compare(a2); } return cmp; } //funzione di ordinamento per il campo codcf/grmerc (campi 29/28 sul csv) //è un long + una stringa static int compare_csv_rows_codcf_grmerc(const TObject** o1, const TObject** o2) { TToken_string& s1 = *(TToken_string*)*o1; TToken_string& s2 = *(TToken_string*)*o2; const long c1 = s1.get_long(29); const long c2 = s2.get_long(29); int cmp = c1 - c2; if (cmp == 0) { const TString80 g1 = s1.get(28); const TString80 g2 = s2.get(28); cmp = g1.compare(g2); } return cmp; } //funzione di ordinamento per il campo codart/codcf (campi 0/29 sul csv) //sono una stringa ed un long static int compare_csv_rows_codart_codcf(const TObject** o1, const TObject** o2) { TToken_string& s1 = *(TToken_string*)*o1; TToken_string& s2 = *(TToken_string*)*o2; const TString80 a1 = s1.get(0); const TString80 a2 = s2.get(0); int cmp = a1.compare(a2); if (cmp == 0) { const long c1 = s1.get_long(29); const long c2 = s2.get_long(29); cmp = c1 - c2; } return cmp; } //funzione di ordinamento per il campo grmerc/codcf (campo 28/29 sul csv) //sono una stringa ed un long static int compare_csv_rows_grmerc_codcf(const TObject** o1, const TObject** o2) { TToken_string& s1 = *(TToken_string*)*o1; TToken_string& s2 = *(TToken_string*)*o2; //deve ordinare sul campo codart ed eventualmente codcf const TString80 g1 = s1.get(28); const TString80 g2 = s2.get(28); int cmp = g1.compare(g2); if (cmp == 0) { const long c1 = s1.get_long(29); const long c2 = s2.get_long(29); cmp = c1 - c2; } return cmp; } void TStatistiche_ve_csv_recordset::aggiungi_ai_totali(const TRiga_documento& riga, const char tipo_dati) { //datadoc (serve a stabilire in quale colonna andrà a sommarsi l'importo della riga corrente const TDate datadoc = riga.doc().get_date(DOC_DATADOC); const int mese = datadoc.month(); real dato; //i dati da analizzare possono essere 'P'rezzi o 'Q'uantità if (tipo_dati == 'P') dato = riga.importo(true, false); //importo riga corrente else dato = riga.quantita(); //qta riga corrente //aggiorna l'importone del mese (ricordarsi che l'array parte da 0 quindi ci va -1 nell'indice) _tot[mese - 1] += dato; //aggiorna anche il totale annuale _tot[12] += dato; } //metodo per la scrittura del csv bool TStatistiche_ve_csv_recordset::aggiungi_riga(TDocument_recordset& righe, const char tipo_dati, const char tipo_stat, const char tipo_dettaglio) { const TDocumento& doc = righe.doc(righe.cursor()->curr()); const int nriga = righe.get(RDOC_NRIGA).as_int(); const TRiga_documento& riga = doc[nriga]; //se non ci sono dati da sommere è inutile perdere tempo! real dato; if (tipo_dati == 'P') dato = riga.importo(true, false); //'P'rezzo->importo else dato = riga.quantita(); //'Q'uantita if (dato.is_zero()) return false; //creazione di un nuovo record da esportare //esiste già questo codart? const TString80 codart = riga.get(RDOC_CODART); TString8 codcf = righe.get("DOC.CODCF").as_string(); codcf.right_just(6); const TString8 grmerc = righe.get("ANAMAG.GRMERC").as_string(); //attenzione ai vari casi di composizione chiave! //A=articolo G=GrMerc+Articolo C=Cliente+Articolo o Articolo+Cliente in base al tipo di stampa scelta TToken_string key; switch (tipo_stat) { case 'A': key.add(codart); break; case 'C': { switch (tipo_dettaglio) { case 'A': key.add(codart); key.add(codcf); break; case 'C': key.add(codcf); key.add(codart); break; case 'G': key.add(codcf); key.add(grmerc); break; case 'M': key.add(grmerc); key.add(codcf); break; default: key.add(codcf); //se non c'e' dettaglio basta codcf! break; } //switch(tipo_dettaglio... } break; case 'G': key.add(grmerc); key.add(codart); break; default: break; } //switch(tipo_stat... long numriga = trova_riga(key, tipo_stat, tipo_dettaglio); if (numriga < 0) { //codart new_rec(""); set(0, TVariant(codart)); //descrart const TString& descrart = cache().get(LF_ANAMAG, codart, ANAMAG_DESCR); set(1, TVariant(descrart)); set(28, TVariant(grmerc)); set(29, TVariant(codcf)); //re-sorting per codart (serve perchè solo il recordset ordinato è scannerizzabile con il plutonico metodo dicotomico) switch (tipo_stat) { case 'A': sort(compare_csv_rows_codart); break; case 'C': { switch (tipo_dettaglio) { case 'A': sort(compare_csv_rows_codart_codcf); break; case 'C': sort(compare_csv_rows_codcf_codart); break; case 'G': sort(compare_csv_rows_codcf_grmerc); break; case 'M': sort(compare_csv_rows_grmerc_codcf); break; default: sort(compare_csv_rows_codcf); break; //ordinamento quando non è richiesto il dettaglio } //switch(tipo_dettaglio... break; } //case 'C' case 'G': sort(compare_csv_rows_grmerc_codart); break; default: break; } //chiave.add(codart); numriga = trova_riga(key, tipo_stat, tipo_dettaglio); CHECKS(numriga >= 0, "Articolo bastardo ", (const char*)codart); } //riempimento del record secondo il tracciato: // codart+descrart+12*[impns+%incid]+grmerc+codcf //datadoc (serve a stabilire in quale colonna andrà a sommarsi l'importo della riga corrente const TDate datadoc = riga.doc().get_date(DOC_DATADOC); const int mese = datadoc.month(); const int column = mese * 2; //le colonne dei mesi sono gennaio=2,febbraio=4,marzo=6... //parte comune ai due casi real datone = get(column).as_real(); //valora totale della colonna mese corrispondente nel csv datone += dato; //aggiunge il valore riga al valore totale articolo del mese nel csv set(column, datone); //riscrive il valore totale aggiornato nella sua colonna(in formato stringa, cioè con la "," e non il ".") //aggiunge anche grmerc, utilizzando codartmag per risalire al grmerc (se usasse codart potrebbe non esistere in anagrafica!) set(28, grmerc); //aggiunge pure il cliente set(29, codcf); return true; } void TStatistiche_ve_csv_recordset::calcola_percentuali() { //%incidenza articolo sul mese = imp_articolo mese / imp tot mese for (bool ok = move_first(); ok; ok = move_next()) { real totale_anno; for (int i = 2; i <= 24; i += 2) { const real imp_art_mese = get(i).as_real(); //ricordando che l'array parte da 0 real perc = imp_art_mese * CENTO / _tot[i/2 - 1]; //calcola la % incidenza articolo sul totale per quel mese perc.round(5); set(i + 1, perc); totale_anno += imp_art_mese; } set(26, totale_anno); const real perc_anno = totale_anno * CENTO / _tot[12]; //calcola la % incidenza articolo sul totale annuale set(27, perc_anno); } } /////////////////////////////////////////////////////////// // APPLICAZIONE /////////////////////////////////////////////////////////// class TStatistiche_ve : public TSkeleton_application { virtual bool check_autorization() const {return false;} virtual const char * extra_modules() const {return "ve";} protected: void elabora(const TMask& mask) const; public: virtual bool create(); virtual void main_loop(); }; //metodo di base per la ricerca delle righe documento che soddisfano i parametri dell'utonto void TStatistiche_ve::elabora(const TMask& mask) const { //PARAMETRI DI STAMPA //-------------------- //tipo dati da estrarre (si utilizza nel metodo di scrittura riga e totali;stabilisce se si vuole qta o prezzo) const char tipo_dati = mask.get(F_TIPODATA)[0]; //tipologia di statistica (Articolo - GruppoMerceologico - Cliente) const char tipo_stat = mask.get(F_TIPOSTAT)[0]; //se sceglie per cliente ha 3 possibilità di dettaglio (''=nessuno,'C'=raccolto per cliente,'A'=raccolto per articolo) const char tipo_dettaglio = mask.get(F_DETTAGLIO)[0]; //stampa particolare bernazzalica in caso di UN SOLO CLIENTE selezionato nella stampa per cliente //in questo caso i totali non sono generali ma solo su quel cliente bool bernazzata = false; if (tipo_stat == 'C') { const long dacli = mask.get_long(F_DACODCLI); const long acodcli = mask.get_long(F_ACODCLI); if (dacli == acodcli && dacli > 0) bernazzata = true; } //creazione del csv recordset che verra' riempito dai record del recordset righe TStatistiche_ve_csv_recordset* csv = new TStatistiche_ve_csv_recordset(mask); //CALCOLO DEI TOTALI //------------------ //recordset per il calcolo dei totali mese/anno;servono per i calcoli futuri delle percentuali TString prequery; prequery << "USE RDOC KEY 1\n"; prequery << "SELECT ((CODARTMAG!=\"\")&&(BETWEEN(33->DATADOC,#DADATA,#ADATA))"; if (bernazzata) prequery << "&&(DOC.TIPOCF=='C')&&(DOC.CODCF==#DACODCLI)"; prequery <<")\n"; prequery << "JOIN DOC INTO PROVV==PROVV ANNO==ANNO CODNUM==CODNUM NDOC==NDOC\n"; prequery << "FROM CODNUM=#CODNUM ANNO=#ANNO PROVV='D'\n"; prequery << "TO CODNUM=#CODNUM ANNO=#ANNO PROVV='D'\n"; TDocument_recordset totali(prequery); totali.set_var("#CODNUM", TVariant(mask.get(F_CODNUM))); totali.set_var("#DADATA", mask.get_date(F_DADATA)); totali.set_var("#ADATA", mask.get_date(F_ADATA)); totali.set_var("#ANNO", TVariant((long)mask.get_int(F_ANNO))); if (bernazzata) totali.set_var("#DACODCLI", mask.get_long(F_DACODCLI)); const long totali_items = totali.items(); if (totali_items > 0) { //E crea pure la progind.. TProgind pi(totali_items, TR("Calcolo totali mensili ed annuali"), true, true); //Scansione del recordset trovato for (bool ok = totali.move_first(); ok; ok = totali.move_next()) { if (!pi.addstatus(1)) break; const TDocumento& doc = totali.doc(totali.cursor()->curr()); const int nriga = totali.get(RDOC_NRIGA).as_int(); //scrive sul CSV i campi che servono al file di excel e al report csv->aggiungi_ai_totali(doc[nriga], tipo_dati); } } //CREAZIONE QUERY //--------------- //scatta la query per la costruzione del recordset TString query; query << "USE RDOC KEY 1\n"; query << "SELECT ((CODARTMAG!=\"\")&&(BETWEEN(33->DATADOC,#DADATA,#ADATA))&&"; switch (tipo_stat) { case 'A': query << "(BETWEEN(CODARTMAG,#DACODART,#ACODART)))\n"; break; case 'C': query << "(DOC.TIPOCF=='C')&&(BETWEEN(DOC.CODCF,#DACODCLI,#ACODCLI)))\n"; break; case 'G': query << "(BETWEEN(ANAMAG.GRMERC,#DAGRMERC,#AGRMERC)))\n"; break; default: break; } //parte comune ai vari casi di statistica della query query << "JOIN DOC INTO PROVV==PROVV ANNO==ANNO CODNUM==CODNUM NDOC==NDOC\n"; //parte non comune (deve joinare anamag per avere il grmerc if (tipo_stat == 'G' || (tipo_stat == 'C' && (tipo_dettaglio == 'G' || tipo_dettaglio == 'M'))) query << "JOIN ANAMAG INTO CODART==CODARTMAG\n"; //ri-parte comune query << "FROM CODNUM=#CODNUM ANNO=#ANNO PROVV='D'\n"; query << "TO CODNUM=#CODNUM ANNO=#ANNO PROVV='D'\n"; //CREAZIONE RECORDSET //------------------- TDocument_recordset righe(query); //parte comune di settaggio variabili righe.set_var("#CODNUM", TVariant(mask.get(F_CODNUM))); righe.set_var("#DADATA", mask.get_date(F_DADATA)); righe.set_var("#ADATA", mask.get_date(F_ADATA)); righe.set_var("#ANNO", TVariant((long)mask.get_int(F_ANNO))); switch(tipo_stat) { case 'A': righe.set_var("#DACODART", TVariant(mask.get(F_DACODART))); righe.set_var("#ACODART", TVariant(mask.get(F_ACODART))); break; case 'C': righe.set_var("#DACODCLI", TVariant(mask.get(F_DACODCLI))); righe.set_var("#ACODCLI", TVariant(mask.get(F_ACODCLI))); break; case 'G': righe.set_var("#DAGRMERC", TVariant(mask.get(F_DAGRMERC))); righe.set_var("#AGRMERC", TVariant(mask.get(F_AGRMERC))); break; default: break; } //CREAZIONE STAMPE/ESPORTAZIONI //----------------------------- //se trova (si spera!) almeno una rigadoc buona comincia il bello del programma const long righe_items = righe.items(); if (righe_items > 0) { //E crea pure la progind.. TProgind pi(righe_items, TR("Generazione file statistiche..."), true, true); //Scansione del recordset trovato for (bool ok = righe.move_first(); ok; ok = righe.move_next()) { if (!pi.addstatus(1)) break; //scrive sul CSV i campi che servono al file di excel e al report csv->aggiungi_riga(righe, tipo_dati, tipo_stat, tipo_dettaglio); } //aggiorna le colonne delle percentuali csv->calcola_percentuali(); //se richiesto il file in formato excel... if (mask.get_bool(F_EXCEL)) { //crea la riga con le intestazioni dei campi e la mette all'inizio csv->compila_intestazione(); //salva il file come richiesto; il nome del file viene deciso in base al tipo di statistica richiesta; il file alla fine.. //..della storia è sempre lo stesso ma ordinato in modo diverso TString path = mask.get(F_PATH); path.lower(); path << "\\stat"; switch (tipo_stat) { case 'A': path << "_art.xls"; break; case 'C': path << "_cli.xls"; break; case 'G': path << "_gmc.xls"; break; default: break; } csv->save_as(path, fmt_text); //accoppa la riga con le intestazioni dei campi csv->destroy(0); #ifdef DBG xvt_sys_goto_url(path, "open"); #endif } //REPORT DI STAMPA //---------------- //creazione del report di stampa TStatistiche_ve_report rep; bool stampa; //in base alle scelte dell'utonto stabilisce quale report usare switch(tipo_stat) { case 'A': stampa = rep.load("ve3800a"); break; case 'C': { switch (tipo_dettaglio) { case 'A': stampa = rep.load("ve3800e"); break; //raggr per art. - dett. per cli case 'C': stampa = rep.load("ve3800d"); break; //raggr. per cli - dett. per art case 'G': stampa = rep.load("ve3800f"); break; //raggr. per cli - dett. per grmerc case 'M': stampa = rep.load("ve3800g"); break; //raggr. per grmerc - dett. per cli default: stampa = rep.load("ve3800c"); break; } } break; case 'G': stampa = rep.load("ve3800b"); break; default: break; } //setta il recordset... rep.set_recordset(csv); if (stampa) { TReport_book book; stampa = book.add(rep); if (stampa) book.print_or_preview(); } } //if(righe_items>0... else delete csv; } void TStatistiche_ve::main_loop() { TStatistiche_ve_mask mask; while (mask.run() == K_ENTER) { elabora(mask); } } bool TStatistiche_ve::create() { //controlla se la chiave ha l'autorizzazione a questo programma Tdninst dninst; if (!dninst.can_I_run(true)) return error_box(TR("Programma non autorizzato!")); return TSkeleton_application::create(); } int ve3800(int argc, char* argv[]) { TStatistiche_ve stat_anal; stat_anal.run(argc, argv, TR("Statistiche di vendita")); return 0; }