#include #include #include #include "svlib01.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("Giorno|Settimana|Quindicina|Mese|" "Bimestre|Trimestre|Quadrimestre|Semestre|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; 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; default :break; } break; case fs_quadrimestrale: switch(f2) { case fs_bimestrale :return 2; case fs_mensile :return 4; case fs_quindicinale :return 8; default :break; } case fs_trimestrale: switch(f2) { case fs_mensile :return 3; case fs_quindicinale :return 6; case fs_settimanale :return 13; default :break; } break; case fs_bimestrale: switch(f2) { case fs_mensile :return 2; case fs_quindicinale :return 4; default :break; } break; case fs_mensile: if (f2 == fs_quindicinale) return 2; 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; } //@cmember 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) { return long(anno)*long(last_period(anno,f))+periodo; } 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; } 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"); _omaggi = ini.get_bool("StatOmaggi"); _omaggio_is_merce = ini.get_bool("OmaggioIsMerce"); _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"); } 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(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) { 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) { 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; default : ok = FALSE; break; } } if (ok && !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::sub(const TRiga_documento& rdoc) { bool ok = can_add(rdoc); if (ok) { TStats_data& data = find(rdoc); data._quantita -= rdoc.quantita(); data._valore -= rdoc.imponibile(); } return ok; } bool TStats_agg::add(const TRiga_documento& rdoc) { bool ok = can_add(rdoc); if (ok) { TStats_data& data = find(rdoc); data._valore += rdoc.imponibile(); real qta = rdoc.quantita(); if (qta.is_zero()) qta = 1.0; data._quantita += qta; } return ok; } bool TStats_agg::update() { // eliminato perché rompeva a Sandro // TProgind pi(_data.items(), "Aggiornamento statistiche", FALSE, TRUE, 60); 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()) { //pi.addstatus(1); 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("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* val) { static TString80 chiavi; 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 ; } /////////////////////////////////////////////////////////// // Cache /////////////////////////////////////////////////////////// #define RWCACHE_SIZE 100 // funzione di default: prende una chiave a caso chiave const TString & TRWrecord_cache::getkey2discard() { THash_object * o=get_some_obj(); CHECK(o,"E' stata chiamata la funzione getkey2discard con la cache vuota"); return o->key(); } void TRWrecord_cache::discard(const TString & vittima) { if (items()) { if (_flags.is_key(vittima)) { const char fl=((TString &)_flags[vittima])[1]; // record modificato o nuovo int err; TRectype & rec=(TRectype & )TRecord_cache::get(vittima); file().curr()=rec; if (fl == 'D') { err=file().rewrite(); if (err!=NOERR) error_box("Errore nella riscrittura della cache"); } else { err=file().write(); if (err!=NOERR) if (err == _isreinsert) file().rewrite(); else error_box("Errore nella scrittura della cache"); } _flags.remove(vittima); } _cache.remove(vittima); } } const TRectype& TRWrecord_cache::get(const char* chiave) { if (items()>=RWCACHE_SIZE) discard(getkey2discard()); const TRectype & rec=TRecord_cache::get(chiave); if (io_result() != NOERR) { // record non trovato: è nuovo _flags.add(chiave,new TString("N")); } return rec; } void TRWrecord_cache::put(const TRectype &r) { test_firm(); TToken_string cachekey; if (!r.empty()) { const RecDes* recd = r.rec_des(); // Descrizione del record della testata const KeyDes& kd = recd->Ky[key_number()-1]; // Elenco dei campi della chiave for (int i = file().tab() ? 1: 0; i < kd.NkFields; i++) // Riempie la chiave selezionata { const int nf = kd.FieldSeq[i] % MaxFields; const RecFieldDes& rf = recd->Fd[nf]; cachekey.add(r.get(rf.Name)); } } TObject* obj = _cache.objptr(cachekey); if (obj != NULL) { // esiste in cache ; tenta di settare il flag a "D"irty; se il flag esiste già è a TRectype & rec=(TRectype &)*obj; rec=r; _flags.add(cachekey , new TString("D")); } else { // non esiste in cache obj = rec2obj(r); _cache.add(cachekey, obj); // qui assume che non esista nemmeno su file, perciò sia "N"uovo; al flush correggerà l'errore _flags.add(cachekey , new TString("N")); } if (items()>=RWCACHE_SIZE) discard(getkey2discard()); } void TRWrecord_cache::clear() { while (items()>0) { const TString & vittima=getkey2discard(); if (_flags.is_key(vittima)) _flags.remove(vittima); _cache.remove(vittima); } } void TRWrecord_cache::flush() { while (items()>0) discard(TRWrecord_cache::getkey2discard()); } THash_object * TRWrecord_cache::get_some_obj() { if (items()==0) return NULL; THash_object * o; while ((o=_cache.get_hashobj()) == NULL) ; return o; } TRWrecord_cache::TRWrecord_cache(int num, int key, bool lock) :TRecord_cache(num,key) { if (lock) file().lock(); } TRWrecord_cache::~TRWrecord_cache() { flush(); } // 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 (bool lock): TRWrecord_cache( LF_SVSTAT,1,lock) { }