#include #include #include #include #include #include #include #include #include "lvlib.h" #include "lvcondv.h" #include "lvrconsplan.h" #include "lvpasplan.h" #include "lv2.h" #include "lv2100a.h" /////////////////////////////////////////////////////////// // TGenera_planning maschera /////////////////////////////////////////////////////////// class TGenera_planning_mask : public TAutomask { public: virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); TGenera_planning_mask (const char* name) : TAutomask(name) {} }; bool TGenera_planning_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case F_CODCF: if (e == fe_close) { if (!field(F_CODCF).empty() && !field(F_CODITI).empty()) return error_box(TR("Non è possibile specificare sia itinerario che cliente")); } break; case F_CODITI: if (e == fe_close) { if (!field(F_CODCF).empty() && !field(F_CODITI).empty()) return error_box(TR("Non è possibile specificare sia itinerario che cliente")); } break; default: break; } return true; } /////////////////////////////////////////////////////////// // TGenera_planning applicazione /////////////////////////////////////////////////////////// class TGenera_planning_app : public TSkeleton_application { TGenera_planning_mask* _msk; TAssoc_array _codriga; protected: virtual bool create(); virtual bool destroy(); bool elimina_planning(const TDate& dadata, const TDate& adata) const; bool elimina_planning_itinerario(const TDate& dadata, const TDate& adata, const long& coditi) const; bool elimina_planning_cliente(const TDate& dadata, const TDate& adata, const long& codcf) const; bool kill_planning (TISAM_recordset& selrighe) const; int prossimo_codriga(const TDate& data); TDate cerca_ultima_consegna(long codcf, long codcont, const TDate& data) const; int conta_consegne_mese(long codcf, long codcont, const TDate& adata) const; void arrotonda_al_giorno(TDate& data, const int ggcons) const; void elabora_passaggio(const TDate& dadata, const TDate& adata, const TISAM_recordset& pplan); public: bool transfer(); virtual void main_loop(); }; bool TGenera_planning_app::create() { _msk = new TGenera_planning_mask("lv2100a"); return TSkeleton_application::create(); } bool TGenera_planning_app::destroy() { delete _msk; return TApplication::destroy(); } //////////////////////////////////////////////////////////////// ////// METODI PER LA CANCELLAZIONE DEI PLANNING ////// //////////////////////////////////////////////////////////////// //ELIMINA_PLANNING: funzione che estrae dalla tabella dei planning tutte le righe comprese tra le due date indicate bool TGenera_planning_app::elimina_planning(const TDate& dadata, const TDate& adata) const { //creo il recordset TISAM_recordset selrighe("USE LVRCONSPLAN KEY 2\nFROM DTCONS=#DADATA\nTO DTCONS=#ADATA"); //setto le variabili selrighe.set_var("#DADATA",dadata); selrighe.set_var("#ADATA",adata); //richiamo la funzione che effettivamente fa la cancellazione delle righe interessate kill_planning(selrighe); return true; } //ELIMINA_PLANNING_ITINERARIO: funzione che estrae dalla tabella dei planning tutte le righe comprese tra... //...le due date indicate relative ad un itinerario specifico bool TGenera_planning_app::elimina_planning_itinerario(const TDate& dadata, const TDate& adata, const long& coditi) const { //creo il recordset TISAM_recordset selrighe("USE LVRCONSPLAN KEY 2\nSELECT (CODITI=#CODITI)\nFROM DTCONS=#DADATA\nTO DTCONS=#ADATA"); //setto le variabili selrighe.set_var("#CODITI",coditi); selrighe.set_var("#DADATA",dadata); selrighe.set_var("#ADATA",adata); //richiamo la funzione che effettivamente fa la cancellazione delle righe interessate kill_planning(selrighe); return true; } //ELIMINA_PLANNING_CLIENTE: funzione che estrae dalla tabella dei planning tutte le righe comprese tra //...le due date indicate relative ad un cliente specifico bool TGenera_planning_app::elimina_planning_cliente(const TDate& dadata, const TDate& adata, const long& codcf) const { //creo il recordset TISAM_recordset selrighe("USE LVRCONSPLAN KEY 2\nSELECT (CODCF=#CODCF)\nFROM DTCONS=#DADATA\nTO DTCONS=#ADATA"); //setto le variabili selrighe.set_var("#CODCF",codcf); selrighe.set_var("#DADATA",dadata); selrighe.set_var("#ADATA",adata); //richiamo la funzione che effettivamente fa la cancellazione delle righe interessate kill_planning(selrighe); return true; } //KILL_PLANNING: funzione che elimina un recordset generato precedentemente bool TGenera_planning_app::kill_planning (TISAM_recordset& selrighe) const { const TRecnotype righe = selrighe.items(); if (righe > 0) { TProgind pi(righe, TR("Eliminazione planning precedenti in corso..."), true, true); TLocalisamfile& rplan = selrighe.cursor()->file(); for (bool ok = selrighe.move_last(); ok; ok = selrighe.move_prev()) { if (!pi.addstatus(1)) break; rplan.remove(); } } return true; } //////////////////////////////////////////////////////////////// ////// METODI PER LA GENERAZIONE DEI PLANNING ////// //////////////////////////////////////////////////////////////// //PROSSIMO_CODRIGA: funzione che restituisce il prossimo codriga per il planning della data interessata int TGenera_planning_app::prossimo_codriga(const TDate& data) { TString8 strcodplan; strcodplan << data.date2ansi(); real *ptriga = (real*)_codriga.objptr(strcodplan); if (ptriga == NULL) { //per ora memorizzo zero ptriga = new real; _codriga.add(strcodplan,ptriga); //se esiste almeno una riga, memorizzo il codriga dell'ultima TISAM_recordset rplan("USE LVRCONSPLAN\nFROM CODPLAN=#CODPLAN\nTO CODPLAN=#CODPLAN"); rplan.set_var("#CODPLAN",TVariant(strcodplan)); if (rplan.move_last()) *ptriga = rplan.get(LVRCONSPLAN_CODRIGA).as_real(); } *ptriga += UNO; //incremento il codriga interessato return ptriga->integer(); } //CERCA_ULTIMA_CONSEGNA: questa funzione ricerca la data dell'ultima consegna fatta ad un certo cliente TDate TGenera_planning_app::cerca_ultima_consegna(long codcf, long codcont, const TDate& data) const { //instanzia un recordset di LVRCONSPLAN prendendo tutte le consegne fatte per un cliente su un certo... //...itinerario prima di una certa data TISAM_recordset rplan("USE LVRCONSPLAN KEY 3\nFROM CODCF=#CODCF CODCONT=#CODCONT\nTO CODCF=#CODCF CODCONT=#CODCONT DTCONS=#DATA"); rplan.set_var("#CODCF",codcf); rplan.set_var("#CODCONT",codcont); rplan.set_var("#DATA",data); if (rplan.move_last()) return rplan.get(LVRCONSPLAN_DTCONS).as_date(); // data dell'ultima consegna fatta return data; } //CONTA_CONSEGNE_MESE: questa funzione conta quante consegne sono state fatte ad un certo cliente //dal primo del mese fino ad una certa data int TGenera_planning_app::conta_consegne_mese(long codcf, long codcont, const TDate& adata) const { TDate dadata = adata; dadata.set_day(1); //instanzia un recordset di LVRCONSPLAN prendendo tutte le consegne fatte per un cliente su un certo... //...itinerario prima di una certa data TISAM_recordset rplan("USE LVRCONSPLAN KEY 3\nFROM CODCF=#CODCF CODCONT=#CODCONT DTCONS=#DADATA\nTO CODCF=#CODCF CODCONT=#CODCONT DTCONS=#ADATA"); rplan.set_var("#CODCF",codcf); rplan.set_var("#CODCONT",codcont); rplan.set_var("#DADATA",dadata); rplan.set_var("#ADATA",adata); return rplan.items(); } //ARROTONDA_AL_GIORNO: questa funzione rrotonda per eccesso la data al giorno della settimana desiderato //ggcons appartiene a [1..7] con 1 = lunedì -> 7 = domenica void TGenera_planning_app::arrotonda_al_giorno(TDate& data, const int ggcons) const { int delta = ggcons - data.wday(); if (delta != 0) { if (delta < 0) delta += 7; data += delta; } } //ELABORA_PASSAGGIO: questa funzione effettivamente genera il passaggio e lo salva nella tabella void TGenera_planning_app::elabora_passaggio(const TDate& dadata, const TDate& adata, const TISAM_recordset& pplan) { //dati recuperati dalla tabella dei passaggi per contratto: const long codcf = pplan.get(LVPASPLAN_CODCF).as_int(); //codice cliente const long coditi = pplan.get(LVPASPLAN_CODITI).as_int(); //codice itinerario const long codcont = pplan.get(LVPASPLAN_CODCONT).as_int(); //codice contratto const bool flstag = pplan.get(LVPASPLAN_FLSTAG).as_bool(); //flag stagionalità const int ggcons = pplan.get(LVPASPLAN_GGCONS).as_int(); //giorno di consegna (1 = lunedì -> 7 = domenica) const int ordfer = pplan.get(LVPASPLAN_ORDFERM).as_int(); //ordine di fermata const TString4 freq = pplan.get(LVPASPLAN_FREQ).as_string(); //codice della tabella di frequenze di consegna char modpass = pplan.get(LVPASPLAN_MODPASS).as_string()[0]; //modalità di passaggio //cache sulle testate dei contratti, selezionati per CODCF e CODCONT TToken_string keycont; keycont.add(codcf); keycont.add(codcont); const TRectype& contratto = cache().get(LF_LVCONDV,keycont); //dati recuperati dalle testate dei contratti: const TDate dadatacont = contratto.get_date(LVCONDV_DATAIN); //data di inizio validità del contratto const TDate adatacont = contratto.get_date(LVCONDV_DATASC); //data di fine validità del contratto const TDate dastag = contratto.get_date(LVCONDV_DTSTAGIN); //data di inizio del periodo di stagionalità const TDate astag = contratto.get_date(LVCONDV_DTSTAGSC); //data di fine del periodo di stagionalità // Controllo se il contratto e' valido nel periodo interessato if (dadatacont > adata || (adatacont.ok() && adatacont < dadata)) return; // Inutile proseguire //cache sulla tabella itinerari TString4 keycoditi; keycoditi.format("%03d", coditi); const TRectype& iti = cache().get("&ITI",keycoditi); //dati recuperati dalla tabella itinerari: const TString8 codaut = iti.get("S1"); //codice autista const TString8 codmez = iti.get("S2"); //codice mezzo //recordset sulla tabella dei periodi di sospensione, limitati per CODCF e CODCONT TISAM_recordset persosp("USE LVPERISOSP\nFROM CODCF=#CODCF CODCONT=#CODCONT\nTO CODCF=#CODCF CODCONT=#CODCONT"); persosp.set_var("#CODCF",codcf); persosp.set_var("#CODCONT",codcont); //cache sulla tabella frequenze consegne const TRectype& frq = cache().get("&FRQ",freq); //dati recuperati dalla tabella frequenze consegne: const int maxcons = frq.get_int("I2"); //numero massimo di consegne per mese TDate primogiorno = dadata; //primogiorno: primo giorno del periodo selezionato if (frq.get_bool("B0")) //se devo usare "USA ULTIMA CONSEGNA", primogiorno diventa la data dell'ultima consegna primogiorno = cerca_ultima_consegna(codcf,codcont,dadata); const int ritardo = frq.get_int("I0"); //ritardo di consegna rispetto alla data di inizio calcolo if (ritardo > 0) //evita anticipi senza senso primogiorno += ritardo; long frequenza = frq.get_long("I1"); //frequenza di consegna if (frequenza <= 0 || frequenza > 28) //forza una frequenza valida frequenza = 7; TLocalisamfile file_rplan(LF_LVRCONSPLAN); TRectype& rplan = file_rplan.curr(); for (TDate d = primogiorno; d <= adata; d += frequenza) { arrotonda_al_giorno(d, ggcons); if (d < dadata) // Puo' succedere se si utilizza la data di ultima consegna continue; if (d > adata) // Inutile generare date fuori range break; //controlla che d sia o meno in un periodo di validità contratto... if (d < dadatacont) continue; // Questa data non e' ancora in contratto: provo la prossima if (adatacont.ok() && d > adatacont) break; // Questa data e' oltre il contratto: inutile proseguire //...e/o in un periodo di stagionalità (se necessario)... ////la riga va elaborata se il flag di stagionalità è TRUE ////e se se la data del periodo di stagionalità è coerente ////e la data in considerazione è compresa tra le date della stagionalità if (flstag && (!astag.ok() || d < dastag || d > astag)) continue; //...e/o in un periodo di sospensione... //...per cui c'è da verificare se è da saltare completamente o... //...se c'è da elaborare ugualmente, ma con una modalità di passaggio differente TString4 codpersosp; for (bool ok = persosp.move_first(); ok; ok = persosp.move_next()) { const TDate inisosp = persosp.get("DATAINI").as_date(); const TDate finsosp = persosp.get("DATAFIN").as_date(); if (d >= inisosp && d <= finsosp) { codpersosp = persosp.get("CODPER").as_string(); const TString& tpsosp = persosp.get("TPSOSP").as_string(); const TRectype& sosp = cache().get("&TSP",tpsosp); modpass = sosp.get_char("S1"); break; } } //se la modalità di passaggio risulta vuota la riga non va elaborata if (modpass <= ' ') continue; //se ho già raggiunto il numero massimo di consegne per mese... //...la riga, anche se supera tutti i controlli, non va elaborata if (maxcons > 0 && conta_consegne_mese(codcf,codcont,d) >= maxcons) continue; //scrivi la chiave const long codplan = d.date2ansi(); //setta il codplan rplan.zero(); rplan.put(LVRCONSPLAN_CODPLAN, codplan); int tmp = prossimo_codriga(d); rplan.put(LVRCONSPLAN_CODRIGA, tmp); rplan.put(LVRCONSPLAN_DTCONS,d); //setta la data di consegna rplan.put(LVRCONSPLAN_CODITI,coditi); //setta il codice itinerario rplan.put(LVRCONSPLAN_ORDFER,ordfer); //setta l'ordine di fermata rplan.put(LVRCONSPLAN_CODCF,codcf); //setta il codice cliente rplan.put(LVRCONSPLAN_CODCONT,codcont); //setta il codice contratto rplan.put(LVRCONSPLAN_GGCONS,ggcons); //setta il giorno di consegna rplan.put(LVRCONSPLAN_MODPASS,modpass); //setta la modalità di passaggio rplan.put(LVRCONSPLAN_CODAUT,codaut); //setta il codice autista rplan.put(LVRCONSPLAN_CODMEZ,codmez); //setta il codice mezzo rplan.put(LVRCONSPLAN_FREQ,freq); //setta la frequenza di consegna rplan.put(LVRCONSPLAN_CONSSTD,true); //setta il flag di "consegna standard" rplan.put(LVRCONSPLAN_PERSOSP,codpersosp); //setta il periodo di sospensione lv_set_update_info(rplan); //setta utente, data e ora dell'ultimo aggiornamento file_rplan.write(); } } //TRANSFER: questo metodo elimina i planning già esistenti in modo da evitare conflitti //e chiama la funzione ELABORA_PASSAGGIO bool TGenera_planning_app::transfer() { const TDate dadata = _msk->get(F_DADATA); const TDate adata = _msk->get(F_ADATA); TString query = "USE LVPASPLAN"; if (_msk->get(F_CODCF).full()) { const long codcf = _msk->get_long(F_CODCF); elimina_planning_cliente(dadata,adata,_msk->get_long(F_CODCF)); query << " SELECT CODCF=" << codcf; } else if (_msk->get(F_CODITI).full()) { const long coditi = _msk->get_long(F_CODITI); elimina_planning_itinerario(dadata,adata,coditi); query << " SELECT CODITI=" << coditi; } else elimina_planning(dadata,adata); _codriga.destroy(); //azzera il numero delle righe del planning già generati TISAM_recordset pplan(query); TProgind pi(pplan.items(), TR("Generazione giri in corso..."), true, true); for (bool ok = pplan.move_first(); ok; ok = pplan.move_next()) { if (!pi.addstatus(1)) break; elabora_passaggio(dadata, adata, pplan); } message_box(FR("La generazione dei giri è stata completata\nin modo corretto")); return true; } void TGenera_planning_app::main_loop() { while (_msk->run() == K_ENTER) transfer(); } int lv2100(int argc, char* argv[]) { TGenera_planning_app app; app.run(argc, argv, TR("Generazione giri")); return 0; }