campo-sirio/lv/lv2100.cpp
luca83 9ea16fb3f1 Patch level : 10.0
Files correlati     : patch 236
Ricompilazione Demo : [ ]
Commento            :
0001164: Conteggio numero massimo consegne nel mese deve essere fatto nell'ambito dell' itinerario
Descrizione 	Come si puù notare dagli allegati la generazione dei giri si interrompe perchè raggiunto il numero massimo di consegne nel mese. ma nel conteggio non si è tenuto conto del cambio di itinerario.

0001162: Messaggio di cancellazione in inserimento passaggi per contratto
Descrizione 	Non deve uscire il messaggio di cancellazione (in allegato)
nè se nei dati di configurazione è impostata la generazione automatica dei giri nè se non è impostata. Tutto qello che è stato inserito a mano può essere cancellato solo a mano.


git-svn-id: svn://10.65.10.50/trunk@18243 c028cbd2-c16b-5b4b-a496-9718f37d4682
2009-02-11 09:43:18 +00:00

501 lines
19 KiB
C++
Executable File

#include <applicat.h>
#include <automask.h>
#include <date.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) const;
bool elimina_planning_itinerario(const TDate& dadata, const TDate& adata, const long& coditi, const bool elimina) const;
bool elimina_planning_cliente(const TDate& dadata, const TDate& adata, const long& codcf, const bool elimina) 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, long coditi) const;
void arrotonda_al_giorno(TDate& data, const int ggcons) const;
void elabora_passaggio(const TDate& dadata, const TDate& adata, const TISAM_recordset& pplan);
const TDate str2date(const TString& data, int year) const;
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 bool elimina_manuali) const
{
//creo il recordset
TString query;
if (elimina_manuali)
query << "USE LVRCONSPLAN KEY 2\nFROM DTCONS=" << dadata << "\nTO DTCONS=" << adata;
else
query << "USE LVRCONSPLAN KEY 2 SELECT CONSSTD!=\"\"\nFROM DTCONS=" << dadata << "\nTO DTCONS=" << adata;
TISAM_recordset selrighe(query);
//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 bool elimina_manuali) 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 bool elimina_manuali) 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, long coditi) 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\nSELECT CODITI=#CODITI\nFROM CODCF=#CODCF CODCONT=#CODCONT DTCONS=#DADATA\nTO CODCF=#CODCF CODCONT=#CODCONT DTCONS=#ADATA");
rplan.set_var("#CODITI",coditi);
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;
}
}
//STR2DATE: converte una stringa in una data o non inizilizzata o sicuramente valida
const TDate TGenera_planning_app::str2date(const TString& data, int year) const
{
TDate date;
const int g = atoi(data.left(2));
const int m = atoi(data.mid(3,2));
const int a = data.len() < 10 ? year : atoi(data.right(4));
if (m >= 1 && m <= 12 && a > 2000 && g >= 1 && g <= date.last_day(m,a))
date = TDate(g,m,a);
return date;
}
//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:
if (pplan.get(LVPASPLAN_NRIGA).as_int() <= 0)
return;
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
//recupero la data di inizio e fine stagionalità
const TString16 strdastag = pplan.get(LVPASPLAN_DTSTAGIN).as_string();
const TString16 strastag = pplan.get(LVPASPLAN_DTSTAGSC).as_string();
//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
// 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
//controlla se è una festività
if(lv_is_holiday(d))
continue;
//...e/o in un periodo distagionalità (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 && strdastag.full() && strastag.full())
{
const TDate dastag = str2date(strdastag,d.year());
const TDate astag = str2date(strastag,d.year());
if (!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,coditi) >= maxcons)
continue;
//scrivi la chiave
const long codplan = d.date2ansi(); //setta il codplan
int codriga = prossimo_codriga(d);
rplan.zero();
rplan.put(LVRCONSPLAN_CODPLAN, codplan); //setta il codplan
rplan.put(LVRCONSPLAN_CODRIGA, codriga); //setta il codriga
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);
if (yesno_box(TR("Si desidera cancellare i giri modificati manualmente?")))
elimina_planning_cliente(dadata,adata,codcf,true);
else
elimina_planning_cliente(dadata,adata,codcf,false);
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);
//questo if è stato commentato in data 11/02/2009 per evitare questo messaggio, in seguito alla segnalazione
//1162 in mantis; penso sia meglio commentarlo perchè era frutto di una segnalazione precedente, quindi
//lo tengo in via precauzionale
//ATTENZIONE: prima esisteva la possibilità di riscrivere tutto completamente; adesso i giri che
//hanno avuto una modifica manuale rimangono sempre (possono essere cancelati solo a mano
//dalla gestione manuale dei giri
/*if (yesno_box(TR("Si desidera cancellare i giri modificati manualmente?")))
elimina_planning_itinerario(dadata,adata,coditi,true);
else*/
elimina_planning_itinerario(dadata,adata,coditi,false);
query << " SELECT CODITI=" << itinerario;
}
else
if (yesno_box(TR("Si desidera cancellare i giri modificati manualmente?")))
elimina_planning(dadata,adata,true);
else
elimina_planning(dadata,adata,false);
_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);
}
return true;
}
void TGenera_planning_app::main_loop()
{
//generazione autonatica dei giri lanciata ogni volta che salvo i passaggi planning per contratto
if (argc() > 2)
{
TFilename ininame(argv(2));
ininame.ltrim(2);
if (ininame.exist())
{
TConfig ini(ininame,"167");
const long codcf = ini.get_long("CODCF");
if (codcf > 0)
{
TLocalisamfile rplan(LF_LVRCONSPLAN);
rplan.last();
const TDate adata = rplan.curr().get_date(LVRCONSPLAN_DTCONS);
const TDate dadata = TDate(TODAY);
_msk->set(F_CODCF, codcf);
_msk->set(F_DADATA, dadata);
_msk->set(F_ADATA, adata);
transfer();
return;
}
}
}
while (_msk->run() == K_ENTER)
if (transfer())
message_box(TR("La generazione dei giri è stata completata\nin modo corretto"));
}
int lv2100(int argc, char* argv[])
{
TGenera_planning_app app;
app.run(argc, argv, TR("Generazione giri"));
return 0;
}