campo-sirio/lv/lv2100.cpp
luca83 f6c12db548 Patch level :10.0
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
2008-11-11 15:50:09 +00:00

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;
}