#include #include #include "svlib01.h" #include "../ve/velib.h" #include "svriep.h" #include "svstat.h" /////////////////////////////////////////////////////////// // Funzioni di utilita' comune /////////////////////////////////////////////////////////// #define FREQ_CODES " GSQ12346A" TFrequenza_statistiche char2frequency(char c) { const TFixed_string list(FREQ_CODES); TFrequenza_statistiche f = TFrequenza_statistiche(list.find(c)); return f; } char frequency2char(TFrequenza_statistiche f) { const char* list = FREQ_CODES; return list[f]; } TString & char2freqname(char c) { static TString16 s; const TFixed_string list(FREQ_CODES); TToken_string std_descr; std_descr.add(TR("Giorno")); std_descr.add(TR("Settimana")); std_descr.add(TR("Quindicina")); std_descr.add(TR("Mese")); std_descr.add(TR("Bimestre")); std_descr.add(TR("Trimestre")); std_descr.add(TR("Quadrimestre")); std_descr.add(TR("Semestre")); std_descr.add(TR("Anno")); s= std_descr.get(list.find(c)-1); return s; } int last_period(const TDate & d, TFrequenza_statistiche f) { return last_period(d.year(),f); } int last_period(int anno, TFrequenza_statistiche f) { int n = 0; switch(f) { case fs_giornaliera : n = 365 + (TDate::last_day(2, anno) == 29); break; case fs_settimanale : n = 52; break; case fs_quindicinale : n = 24; break; case fs_mensile : n = 12; break; case fs_bimestrale : n = 6; break; case fs_trimestrale : n = 4; break; case fs_quadrimestrale : n = 3; break; case fs_semestrale : n = 2; break; case fs_annuale : n = 1; break; default: NFCHECK("Invalid frequency"); } return n; } int divide(TFrequenza_statistiche f1, TFrequenza_statistiche f2) { CHECK(f1 >= f2 && f2 != fs_nulla, "Invalid frequency division"); if (f1 == f2) return 1; switch (f1) { case fs_annuale: switch(f2) { case fs_semestrale : return 2; case fs_quadrimestrale: return 3; case fs_trimestrale : return 4; case fs_bimestrale : return 6; case fs_mensile : return 12; case fs_quindicinale : return 24; case fs_settimanale : return 52; case fs_giornaliera : return 365 ; default : break; } break; case fs_semestrale: switch(f2) { case fs_trimestrale :return 2; case fs_bimestrale :return 3; case fs_mensile :return 6; case fs_quindicinale :return 12; case fs_settimanale :return 26; // case fs_giornaliera : return (365)/2; default :break; } break; case fs_quadrimestrale: switch(f2) { case fs_bimestrale :return 2; case fs_mensile :return 4; case fs_quindicinale :return 8; // case fs_giornaliera : return 365/3; default :break; } case fs_trimestrale: switch(f2) { case fs_mensile :return 3; case fs_quindicinale :return 6; case fs_settimanale :return 13; // case fs_giornaliera : return 365/4; default :break; } break; case fs_bimestrale: switch(f2) { case fs_mensile :return 2; case fs_quindicinale :return 4; // case fs_giornaliera : return 365/6; default :break; } break; case fs_mensile: if (f2 == fs_quindicinale) return 2; break; case fs_quindicinale : switch(f2) { case fs_giornaliera : return 15; default :break; } break; case fs_settimanale : switch(f2) { case fs_giornaliera : return 7; default :break; } break; default: break; } return 0; } //@cmember ritorna la parte "periodo" di una data (l'anno si ottiene da year()) // tutti i periodi cominciano da 1 int date2period(const TDate& datadoc, TFrequenza_statistiche freq) { int classe; if (freq > fs_settimanale) { classe = datadoc.month(); switch(freq) { case fs_quindicinale: classe = (classe-1) * 2 + 1; if (datadoc.day() > 15) classe++; break; case fs_bimestrale: classe = (classe-1) / 2 + 1; break; case fs_trimestrale: classe = (classe-1) / 3 + 1; break; case fs_quadrimestrale: classe = (classe-1) / 4 + 1; break; case fs_semestrale: classe = (classe-1) / 6 + 1; break; case fs_annuale: classe = 1; break; default: // fs_mensile break; } } else { const TDate primo(1, 1, datadoc.year()); classe = int(datadoc - primo); if (freq == fs_settimanale) { classe /= 7; if (classe > 51) classe = 51; } classe++; } return classe; } long date2long(const TDate d, TFrequenza_statistiche f) { return long(d.year())*long(last_period(d.year(),f))+date2period(d,f); } long period2long(const int anno, const int periodo, TFrequenza_statistiche f) { if (f==fs_annuale) return long(anno)*long(100)+periodo; else return long(anno)*long(last_period(anno,f))+periodo; } // calcola approssimativamente la data corrispondente ad una coppia anno/periodo const TDate & period2date(const int anno, int periodo, TFrequenza_statistiche f) { static TDate d; d.set_year(anno+(periodo-1)/last_period(anno,f)); d.set_month(1); d.set_day(1); periodo=periodo % last_period(anno,f); switch(f) { case fs_giornaliera: d+=periodo-1; break; case fs_settimanale: d+=(periodo-1)*7; break; case fs_quindicinale: d+=(periodo-1)*15; break; case fs_mensile: d.set_month(periodo); break; case fs_bimestrale: d.set_month(periodo*2); break; case fs_trimestrale: d.set_month(periodo*3); break; case fs_quadrimestrale:d.set_month(periodo*4); break; case fs_semestrale: d.set_month(periodo*6); break; case fs_annuale: case fs_nulla: default: break; } return d; } const TDate& floor(TDate& data, TFrequenza_statistiche freq) { switch (freq) { case fs_settimanale: { /* const TDate primo(1, 1, data.year()); int settimana = int((data - primo) / 7); if (settimana > 51) settimana = 51; data = primo; data += settimana * 7; */ data -= data.wday()-1; } break; case fs_quindicinale: data.set_day(data.day() <= 15 ? 1 : 16); break; case fs_mensile: data.set_day(1); break; case fs_bimestrale: data.set_day(1); data.set_month(((data.month()-1) / 2) * 2 + 1); break; case fs_trimestrale: data.set_day(1); data.set_month(((data.month()-1) / 3) * 3 + 1); break; case fs_quadrimestrale: data.set_day(1); data.set_month(((data.month()-1) / 4) * 4 + 1); break; case fs_semestrale: data.set_day(1); data.set_month(data.month() <= 6 ? 1 : 7); break; case fs_annuale: data.set_day(1); data.set_month(1); break; default: break; } return data; } const TDate& ceil(TDate& data, TFrequenza_statistiche freq) { floor(data, freq); switch (freq) { case fs_settimanale: data += 6; if (data.month() == 12 && data.day() >= 29) data.set_end_month(); break; case fs_quindicinale: if (data.day() == 1) data.set_day(15); else data.set_end_month(); break; case fs_mensile: data.set_end_month(); break; case fs_bimestrale: data.addmonth(1); data.set_end_month(); break; case fs_trimestrale: data.addmonth(2); data.set_end_month(); break; case fs_quadrimestrale: data.addmonth(3); data.set_end_month(); break; case fs_semestrale: data.addmonth(5); data.set_end_month(); break; case fs_annuale: data.set_month(12); data.set_day(31); break; default: break; } return data; } /////////////////////////////////////////////////////////// // TStats_agg /////////////////////////////////////////////////////////// void TStats_agg::init() { _ditta = prefix().get_codditta(); TConfig ini(CONFIG_DITTA, "sv"); _frequenza = char2frequency(ini.get_char("Frequenza")); _merce = ini.get_bool("StatMerce"); _prestazioni = ini.get_bool("StatPrestazioni"); _spesedoc = ini.get_bool("StatSpesedoc"); _omaggi = ini.get_bool("StatOmaggi"); _omaggio_is_merce = ini.get_bool("OmaggioIsMerce"); _omaggi_valore = ini.get_bool("OmaggioValore"); _art_nocode = ini.get_bool("ArtNoCode"); _art_noanag = ini.get_bool("ArtNoAnag"); _art_noanag_grp = ini.get_bool("ArtNoAnagGrp"); _agente = ini.get_bool("AgenteGrp"); _cliente = ini.get_bool("ClienteGrp"); _zona = ini.get_bool("ZonaGrp"); _giacenza = ini.get_bool("GiacenzaGrp"); _magazzino = ini.get_bool("MagazzinoGrp"); _catvend = ini.get_bool("CatVendGrp"); _valfield = ini.get("ValFld"); } void TStats_agg::test_firm() const { const long ditta = prefix().get_codditta(); if (ditta > 0 && ditta != _ditta) { CHECK(_data.items() == 0, "Non cambiare ditta durante un'operazione di ricalcolo!"); ((TStats_agg*)this)->init(); } } // scrive la chiave nel record // utilizza la convenzione in TStats_agg::find per la tokenstring void TStats_agg::put_key(TRectype& stat, TToken_string& key) const { CHECK(stat.num() == LF_SVRIEP, "Ci vuole un record delle statistiche"); stat.zero(); key.restart(); stat.put(SVR_ANNO, key.get()); stat.put(SVR_PERIODO, key.get()); stat.put(SVR_TIPODOC, key.get()); stat.put(SVR_TIPOART, key.get()); stat.put(SVR_CODART, key.get()); stat.put(SVR_UMQTA, key.get()); stat.put(SVR_CODAG, key.get()); stat.put(SVR_TIPOCF, key.get()); stat.put(SVR_CODCF, key.get()); stat.put(SVR_ZONA, key.get()); stat.put(SVR_GIAC, key.get()); stat.put(SVR_MAG, key.get()); stat.put(SVR_CATVEN, key.get()); } // ricerca la chiave nell'assocarray // la convenzione per la tokenstring è utilizzata in put_key TStats_agg::TStats_data& TStats_agg::find(const TRiga_documento& rdoc) { test_firm(); const TDocumento& doc = rdoc.doc(); const TDate datadoc = doc.get_date(DOC_DATADOC); TToken_string key(64); key.add(datadoc.year()); key.add(date2period(datadoc)); key.add(doc.get(DOC_TIPODOC)); char tipo = rdoc.tipo().tipo(); if (tipo == RIGA_OMAGGI && _omaggio_is_merce) tipo = RIGA_MERCE; key.add(tipo); TString80 codart; if (tipo != RIGA_PRESTAZIONI && tipo != RIGA_SPESEDOC) { codart = rdoc.get(RDOC_CODARTMAG); if (codart.empty() && !_art_noanag_grp) codart = rdoc.get(RDOC_CODART); } else codart = rdoc.get(RDOC_CODART); key.add(codart); // l'unità di misura non viene MAI RAGGRUPPATA sul riepilogo statistiche! key.add(rdoc.get(RDOC_UMQTA)); if (_agente) key.add(doc.get(DOC_CODAG)); else key.add(""); if (_cliente) { key.add(doc.get(DOC_TIPOCF)); key.add(doc.get(DOC_CODCF)); } else { key.add(""); key.add(""); } if (_zona) key.add(doc.get(DOC_ZONA)); else key.add(""); if (_giacenza) key.add(rdoc.get(RDOC_LIVELLO)); else key.add(""); if (_magazzino) key.add(rdoc.get(RDOC_CODMAG)); else key.add(""); if (_catvend) key.add(doc.get(DOC_CATVEN)); else key.add(""); TStats_data* ptr = (TStats_data*)_data.objptr(key); if (ptr == NULL) { ptr = new TStats_data; _data.add(key, ptr); } return *ptr; } bool TStats_agg::can_add(const TRiga_documento& rdoc) const { test_firm(); const TTipo_documento& tip = rdoc.doc().tipo(); bool ok = tip.statistiche(); if (ok) // Controlla se il tipo documento e' attivo per le statistiche { const char tipo = rdoc.tipo().tipo(); switch(tipo) { case RIGA_MERCE : ok = _merce; break; case RIGA_OMAGGI : ok = _omaggi; break; case RIGA_PRESTAZIONI: ok = _prestazioni; break; case RIGA_SPESEDOC : ok = _spesedoc; break; default : ok = false; break; } if (ok && (tipo == RIGA_MERCE || tipo == RIGA_OMAGGI) && !rdoc.is_articolo()) { const TString& codart = rdoc.get(RDOC_CODART); if (codart.empty()) ok = _art_nocode; else ok = _art_noanag; } } return ok; } void TStats_agg::reset() { _data.destroy(); } bool TStats_agg::algebric_sum(const TRiga_documento& rdoc, int sign) { const bool ok = can_add(rdoc); if (ok) { TStats_data& data = find(rdoc); real val_riga; if (_valfield.empty()) { if (_omaggi_valore && rdoc.is_omaggio()) val_riga = rdoc.imponibile_omaggio(2); // Considera il valore indipendentemente dall'addebito IVA else val_riga = rdoc.imponibile(); } else val_riga = rdoc.get_real(_valfield); const TDocumento& d = rdoc.doc(); if (d.in_valuta()) { TCurrency_documento v(val_riga, d); v.change_to_firm_val(); val_riga = v.get_num(); } if (sign < 0) data._valore -= val_riga; else data._valore += val_riga; real qta = rdoc.quantita(); if (qta.is_zero()) qta = UNO; if (sign < 0) data._quantita -= qta; else data._quantita += qta; } return ok; } bool TStats_agg::sub(const TRiga_documento& rdoc) { return algebric_sum(rdoc, -1); } bool TStats_agg::add(const TRiga_documento& rdoc) { return algebric_sum(rdoc, +1); } bool TStats_agg::empty() { TLocalisamfile stat(LF_SVRIEP); // return stat.empty(); // Per ora non e' affatto affidabile return stat.first() != NOERR; } bool TStats_agg::update() { test_firm(); bool ok = TRUE; TToken_string key(64); TLocalisamfile stat(LF_SVRIEP); stat.set_curr(new TSVriep_record); TRectype& curr = stat.curr(); _data.restart(); for (THash_object* h = _data.get_hashobj(); h; h = _data.get_hashobj()) { TStats_data& data = (TStats_data&)h->obj(); if (data._quantita.is_zero() && data._valore.is_zero()) continue; key = h->key(); bool saved = FALSE; while (!saved) { put_key(curr, key); int err = stat.read(_isequal, _lock); if (err != NOERR) put_key(curr, key); real quantita(curr.get(SVR_QUANTITA)); real valore(curr.get(SVR_VALORE)); quantita += data._quantita; valore += data._valore; curr.put(SVR_QUANTITA, quantita); curr.put(SVR_VALORE, valore); if (err == NOERR) err = stat.rewrite(); else err = stat.write(); switch(err) { case NOERR : saved = TRUE; break; case _isreinsert: break; default : saved = !yesno_box(FR("Errore %d nell'aggiornamento statistiche:" "Si desidera ritentare?"), err); break; } } } reset(); return ok; } TStats_agg::TStats_agg() : _ditta(-1) { } /////////////////////////////////////////////////////////// // Record del file svriep /////////////////////////////////////////////////////////// TSVriep_record:: TSVriep_record(): TVariable_rectype(LF_SVRIEP) {} TSVriep_record::~TSVriep_record() {} const TString & TSVriep_record::get_str(const char* fieldname) const { static TString80 chiavi; chiavi=TRectype::get_str(SVR_CHIAVI); if (strcmp(fieldname,SVR_CODART)==0) return chiavi.mid(0,20); else if (strcmp(fieldname,SVR_CODAG)==0) return chiavi.mid(20,5); else if (strcmp(fieldname,SVR_TIPOCF)==0) return chiavi.mid(25,1); else if (strcmp(fieldname,SVR_CODCF)==0) return chiavi.mid(26,6); else if (strcmp(fieldname,SVR_ZONA)==0) return chiavi.mid(32,3); else if (strcmp(fieldname,SVR_GIAC)==0) return chiavi.mid(35,15); else if (strcmp(fieldname,SVR_CATVEN)==0) return chiavi.mid(50,3); else if (strcmp(fieldname,SVR_MAG)==0) return chiavi.mid(53,3); else return TRectype::get_str(fieldname); } void TSVriep_record::put_str(const char* fieldname, const char* v) { const TString80 val(v); TString80 chiavi(TRectype::get_str(SVR_CHIAVI)); if (strcmp(fieldname,SVR_CODART)==0) chiavi.overwrite(val,0); else if (strcmp(fieldname,SVR_CODAG)==0) chiavi.overwrite(val,20); else if (strcmp(fieldname,SVR_TIPOCF)==0) chiavi.overwrite(val,25); else if (strcmp(fieldname,SVR_CODCF)==0) chiavi.overwrite(val,26); else if (strcmp(fieldname,SVR_ZONA)==0) chiavi.overwrite(val,32); else if (strcmp(fieldname,SVR_GIAC)==0) chiavi.overwrite(val,35); else if (strcmp(fieldname,SVR_CATVEN)==0) chiavi.overwrite(val,50); else if (strcmp(fieldname,SVR_MAG)==0) chiavi.overwrite(val,53); else { TRectype::put_str(fieldname,val); return ; } TRectype::put_str(SVR_CHIAVI,chiavi); return ; } /////////////////////////////////////////////////////////// // cerca di restituire una chiave di bassa priorità (livello const TString & TStat_cache::getkey2discard() { THash_object * o; CHECK(items()>0,"E' stata chiamata la funzione getkey2discard con la cache vuota"); while ((o=_cache.get_hashobj()) == NULL) ; if (((TRectype &)o->obj()).get_int(SVS_LIVELLO)<=1) { while ((o=_cache.get_hashobj()) == NULL) ; if (((TRectype &)o->obj()).get_int(SVS_LIVELLO)==0) while ((o=_cache.get_hashobj()) == NULL) ; } return o->key(); } TStat_cache::TStat_cache (TLocalisamfile *f, bool lock): TRWrecord_cache( f,1,lock) { } // azzera la cache e distrugge il file! void TStat_cache::zap() { clear(); int err=NOERR; while (err==NOERR) { err=file().first(); if (err==NOERR) err=file().remove(); } }