Files correlati : Ricompilazione Demo : [ ] Commento : 0000980: Cancellazione planning esistente se ri-generato Planning per singolo itinerario Generato Planning per tutti gli itinerari per l'intero mese di novembre 2008. Generato Planning pewr il singolo itinerario (in questo caso lo 005) viene sovrascritto tutto il planning del mese con i soli dati dell'itinerario 005. git-svn-id: svn://10.65.10.50/trunk@17589 c028cbd2-c16b-5b4b-a496-9718f37d4682
424 lines
16 KiB
C++
Executable File
424 lines
16 KiB
C++
Executable File
#include <applicat.h>
|
|
#include <automask.h>
|
|
#include <isam.h>
|
|
#include <progind.h>
|
|
#include <real.h>
|
|
#include <recarray.h>
|
|
#include <recset.h>
|
|
#include <relation.h>
|
|
|
|
#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
|
|
{
|
|
TString4 itinerario;
|
|
itinerario.format("%03d",coditi);
|
|
//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);
|
|
TString4 itinerario;
|
|
itinerario.format("%03d",coditi);
|
|
elimina_planning_itinerario(dadata,adata,coditi);
|
|
query << " SELECT CODITI=" << itinerario;
|
|
}
|
|
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;
|
|
}
|