#include #include #include #include #include "lvlib.h" #include "lvpasplan.h" #include "lvrconsplan.h" #include "lv2200a.h" /////////////////////////////////////// //// TGESTIONE_PLANNING_MASK //// /////////////////////////////////////// //classe TGestione_planning_mask class TGestione_planning_mask : public TAutomask { TToken_string _rigaoriginale; private: void fill_sheet(); void erase_sheet(); bool changed(TToken_string& oldrow, TToken_string& newrow, short id); int codice_riga(const TDate& data); void aggiorna_plan(); void rewrite_all(TToken_string& rigamodificata, TDate& data, long codplan); bool cancella_riga(); void nuova_riga(); bool richiesta_modifica(); public: virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); TGestione_planning_mask (const char* name) : TAutomask(name) {} }; static int sort_by_stop(TSheet_field& sheet, int r1, int r2) { TToken_string& row1 = sheet.row(r1); TToken_string& row2 = sheet.row(r2); const TDate d1 = row1.get(0); const TDate d2 = row2.get(0); int dif = d1 - d2; if (dif == 0) { dif = row1.get_int(2) - row2.get_int(2); if (dif == 0) dif = row1.get_int(3) - row2.get_int(3); } return dif; } static int sort_by_time(TSheet_field& sheet, int r1, int r2) { TToken_string& row1 = sheet.row(r1); TToken_string& row2 = sheet.row(r2); const TDate d1 = row1.get(0); const TDate d2 = row2.get(0); int dif = d1 - d2; if (dif == 0) { dif = row1.get_int(2) - row2.get_int(2); if (dif == 0) { int time1; int time2; TString8 orario; TString4 tmp; //converto in numero il primo orario orario = row1.get(4); tmp = orario.mid(0,2); time1 = atoi(tmp) * 100; tmp = orario.mid(3,2); time1 += atoi(tmp); //converto in numero il secondo orario orario = row2.get(4); tmp = orario.mid(0,2); time2 = atoi(tmp) * 100; tmp = orario.mid(3,2); time2 += atoi(tmp); dif = time1 - time2; } } return dif; } //FILL_SHEET: questo metodo si occupa di riempire lo sheet dinamicamente in base ai parametri che l'utente decide void TGestione_planning_mask::fill_sheet() { if (field(F_DADATA).empty()) return; TDate adata; if (field(F_ADATA).empty()) { //instanzio un recordset sui planning, filtrando sui coditi TISAM_recordset rplan("USE LVRCONSPLAN KEY 2\nFROM DTCONS=#DADATA"); rplan.set_var("#DADATA", get_date(F_DADATA)); rplan.move_last(); adata = rplan.get(LVRCONSPLAN_DTCONS).as_date(); } else adata = get_date(F_ADATA); TString query = "USE LVRCONSPLAN KEY 2"; const long coditi = get_long(F_CODITI); const long codcli = get_long(F_CODCF); if (coditi || codcli) { query << " SELECT "; if (codcli > 0) query << "(CODCF=" << codcli << ')'; if (coditi > 0) { if (codcli > 0) query << "&&"; query << "(CODITI=" << coditi << ')'; } } query << "\n"; query << "FROM DTCONS=" << get(F_DADATA) << "\n"; query << "TO DTCONS=" << adata << "\n"; TISAM_recordset plan(query); TProgind pi(plan.items(), TR("Elaborazione dati giri in corso..."), true, true); TSheet_field& sheet = sfield(F_PLAN); TMask& sheetmask = sheet.sheet_mask(); TRelation& rel = *plan.cursor()->relation(); //accesso al file delle righe sheet.destroy(); for (bool ok = plan.move_first(); ok; ok = plan.move_next()) { if (!pi.addstatus(1)) break; TToken_string& row = sheet.row(-1); //crea una nuova riga dello sheet //scandisco nella maschera tutti i campi che hanno un field FOR_EACH_MASK_FIELD(sheetmask,i,f) { const TFieldref* fr = f->field(); //leggo il valore dalla relation if (fr != NULL) { const int pos = sheet.cid2index(f->dlg()); row.add(fr->read(rel),pos); //metto il valore letto nella posizione corretta nella TToken_string } } sheet.check_row(sheet.items()-1); //fa fare alla maschera la decodifica dei codici e aggiorna la TToken_string } switch (ini_get_int(CONFIG_DITTA, "lv", "Ordgir")) { case 1: sheet.sort(sort_by_stop); break; case 2: sheet.sort(sort_by_time); break; default: break; } sheet.force_update(); } //ERASE_SHEET: questo metodo si occupa di cancellare tutti i dati visuliazzati nello sheet void TGestione_planning_mask::erase_sheet() { if (field(F_DADATA).empty() || field(F_ADATA).empty()) return; TString query = "USE LVRCONSPLAN KEY 2"; const long coditi = get_long(F_CODITI); const long codcli = get_long(F_CODCF); if (coditi || codcli) { query << " SELECT "; if (codcli > 0) query << "(CODCF=" << codcli << ')'; if (coditi > 0) { if (codcli > 0) query << "&&"; query << "(CODITI=" << coditi << ')'; } } query << "\n"; query << "FROM DTCONS=" << get(F_DADATA) << "\n"; query << "TO DTCONS=" << get(F_ADATA) << "\n"; TISAM_recordset plan(query); if (yesno_box(FR("Si desidera eliminare %ld righe?"),plan.items())) { TProgind pi(plan.items(), TR("Cancellazione giri in corso..."), false, true); TLocalisamfile& rplan = plan.cursor()->file(); for (bool ok = plan.move_first(); ok; ok = plan.move_next()) { if (!pi.addstatus(1)) break; rplan.remove(); } TSheet_field& sheet = sfield(F_PLAN); sheet.destroy(); sheet.force_update(); } } //CHANGED: metodo che confronta due due campi di una TToken_string e restitusce true se sono diversi bool TGestione_planning_mask::changed(TToken_string& oldrow, TToken_string& newrow, short id) { const TSheet_field& sheet = sfield(F_PLAN); const int i = sheet.cid2index(id); TString80 oldval(oldrow.get(i)); oldval.trim(); TString80 newval(newrow.get(i)); newval.trim(); return oldval != newval; } //CODICE_RIGA: metodo che trova il primo codiceriga libero per un certo codplan int TGestione_planning_mask::codice_riga(const TDate& data) { const long codplan = data.date2ansi(); //per ora memorizzo zero int ptriga = 0; //se esiste almeno una riga, memorizzo il codriga dell'ultima TISAM_recordset rplan("USE LVRCONSPLAN KEY 1\nFROM CODPLAN=#CODPLAN\nTO CODPLAN=#CODPLAN"); rplan.set_var("#CODPLAN",TVariant(codplan)); if (rplan.move_last()) ptriga = rplan.get(LVRCONSPLAN_CODRIGA).as_int(); ptriga++; //incremento il codriga interessato return ptriga; } //AGGRIONA_PLAN: metodo che gestisce l'aggiornamanto dinamico dello sheet void TGestione_planning_mask::aggiorna_plan() { //bool che indicano se effettivamente devo fare una rewrite dei dati... //...se ho modificato un autista e/o un mezzo, questo va modificato su tutte... //...le righe di quel planning se confermato dall'utente bool dorewrite = false; bool updatecar = false; //variabili che mi premettono di lavorare sullo sheet TSheet_field& sheet = sfield(F_PLAN); TToken_string& rigamodificata = sheet.row(sheet.selected()); //metto in una stringa la riga che sto analizzando //instanzio un isam file sulla tabella dei planning TLocalisamfile rplan(LF_LVRCONSPLAN); //calcolo il codplan originale const int posdata = sheet.cid2index(F_S_DATA); const TDate dataor = _rigaoriginale.get(posdata); const long codplanor = dataor.date2ansi(); //calcolo il codriga originale const int posriga = sheet.cid2index(F_S_RIGA); const int codrigaor = _rigaoriginale.get_int(posriga); //creo i nuovi codplan e codriga TDate data = rigamodificata.get(posdata); const long codplan = data.date2ansi(); const int codriga = codice_riga(data); //se esiste almeno una riga per quel planning, leggo il record corrispondente if (codrigaor > 0) { rplan.zero(); rplan.put(LVRCONSPLAN_CODPLAN,codplanor); rplan.put(LVRCONSPLAN_CODRIGA,codrigaor); rplan.read(); } //se ho modificato la data di una riga esistente o se se ho creata una nuova, allora if (changed(_rigaoriginale,rigamodificata,F_S_DATA) || codrigaor <= 0) { //se esiste una riga sul file, la rimuovo if (codrigaor > 0 && rplan.status() == NOERR) rplan.remove(); //creo la nuova riga rplan.zero(); rplan.put(LVRCONSPLAN_CODPLAN,codplan); rplan.put(LVRCONSPLAN_CODRIGA,codriga); rplan.put(LVRCONSPLAN_DTCONS,data); rplan.put(LVRCONSPLAN_CODPLANOR,codplanor); int err = rplan.write(); //scrivo la nuova riga if (err != NOERR) warning_box (FR("Errore di scrittura. Errore %d"), err); //modifico anche la TToken_string così da evitare errori al momento della rewrite rigamodificata.add(data,posdata); rigamodificata.add(codriga,posriga); dorewrite = true; } //se viene modificato un periodo di sospensione e/o una modalità di passaggio... //...e/o un codice itinerario su una riga, e/o la frequenza di consegne... //...e/o l'ordine di fermata, e/o l'ora di consegna, devo fare la rewrite di quella riga //ATTENZIONE: la modifica di un itinerario implica anche la modifica di autista e mezzo... //...mettendo quelli previsti per il nuovo itinerario if (changed(_rigaoriginale,rigamodificata,F_S_PERSOSPVAR) || changed(_rigaoriginale,rigamodificata,F_S_MODPASS) || changed(_rigaoriginale,rigamodificata,F_S_ITI) || changed(_rigaoriginale,rigamodificata,F_S_FREQ) || changed(_rigaoriginale,rigamodificata,F_S_ORDFER) || changed(_rigaoriginale,rigamodificata,F_S_ORA)) dorewrite = true; //se viene modificato un autista e/o un mezzo devo fare la rewrite di quella riga... //... e riaggiornare anche tutte le righe di quell'itinerario in quella data con i nuovi parametri if (changed(_rigaoriginale,rigamodificata,F_S_CODAUT) || changed(_rigaoriginale,rigamodificata,F_S_CODMEZ)) dorewrite = updatecar = true; //se devo fare una rewrite: if (dorewrite) { TMask& m = sheet.sheet_mask(); //per ogni campo della maschera che ha un field, ci scrivo dentro il nuovo valore FOR_EACH_MASK_FIELD(m,i,f) { const TFieldref* rf = f->field(); if (rf != NULL) { const char* val = rigamodificata.get(sheet.cid2index(f->dlg())); rf->write(val,rplan.curr()); } } rplan.curr().zero(LVRCONSPLAN_CONSSTD); lv_set_update_info(rplan.curr()); if (rplan.rewrite() != NOERR) warning_box (TR("Errore di scrittura.")); if (updatecar && _rigaoriginale.not_empty()) rewrite_all(rigamodificata, data, codplan); } } //REWRITE_ALL: metodo che riporta le modifiche fatte su autista e/o mezzo su una riga su tutte le righe //di un giro in una determinata data su richiesta dell'utente void TGestione_planning_mask::rewrite_all(TToken_string& rigamodificata, TDate& data, long codplan) { //instanzio una variabile di sheet TSheet_field& sheet = sfield(F_PLAN); //instanzio un isam file sulla tabella dei planning TLocalisamfile rplan(LF_LVRCONSPLAN); //se ho modificato autista e/o mezzo, allora: TString msg; msg << "Si desidera apportare le stesse modifiche\n" << " sull'autista e/o sul mezzo su tutte le righe\n" << " del giro in data " << data << "?"; if (yesno_box(msg)) { //recupero dai dati modificati i nuovi valori di autista e mezzo const int codaut = rigamodificata.get_int(sheet.cid2index(F_S_CODAUT)); const TString8 codmez= rigamodificata.get(sheet.cid2index(F_S_CODMEZ)); //recupero il codice itinerario su cui sto facendo la modifica const long coditi = rigamodificata.get_long(sheet.cid2index(F_S_ITI)); //instanzio un recordset sui planning, filtrando sui coditi TISAM_recordset recrplan("USE LVRCONSPLAN SELECT CODITI=#CODITI\nFROM CODPLAN=#CODPLAN\nTO CODPLAN=#CODPLAN"); recrplan.set_var("#CODPLAN",codplan); recrplan.set_var("#CODITI",coditi); TRectype& rec = recrplan.cursor()->curr(); for (bool ok = recrplan.move_first(); ok; ok = recrplan.move_next()) { //cambio autista e mezzo rec.put(LVRCONSPLAN_CODAUT,codaut); rec.put(LVRCONSPLAN_CODMEZ,codmez); rec.zero(LVRCONSPLAN_CONSSTD); lv_set_update_info(rec); //faccio l'effettiva rewrite rec.rewrite(rplan); } } fill_sheet(); return; } //CANCELLA_RIGA: mettodo che elimina una riga dal planning bool TGestione_planning_mask::cancella_riga() { int err=NOERR; //variabile che mi permette di lavorare sullo sheet TSheet_field& sheet = sfield(F_PLAN); //instanzio un isam file sulla tabella dei planning TLocalisamfile rplan(LF_LVRCONSPLAN); //creo il codplan const int posdata = sheet.cid2index(F_S_DATA); TDate data = _rigaoriginale.get(posdata); long codplan = data.date2ansi(); //creo il codriga const int posriga = sheet.cid2index(F_S_RIGA); int codriga = _rigaoriginale.get_int(posriga); //se esiste almeno una riga per quel planning, leggo il record corrispondente if (codriga > 0) { rplan.zero(); rplan.put(LVRCONSPLAN_CODPLAN,codplan); rplan.put(LVRCONSPLAN_CODRIGA,codriga); err = rplan.remove(); } return err == NOERR; } //NUOVA_RIGA: metodo che genera una nuova riga di planning e propone sullo sheet i campi che può riempire void TGestione_planning_mask::nuova_riga() { TSheet_field& sheet = sfield(F_PLAN); TToken_string& rigamodificata = sheet.row(sheet.selected()); rigamodificata.add(get_date(F_DADATA),sheet.cid2index(F_S_DATA)); if (!field(F_CODCF).empty()) { rigamodificata.add(get_long(F_CODCF),sheet.cid2index(F_S_CODCF)); rigamodificata.add(get(F_RAGSOCCLI),sheet.cid2index(F_S_RAGSOCCLI)); } if (!field(F_CODITI).empty()) rigamodificata.add(get_long(F_CODITI),sheet.cid2index(F_S_ITI)); sheet.check_row(sheet.selected()); //fa fare alla maschera la decodifica dei codici e aggiorna la TToken_string return; } //RICHIESTA_MODIFICA: metodo che controlla se la modifica del record richiesta comporta la sovrascrittura con un altro bool TGestione_planning_mask::richiesta_modifica() { //variabili che mi premettono di lavorare sullo sheet TSheet_field& sheet = sfield(F_PLAN); TToken_string& rigamodificata = sheet.row(sheet.selected()); //metto in una stringa la riga che sto analizzando if (!changed(_rigaoriginale,rigamodificata,F_S_DATA) && !changed(_rigaoriginale,rigamodificata,F_S_ITI) && !changed(_rigaoriginale,rigamodificata,F_S_CODCF) && !changed(_rigaoriginale,rigamodificata,F_S_CODCONT)) return true; //estraggo i dati di interesse dalla riga che ho modificato e da quella originale int pos = sheet.cid2index(F_S_DATA); const TDate data = rigamodificata.get(pos); const long codplan = data.date2ansi(); pos = sheet.cid2index(F_S_ITI); const TString4 itinerario = rigamodificata.get(pos); pos = sheet.cid2index(F_S_CODCF); const long cliente = rigamodificata.get_long(pos); pos = sheet.cid2index(F_S_CODCONT); const long contratto = rigamodificata.get_long(pos); //instanzio un isam file sulla tabella dei planning TLocalisamfile rplan(LF_LVRCONSPLAN); rplan.setkey(3); //leggo il record (se esiste) rplan.put(LVRCONSPLAN_CODCF,cliente); rplan.put(LVRCONSPLAN_CODCONT,contratto); rplan.put(LVRCONSPLAN_DTCONS,data); rplan.put(LVRCONSPLAN_CODITI,itinerario); if (rplan.read() == NOERR) return yesno_box(FR("La consegna così modificata corrisponde\nad una consegna già esistente.\nSi desidera duplicare la consegna?")); else return true; } //ON_FIELD_EVENT: metodo che gestisce i vari eventi che si verificano sui campi della maschera bool TGestione_planning_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case DLG_DELREC: if (e == fe_button && jolly == 0) { erase_sheet(); return false; } break; case DLG_QUIT: if (e == fe_button && sfield(F_PLAN).items() > 0) { _rigaoriginale = sfield(F_PLAN).row(sfield(F_PLAN).selected()); aggiorna_plan(); } break; //se questi campi vengono riempiti, allora riempi lo sheet opportunamante case F_DADATA: case F_ADATA: case F_CODITI: case F_CODCF: if (e == fe_modify || e == fe_init) { fill_sheet(); } break; case F_ORDGIRI: if (e == fe_init) { const int ordinaper = ini_get_int(CONFIG_DITTA, "lv", "Ordgir"); o.set(ordinaper); } break; //analisi delle operazioni sullo sheet case F_PLAN: switch (e) { case se_query_modify: //se viene selezionata una riga, la salvo in una variabile globale { TSheet_field& sheet = (TSheet_field&)o; _rigaoriginale = sheet.row(sheet.selected()); } break; case se_notify_modify: //se avviene una modifica, aggiorna il planning e lo sheet if (!richiesta_modifica()) { fill_sheet(); break; } else { aggiorna_plan(); fill_sheet(); break; } case se_notify_del: //se viene eliminata una riga dallo sheet, cancellala anche dal file cancella_riga(); break; case se_query_add: //se si decide di fare una modifica sullo sheet, ma i campi data della testata sono vuoti, rifiutati if (field(F_DADATA).empty()) return false; break; case se_notify_add: //se si aggiunge una riga nuova allo sheet, svuota la var globale e aggiungi la riga anche al file _rigaoriginale = ""; nuova_riga(); break; default: break; } break; //quando riempio il campo data dello sheet, decodifica il giorno di consegna case F_S_DATA: if (e == fe_modify || e == fe_init) { const TDate dtcons = o.get(); const TString16 ggcons = itow(dtcons.wday()); o.mask().set(F_S_GIORNO,ggcons); } break; //quando riempio il campo itinerario dell sheet, recupero i dati di autista e mezzo da &ITI case F_S_ITI: if (e == fe_modify && !o.empty()) { TMask& msk = o.mask(); const TRectype& iti = cache().get("&ITI",o.get()); if (!iti.empty()) { const bool riganuova = msk.get_int(F_S_RIGA) <= 0; if (riganuova || msk.field(F_S_CODAUT).empty()) msk.set(F_S_CODAUT,iti.get("S1")); if (riganuova || msk.field(F_S_CODMEZ).empty()) msk.set(F_S_CODMEZ,iti.get("S2")); } } break; //quando riempio il campo cliente sullo sheet controllo se posso proporre dei dati di default sul planning //questo pezzo è commentato perchè si è desciso di tralasciare questi automatismi... //...li tengo per un eventuale uso futuro /*case F_S_CODCF: if (e == fe_modify && !o.empty()) { TMask& msk = o.mask(); //maschera di sheet TLocalisamfile pplan(LF_LVPASPLAN); pplan.put(LVPASPLAN_CODCF,msk.get(F_S_CODCF)); pplan.put(LVPASPLAN_CODCONT,msk.get(F_S_CODCONT)); pplan.put(LVPASPLAN_GGCONS,msk.get_date(F_S_DATA).wday()); int err = pplan.read(_isgteq); //se trovo dei dati validi sulla tabella LF_LVPASPLAN, riempio tutti i campi che riesco,... //...utilizzando anche la tabella &ITI if (err == NOERR) { msk.set(F_S_ITI,pplan.get_int(LVPASPLAN_CODITI),true); msk.set(F_S_ORDFER,pplan.get_int(LVPASPLAN_ORDFERM)); msk.set(F_S_MODPASS,pplan.get_char(LVPASPLAN_MODPASS)); } } break;*/ default: break; } return true; } /////////////////////////////////////// //// TGESTIONE_PLANNING_APP //// /////////////////////////////////////// //classe TGestione_planning_app class TGestione_planning_app : public TSkeleton_application { TGestione_planning_mask* _msk; protected: virtual bool create(); virtual bool destroy(); public: bool transfer(); virtual void main_loop(); }; //CREATE: metodo costruttore bool TGestione_planning_app::create() { _msk = new TGestione_planning_mask("lv2200a"); return TSkeleton_application::create(); } //DESTROY: metodo distruttore bool TGestione_planning_app::destroy() { delete _msk; return TApplication::destroy(); } //TRANDSFER: metodo richiamato per permettere l'esecuzione del programma bool TGestione_planning_app::transfer() { return true; } void TGestione_planning_app::main_loop() { while (_msk->run() == K_ENTER) transfer(); } int lv2200(int argc, char* argv[]) { TGestione_planning_app app; app.run(argc, argv, TR("Gestione planning")); return 0; }