// ve6300.cpp: programma di generazione effetti
// Muliebrica parodia...
//      Il cavo delirante che irrompe nel silenzio dell'apogeo
//              *  * *  *   *    *     *
//           *
//        _                _______ _____________      ___================
//      _I_I___oo_____ii__| |_|| | |            \     |  |O|  |_|  |_|
//     |   | -  -   |     I    | | |    L M S    |    |  | |
//    _|___|________|____ I____|_|=|_____________|=II=|__|_|_____________
//  I=| o (_)--(_)--(_)--(_) O--O   O-O +++++ O-O \--/  ()==()   ++++
//=======================================================================
// 
//                               
//     ____||____============____||___    ___===============================
//    /__|     OOOOOOOOOOOOO    [_]  |    |  |[]|  [_]    [_]    [_]    [_]
//  /             S N C F            |    |  |  |
//  \________________________________|_ii_|__|__|___________________________
//    ()==()    === ++++ ===   ()==()       ()==()     +++   +++   ++++++++
//=========================================================================
//
// Have you ever heard MK ?

#include <applicat.h>
#include <relation.h>
#include <tabutil.h>
#include <mask.h>
#include <sheet.h>
#include <urldefid.h>
#include <progind.h>
#include <modaut.h>

#include <doc.h>
#include <rdoc.h>
#include <effetti.h>
#include <reffetti.h>

#include "../cg/saldacon.h"
#include "../ef/ef0101.h"
#include "velib01.h"
#include "ve6300a.h"

// Enum di possibili errori

enum error_type {
  no_error,
  elaboration_error,
  nr_doc_error,
  chg_stat_error,
  datadoc_error,
  codpag_error,
  scadenze_error,
  write_error,
  generic_error
};

// TGenerazioneEffetti_app
// Applicazione di generazione effetti
class TGenerazioneEffetti_app : public TApplication
{
  TString16     _cod_el;       // codice elaborazione immesso
  TDate         _data_reg;     // data di registrazione immessa
  TDate         _data_ini;     // data di inizio intervallo
  TDate         _data_fine;    // data di fine intervallo
  bool          _raggruppa;    // se TRUE raggruppa gli effetti secondo i criteri
  TMask         *_msk;         // maschera di selezione dati
  TRelation     *_clifo;       // relazione dei clienti e fornitori + cfven
  TLocalisamfile *_docfile,    // file dei documenti (per far funzionare TDocumento)
                 *_rdocfile,   // file delle righe documento (per far funzionare TDocumento)
                 *_efffile,    // file effetti
                 *_refffile,   // file righe di effetti
                 *_cessfile,   // file righe di cessionari
                 *_tab;        // tabella di ditta
  TTable         *_cpg;        // tabella condizioni di pagamento
  TDocumento     *_doc;        // documento corrente
  TPagamento     *_pagamento;  // pagamento corrente, ricalcolato prima di scrivere le scadenze
  TBit_array     _valid_array; // array dei tipi di pagamento validi per la generazione di effetti
  TArray_sheet   *_num_sheet;  // Array sheet selezionabile dei codici numerazione
  TString_array  _tipi_doc;    // Array di stringhe contenente i tipi documenti da elaborare
  TArray         _effetti_array;//Array di effetti (TEffetto) da scrivere
  TAssoc_array   _group_array; // Assoc_array dei documenti da raggruppare. La chiave di ogni elemento
                               // e composta dagli stessi criteri di raggruppamento (vedi sotto)
                               // Ogni elemento e una TToken_string. Ogni elemento della TToken_string contiene
                               // la chiave per identificare il documento che fa parte di quel raggruppamento
  error_type     _error;       // Errore rilevato durante l'elaborazione
  long           _total_bills; // Totale effetti generati
  bool           _can_write;   // se TRUE e' abilitata la scrittura. Non appena rileva un errore rimane a FALSE for this instance
  char           _final_doc_status; // Valore per lo stato finale del documento
                                    // Flags di raggruppamento opzionali (%ELD->S1):
  bool           _group_by_date,    // raggruppa per data di scadenza
                 _group_by_sosp,    // raggruppa per flag sosp. d'imposta
                 _group_by_change,  // raggruppa per cambio
                 _group_by_pag,     // raggruppa per cond. di pagamento
                 _group_by_bank,    // raggruppa per banca (ABI-CAB)
                 _group_by_type,    // raggruppa per tipo di documento
                 _group_by_num,     // raggruppa per cod. numerazione
                 _group_by_agent;   // raggruppa per agente
protected: // TApplication
  // Cambia lo stato del documento
  error_type   change_doc_status();
  // Coontrolla se lo stato ed il tipo del documento sono validi e rispettano la selezione
  bool doc_tipo_stato_ok();
  // Visualizza l'ultimo errore rilevato
  void display_error();
  // Genera l'effetto
  void generate_bill();
  // Genera gli effetti
  void generate();
  // Le 4 seguenti non hanno bisogno di commenti
  virtual bool menu(MENU_TAG mt);
  virtual bool create();
  virtual bool destroy();
  // Handler del codice elaborazione differita
  static bool handle_cod_eld(TMask_field& f, KEY k);
  // Handler dell'intervallo di date
  static bool handle_data_range(TMask_field& f, KEY k);
  // Handler del pulsante di selezione codici numerazione
  static bool handle_select(TMask_field& f, KEY k);
  // Ritorna il TArray_sheet contenente le selezioni sui codici numerazione
  TArray_sheet* get_num_sheet() const { return _num_sheet; }
  // Costruisce lo sheet dei codici numerazione
  void   build_num_sheet();
  // Restituisce TRUE se il tipo di pagamento passato e' valido per generare l'effetto
  bool   valid_type(int tipo_pag) const ;
  // Aggiunge il documento corrente alla lista dei documenti raggruppabili
  void   add_to_group_list();
  // Costruisce la stringa chiave per raggruppamento
  void   build_group_key(TString& key);
  // Costruisce la stringa chiave per documento
  void   build_doc_key(TToken_string& key);
  // Setta i flags di raggruppamento opzionali in base alla tabella 
  void   set_options();
  // Effettua il raggruppamento vero e proprio degli effetti (solo se ci sono elementi nell'assoc_array
  void   group_bills();
  // Istanzia il pagamento corrente
  void   calc_pagamento(TString16& codpag, const real& totale_fatt, TString16& data, const bool valuta, const real& change);
  // Scrive i record array degli effetti raggruppati
  error_type write_groups();
  // Cambia lo stato dei gruppi di documenti raggruppati in effetti
  error_type change_group_status();
public:
  // Verifica se non ci sono stati errori
  bool good() const { return _error == no_error;}
  TGenerazioneEffetti_app() {_msk = NULL; _num_sheet = NULL;} 
  virtual ~TGenerazioneEffetti_app() { }
};

inline TGenerazioneEffetti_app& app() { return (TGenerazioneEffetti_app&) main_app(); }

bool TGenerazioneEffetti_app::handle_data_range(TMask_field& f, KEY k)
{
  if (k==K_ENTER && f.dirty())
  {
    TMask& m = f.mask();
    TDate da(m.get_date(F_DATA_INI));
    TDate a(m.get_date(F_DATA_FIN));
    //m.field(F_DATA_REG).set_dirty();
    //if (a == botime || da == botime) return TRUE;
    if (a < da) 
    {
      f.error_box("La data di inizio deve essere minore della data di fine.");
      return FALSE;
    }
    //if ((a - da) > 15)
    //{
    //  f.error_box("L'intervallo tra le date non puo' eccedere i 15 giorni.");
    //  return FALSE;
    //}
  }
  return TRUE;
}

bool TGenerazioneEffetti_app::handle_cod_eld(TMask_field& f, KEY k)
{
  if (f.to_check(k) && k == K_TAB) // se e' cambiato ricostruisce anche lo sheet dei codici numerazione
  {
    app()._cod_el = f.get(); // aggiorna il codice elaborazione per la build_num_sheet()
    f.mask().disable(DLG_OK);
    app().build_num_sheet();
  }
  return TRUE;
}

bool TGenerazioneEffetti_app::handle_select(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {
    TMask& m = f.mask();
    TArray_sheet* s = app().get_num_sheet();
    if (s->run())
    {
      if (s->checked() != 0) // Hai selezionato qualcosa ?
        m.enable(DLG_OK);    // allora abilita il pulsante di conferma 
      else
        m.disable(DLG_OK);
    }
  }
  return TRUE;
}

void TGenerazioneEffetti_app::build_num_sheet()
{
  _num_sheet->destroy();
  
  TTable eld("%ELD");
  TTable num("%NUM");
  TString s1,s2,s3;
  TString16 tipon1,tipon2,tipon3,tipon4,tipon5; // tipi documento validi per la numerazione
  long pos;
  
  eld.put("CODTAB",_cod_el);
  if (eld.read() == NOERR)
  {
    TToken_string t;
    s1.format("%-20s",(const char*)eld.get("S2"));
    s3 = eld.get("S7");
    _final_doc_status = eld.get("S4")[0];
    for (int i=0;i<5;i++)
    {
      t = s1.mid(i*4,4);     // Tipo documento
      if (t.trim().empty()) break;
      t.add(s3.mid(i,1));  // Stato iniziale
      _tipi_doc.add(t);    // Aggiunge questo tipo documento alla lista
    }
    for (pos=0,num.first();num.good();num.next(),pos++) // scorre tutte le numerazioni possibili
    {
      TToken_string t;
      t.add(" ");
      t.add(num.get("CODTAB"));
      t.add(num.get("S0"));
      _num_sheet->add(t);
      s2 = num.get("S2"); // reperisce i tipi documento validi per questa numerazione
      tipon1 = s2.mid(0,4);
      tipon2 = s2.mid(4,4);
      tipon3 = s2.mid(8,4);
      tipon4 = s2.mid(12,4);
      tipon5 = s2.mid(16,4);
      const int n1 = s1.find(tipon1);
      const int n2 = s1.find(tipon2);
      const int n3 = s1.find(tipon3);
      const int n4 = s1.find(tipon4);
      const int n5 = s1.find(tipon5);
      if ((tipon1.empty() || n1<0) && (tipon2.empty() || n2<0) && 
           (tipon3.empty() || n3<0) && (tipon4.empty() || n4<0) &&
           (tipon5.empty() ||  n5<0))
        _num_sheet->disable_row(pos);
      else
        _num_sheet->enable_row(pos);
    }
  }
}

bool TGenerazioneEffetti_app::valid_type(int pag) const
{
  return _valid_array[pag];
}

bool TGenerazioneEffetti_app::create()
{
  TApplication::create();
  if (!has_module(EFAUT))
  {
    error_box("Impossibile eseguire il programma se il modulo Effetti non e' abilitato");
    return FALSE;
  }
  _error = no_error;
  _can_write = TRUE;
  _pagamento = NULL;
  _msk = new TMask("ve6300a");
  _msk->set_handler(F_CODICE_ELAB,handle_cod_eld);
  _msk->set_handler(F_DATA_INI,handle_data_range);
  _msk->set_handler(F_DATA_FIN,handle_data_range);
  _msk->set_handler(DLG_USER,handle_select);
  _num_sheet = new TArray_sheet(-1,-1,-4,-4,"Codici numerazione",
               "@1|Cod. numerazione|Descrizione@50");
  _docfile = new TLocalisamfile(LF_DOC);
  _rdocfile = new TLocalisamfile(LF_RIGHEDOC);
  _efffile = new TLocalisamfile(LF_EFFETTI);
  _refffile = new TLocalisamfile(LF_REFFETTI);
  _cessfile = new TLocalisamfile(LF_CESS);
  _doc = new TDocumento;
  _cpg = new TTable("%CPG");
  _tab = new TLocalisamfile(LF_TAB);
  _clifo = new TRelation(LF_CLIFO);
  _clifo->add(LF_CFVEN,"TIPOCF=TIPOCF|CODCF=CODCF");
  // Settaggio dell'array dei tipi documento che possono generare effetti
  _valid_array.set(2);  // tratta
  _valid_array.set(3);  // riba
  _valid_array.set(5);  // paghero'
  _valid_array.set(7);  // tratta accettata
  _valid_array.set(8);  // rapporti interbancari diretti (rid)
  _valid_array.reset(9); // bonifici 
  dispatch_e_menu(BAR_ITEM(1));
  return TRUE;
}

bool TGenerazioneEffetti_app::destroy()
{
  if (_msk) delete _msk;
  if (_num_sheet) delete _num_sheet;
  if (_docfile) delete _docfile;
  if (_rdocfile) delete _rdocfile;
  if (_efffile) delete _efffile;
  if (_refffile) delete _refffile;
  if (_cessfile) delete _cessfile;
  if (_doc) delete _doc;
  if (_cpg) delete _cpg;
  if (_tab) delete _tab;
  if (_clifo) delete _clifo;
  return TApplication::destroy();
}

bool TGenerazioneEffetti_app::menu(MENU_TAG mt)
{
  while (_msk->run() == K_ENTER)
  {
    _cod_el = _msk->get(F_CODICE_ELAB);
    _data_ini = _msk->get_date(F_DATA_INI);
    _data_fine = _msk->get_date(F_DATA_FIN);
    _raggruppa = _msk->get_bool(F_RAGGRUPPA);
    generate();
  }
  return FALSE;
}

bool TGenerazioneEffetti_app::doc_tipo_stato_ok()
// Verifica che il tipo documento corrente esista tra i tipi previsti dalla elaborazione
// differita selezionata
{
  const int items = _tipi_doc.items();
  bool found = FALSE;
  const TString16 tipo(_doc->tipo().codice());
  const char stato = _doc->stato();
  for (int i=0;i<items && !found;i++)
  {
    TToken_string& t = _tipi_doc.row(i);
    const TString16 tipox(t.get(0));
    const char statox = t.get(1)[0];
    if (tipo == tipox && stato == statox)
      found = TRUE;
  }
  return found;
}

error_type TGenerazioneEffetti_app::change_doc_status()
// Cambia lo stato del documento
{
  _doc->stato(_final_doc_status);
  if (_doc->head().rewrite(*_docfile) != NOERR)
    _error = chg_stat_error; 
  return _error;
}

error_type TGenerazioneEffetti_app::change_group_status()
// Cambia lo stato dei documenti raggruppati
{
  const int items = _group_array.items();
  if (items > 0) 
  {
    _error = no_error;
    TToken_string * group_element;
    int i=0;
    // Ciclo sugli elementi dell'assoc_array
    for (group_element = (TToken_string *) _group_array.first_item(); group_element != NULL && i < items; i++,group_element = (TToken_string*)_group_array.succ_item())
    {
      group_element->restart();
      const int doc_items = group_element->items();
      for (int j=0;j<doc_items;j++)
      {
        TToken_string t(group_element->get(j),'$');
        char provv = t.get_char(0);
        int anno = t.get_int(1);
        TString codnum(t.get(2));
        long numdoc = t.get_long(3);
        _docfile->put(DOC_PROVV,provv);
        _docfile->put(DOC_ANNO,anno);
        _docfile->put(DOC_CODNUM,codnum);
        _docfile->put(DOC_NDOC,numdoc); 
        if (_docfile->read() == NOERR) // Legge il documento 
        {
          _docfile->put(DOC_STATO,_final_doc_status);
          _docfile->rewrite();
        }
      }
    }
  }
  return _error;
}

void TGenerazioneEffetti_app::display_error()
{
  TString msg;
  TString numerazione = _doc->numerazione();
  const long numero = _doc->numero();

  switch (_error)
  {
    case elaboration_error:
      msg.format("Il documento %s/%ld non rientra tra i tipi documento validi per l'elaborazione."
                "Verificare i tipi documento ed il loro stato iniziale sul codice elaborazione inserito.",(const char*)numerazione,numero);
      break;
    case nr_doc_error:
      msg.format("Rilevato un numero di documento errato contabilizzando il documento %s/%ld."
                "Verificare il numero documento e il codice numerazione inseriti in tabella.",(const char*)numerazione,numero);
      break;
    case chg_stat_error:
      msg.format("Rilevato un errore cambiando lo stato al documento %s/%ld."
                "Verificare l'integrita' del file documenti.",(const char*)numerazione,numero);
      break;
    case datadoc_error:
      msg.format("Rilevato una data documento vuota relativamente al documento %s/%ld."
                "Verificare l'informazione inserita.",(const char*)numerazione,numero);
      break;
    case codpag_error:
      msg.format("Rilevato un codice pagamento non esistente relativamente al documento %s/%ld."
                "Verificare l'esistenza del codice pagamento inserito.",(const char*)numerazione,numero);
      break;
    case scadenze_error:
      msg.format("Calcolate 0 scadenze relativamente al documento %s/%ld."
                "Verificare la correttezza del codice pagamento inserito.",(const char*)numerazione,numero);
      break;
    case write_error:
      if (_efffile->status() != NOERR)
        msg.format("Si e' verificato l' errore %d in scrittura sul file effetti relativamente al documento %s/%ld"
                "Verificare la consistenza del file.",_efffile->status(),(const char*)numerazione,numero);
      else
        msg.format("Si e' verificato l' errore %d in scrittura sul file righe effetti relativamente al documento %s/%ld"
                "Verificare la consistenza del file.",_refffile->status(),(const char*)numerazione,numero);
      break;
    default:
      msg.format("E' stato rilevato un errore generico contabilizzando il documento %s/%ld.",
                  (const char*)numerazione,numero);
      break;
  }
  warning_box(msg);
  _error = no_error; // reset error, as any other one would do, so you can show me the other ones.
  _can_write = FALSE; // But from now on u cannot write anymore. U must exit this program and repair errors occurred.
}

// Criteri di raggruppamento effetti.
// Un documento puo' essere raggruppato con i seguenti criteri:
// - flag di raggruppamento effetti a TRUE (obbligatorio)
// - cliente                               (obbligatorio)
// - valuta                                (obbligatorio)
// - data scadenza                         (opzionale)
// - flag di sosp. d'imposta               (opzionale)
// - cambio valuta                         (opzionale)
// - cond di pagamento                     (opzionale)
// - banca                                 (opzionale)
// - tipo doc.                             (opzionale)
// - cod. numerazione                      (opzionale)
// - agente                                (opzionale)
// I parametri opzionali sono decisi nella tabella elaborazioni
// Spiegazione del raggruppamento:
// Il raggruppamento di documenti diversi in uno stesso effetto consiste nella creazione
// di piu' righe dello stesso effetto.
// Ad esempio supponiamo di avere il cliente 10 con 4 fatture ognuna con condizioni di pagamento diverse
// la fattura 1 e' suddivisa in 2 rate (10.000 + 30.000)
// la fattura 2 e' suddivisa in 4 rate (15.000 + 20.000 + 25.000 + 30.000)
// la fattura 3 e' suddivisa in 5 rate (20.000 + 40.000 + 80.000 + 160.000 + 320.000)
// la fattura 4 e' suddivisa in 3 rate (50.000 + 100.000 + 150.000)
// Il numero di effetti generati equivale al massimo numero di rate (5 in questo caso)
// quindi avremo 5 effetti:
// il primo avra' 4 righe (prima rata di tutte le fatture)
// il secondo avra' 4 righe (seconda rata di tutte le fatture)
// il terzo avra' 3 righe (terza rata delle fatture 2,3,4)
// il quarto avra' 2 righe (quarta rata delle fatture 2 e 3)
// il quinto avra' 1 riga (quinta rata della fattura 3)
// La testata di ogni effetto conterra' il totale (somma) dei singoli importi delle righe
// ovvero:
// tot. primo effetto:        95.000
// tot. secondo effetto:     190.000
// tot. terzo effetto:       255.000
// tot. quarto effetto:      190.000
// tot. quinto effetto:      320.000
// I dati della testata (soprattutto la data di scadenza) di ogni effetto vengono presi dal 
// primo documento valido per la generazione dell'effetto corrente:
// per i primi due effetti vale la fattura 1
// per gli effetti 3 e 4 vale la fattura 2
// per l'effetto 5 vale la fattura 3.
// Questo e' quanto ho appreso (Hope I'm right...) ;-)

void TGenerazioneEffetti_app::add_to_group_list()
{  
  TString key;
  TToken_string t("",'$');
  build_group_key(key);
  build_doc_key(t);
  
  const bool is_key = _group_array.is_key(key);
  TToken_string tt(is_key ? (TToken_string&) _group_array[key]: "");
  tt.add(t);
  _group_array.add(key,tt, is_key ? TRUE : FALSE);
}

void TGenerazioneEffetti_app::build_group_key(TString& key)
{
  long cli = _doc->get_long(DOC_CODCF);
  TString16 val(_doc->get(DOC_CODVAL));
  
  // campi obbligatori
  key.format("%7ld%3s",cli,(const char*)val);
  // campi opzionali
  if (_group_by_date)
    key << _doc->get(DOC_DATAINSC);
//  Qual'e' il flag di sospensione d'imposta del documento ??    
//  if (_group_by_sosp)
//    key << _doc->get(DOC_????);
  if (_group_by_change)
    key << _doc->get(DOC_CAMBIO);
  if (_group_by_pag)
    key << _doc->get(DOC_CODPAG);
  if (_group_by_bank)
  {
    key << _doc->get(DOC_CODABIA); // banca cliente
    key << _doc->get(DOC_CODCABA);
  }
  if (_group_by_type)
    key << _doc->get(DOC_TIPODOC);
  if (_group_by_num)
    key << _doc->get(DOC_CODNUM);
  if (_group_by_agent)
    key << _doc->get(DOC_CODAG);    
}

void TGenerazioneEffetti_app::build_doc_key(TToken_string& key)
{
  key.add(_doc->get(DOC_PROVV));
  key.add(_doc->get(DOC_ANNO));
  key.add(_doc->get(DOC_CODNUM));
  key.add(_doc->get(DOC_NDOC));
}

void TGenerazioneEffetti_app::set_options()
{
  TTable eld("%ELD");
  eld.put("CODTAB",_cod_el);
  if (eld.read() == NOERR)
  {
    TString s1;
    s1.format("%-20s",(const char*)eld.get("S1")); // Forzo la sua lunghezza a 20 per evitare subscript errors
    _group_by_date   = s1[12] == 'X';
    _group_by_sosp   = s1[13] == 'X';
    _group_by_change = s1[0] == 'X';
    _group_by_pag    = s1[4] == 'X';
    _group_by_bank   = s1[5] == 'X';
    _group_by_type   = s1[2] == 'X';
    _group_by_num    = s1[3] == 'X';
    _group_by_agent  = s1[7] == 'X';
  }
}

void TGenerazioneEffetti_app::calc_pagamento(TString16& codpag,const real& totale_fatt, TString16& data, const bool valuta, const real& change)
{
  _pagamento = new TPagamento(codpag, data);
  real totspese = _doc->spese();
  real totimposte = _doc->imposta();
  real totimponibili = totale_fatt - totimposte - totspese;
  if (valuta)
  {
    real val1 = totimponibili * change;
    real val2 = totimposte * change;
    real val3 = totspese * change;
    _pagamento->set_total_valuta( totimponibili, totimposte, totspese, change, val1, val2 ,val3);
  }
  else
    _pagamento->set_total( totimponibili, totimposte, totspese );
  _pagamento->set_rate_auto( );      
}

error_type TGenerazioneEffetti_app::write_groups()
{
  const int items = _effetti_array.items();
  int err = NOERR;
  _error = no_error;
  for (int i = 0; i<items && err == NOERR; i++) // scorre gli effetti generati e li scrive su file
  {
    TEffetto& effetto = (TEffetto&)_effetti_array[i];
    err = effetto.write(*_efffile); // Pensa a tutto lui per rinumerare...
  }
  if (err != NOERR)
  { 
    i--;
    for (int j = 0; j<i; j++) // rimuove gli effetti raggruppati, per non lasciare documenti raggruppati parzialmente
    {
      TEffetto& effetto = (TEffetto&)_effetti_array[j];
      // posso fare tranquillamente la remove, visto che la numerazione dell'effetto e' gia' stata effettuata
      effetto.remove(*_efffile);
    }
    error_box("Errore %d nella scrittura gruppi di effetti. Nessun raggruppamento effettuato.",err);
  }
  return _error;
}

void TGenerazioneEffetti_app::generate_bill() // bill in inglese significa anche effetto eheheh ;-)
{
// That's my JOB! 
// Ogni fattura rata della fattura (si parla di effetti attivi per ora) genera 1 effetto.
// con almeno una riga.
// Ad es. Se ho una fattura suddivisa in 3 rate essa generera' 3 effetti  con una riga.
// Nel caso si decida di raggruppare gli effetti (vedi criteri di raggruppamento)
// avro' un effetto con tante righe quante scadenze di fattura sono raggruppabili
// Le fatture possono generare effetti solo per i seguenti tipi di pagamento:
// 2 - Tratta
// 3 - Ri.Ba.
// 5 - Paghero'
// 7 - Tratta accettata
// 8 - Rapporti interbancari diretti
// Caso mai tale insieme risultasse incompleto od errato basta solo modificare l'enum posto all'inizio del programma

  TString16 codpag(_doc->get(DOC_CODPAG));

  _cpg->put("CODTAB",codpag);
  if (_cpg->read()!= NOERR)
  {
    _error = codpag_error;
    return;
  }
  TString16 data(_doc->get(DOC_DATAINSC));
  const real totale_fatt = _doc->totale_doc(); // Importo in valuta
  const bool valuta = _doc->in_valuta();
  const real change = _doc->cambio(); 
  
  // calcolo delle scadenze
  calc_pagamento(codpag,totale_fatt,data,valuta,change);
  
  CHECK(_pagamento,"Failed to create a TPagamento");
  const int numrate = _pagamento->n_rate( );  
  if (numrate < 1)
  {
    _error = scadenze_error;
    return;
  }
  
  _efffile->last();
  // Variabili per la scrittura dell'effetto
  long nprog = _efffile->get_long(EFF_NPROGTR)+1;
  const long codcf = _doc->get_long(DOC_CODCF);
  const TString16 codval(_doc->get(DOC_CODVAL));
  const TDate data_cambio = _doc->get_date(DOC_DATACAMBIO);
  const long codabi = _doc->get_long(DOC_CODABIA);
  const long codcab = _doc->get_long(DOC_CODCABA);
  const TString16 provv(_doc->get(DOC_PROVV));
  const int anno = _doc->get_int(DOC_ANNO);
  const TString16 codnum(_doc->get(DOC_CODNUM));
  const long nfatt = _doc->get_long(DOC_NDOC);
  const TDate datafatt = _doc->get_date(DOC_DATADOC);
  TRectype& effetto = _efffile->curr();
  TRectype& reffetto = _refffile->curr();
  real importo;
  for (int i = 0; i < numrate && good(); i++)
  {
    effetto.zero();
    reffetto.zero();
    effetto.put(EFF_NPROGTR,nprog);
    effetto.put(EFF_DATASCAD, _pagamento->data_rata(i));
    effetto.put(EFF_TIPOPAG,_pagamento->tipo_rata(i));
    effetto.put(EFF_ULTCLASS,_pagamento->ulc_rata(i));
    effetto.put(EFF_CODCF, codcf);
    effetto.put(EFF_CODVAL, codval);    
    if (valuta)
    {
      effetto.put(EFF_CAMBIO, change);        
      effetto.put(EFF_DATACAMBIO,data_cambio);
    }
    effetto.put(EFF_CODABI,codabi);
    effetto.put(EFF_CODCAB,codcab);
    effetto.put(EFF_EFFCOMP,TRUE);
    if (i == numrate - 1) effetto.put(EFF_ULTRATA,TRUE);
    // Put sulla riga dell'effetto
    reffetto.put(REFF_NPROGTR,nprog);
    reffetto.put(REFF_NRIGATR,1);
    reffetto.put(REFF_DATAFATT, datafatt);
    reffetto.put(REFF_PROVV,provv);
    reffetto.put(REFF_ANNODOC,anno);
    reffetto.put(REFF_CODNUM,codnum);
    reffetto.put(REFF_NFATT,nfatt);
    importo = _pagamento->importo_rata(i,FALSE);
    effetto.put(EFF_IMPORTO,importo);
    reffetto.put(REFF_IMPFATT,totale_fatt);
    reffetto.put(REFF_IMPORTO,importo);
    if (valuta) 
    {
      importo = _pagamento->importo_rata(i,TRUE);  // Importo in valuta
      real totfatlit = totale_fatt*change; //Not sure about this...
      effetto.put(EFF_IMPORTOVAL,importo);
      reffetto.put(REFF_IMPFATTVAL,totale_fatt);
      reffetto.put(REFF_IMPFATT,totfatlit);
      reffetto.put(REFF_IMPORTOVAL,importo);
    }
    
    if (_efffile->write() == NOERR && _refffile->write() == NOERR)
    {
      _total_bills++;
      nprog++;
    }
    else _error = write_error;
  }
  
  if (_pagamento) delete _pagamento;
  if (good() && _can_write)
    change_doc_status();
}

void TGenerazioneEffetti_app::group_bills()
{
  // Bail out if there aren't items...
  _error = no_error;
  const int items = _group_array.items();
  if (items == 0) return; 
  
  int i=0,j,n,offset=0;
  TString msg1,msg2;
  TString16 codpag;
  real imprata, imprataval, importo, importoval;
  msg1 = "Raggruppamento effetti ";
  msg1 << "dal ";
  msg1 << _data_ini.string() << " al ";
  msg1 << _data_fine.string();
  
#ifdef DBG      
  TProgind p(items,msg1,TRUE,TRUE,1);
#else
  TProgind p(items,msg1,FALSE,TRUE,1);
#endif 
  TToken_string * group_element;
  // Ciclo sugli elementi dell'assoc_array
  for (group_element = (TToken_string *) _group_array.first_item(); group_element != NULL && i < items; i++,group_element = (TToken_string*)_group_array.succ_item())
  {
#ifdef DBG      
    if (p.iscancelled()) break;
#endif
    p.setstatus(i+1);
    // Ciclo sui documenti da raggruppare
    group_element->restart();
    const int doc_items = group_element->items();
    for (j=0;j<doc_items;j++)
    {
#ifdef DBG      
      if (p.iscancelled()) break;
#endif
      TToken_string t(group_element->get(j),'$');
      char provv = t.get_char(0);
      int anno = t.get_int(1);
      TString codnum(t.get(2));
      long numdoc = t.get_long(3);
      if (_doc->read(provv,anno,codnum,numdoc) != NOERR) continue; // Legge il documento (giusto saltare e proseguire se non lo trova?)
      
      msg2 = "Documento: ";
      msg2 << codnum << "/" << numdoc;
      xvt_statbar_set(msg2);
      do_events();
      // Ricalcola le scadenze
      codpag = _doc->get(DOC_CODPAG);
    
      _cpg->put("CODTAB",codpag);
      if (_cpg->read()!= NOERR)
      {
        _error = codpag_error;
        return;
      }
      TString16 data(_doc->get(DOC_DATAINSC));
      const real totale_fatt = _doc->totale_doc();
      const bool valuta = _doc->in_valuta();
      const real change = _doc->cambio(); 
      const TDate datafatt = _doc->get_date(DOC_DATADOC);

      calc_pagamento(codpag,totale_fatt,data,valuta,change);
      CHECK(_pagamento,"Failed to create a TPagamento");
      const int numrate = _pagamento->n_rate( );  
      if (numrate < 1)
      {
        _error = scadenze_error;
        return;
      }
      
      // Scorre le scadenze
      for (n = 1; n <= numrate && good(); n++)
      {
        // Se non esiste effetto n-esimo (corrisponde al numero di rata corrente+offset)
        // lo genera con la relativa riga. Se esiste vi somma gli importi ed accoda la riga
        const bool is_new = n+offset > _effetti_array.items();
        if (is_new) // Nuovo effetto: crea effetto con i dati di questo documento (e' il primo che incontro nella scansione)
        {
          TEffetto effetto;
          // Setta i dati della testata;
          effetto.put(EFF_DATASCAD, _pagamento->data_rata(n-1));
          effetto.put(EFF_TIPOPAG,_pagamento->tipo_rata(n-1));
          effetto.put(EFF_ULTCLASS,_pagamento->ulc_rata(n-1));
          effetto.put(EFF_CODCF, _doc->get_long(DOC_CODCF));
          effetto.put(EFF_CODVAL, _doc->get(DOC_CODVAL));
          effetto.put(EFF_CAMBIO, change);
          effetto.put(EFF_DATACAMBIO,_doc->get_date(DOC_DATACAMBIO));
          effetto.put(EFF_CODABI,_doc->get(DOC_CODABIA));
          effetto.put(EFF_CODCAB,_doc->get(DOC_CODCABA));
          effetto.put(EFF_EFFCOMP,TRUE);
          _effetti_array.add(effetto);
        }
        // aggiorna totale effetto (testata)
        TEffetto& effetto=(TEffetto&)_effetti_array[n+offset-1];
        importo = effetto.get_real(EFF_IMPORTO);
        imprata = _pagamento->importo_rata(n-1,FALSE);
        importo += imprata;
        effetto.put(EFF_IMPORTO,importo);
        if (valuta)
        {
          importoval = effetto.get_real(EFF_IMPORTOVAL);
          imprataval = _pagamento->importo_rata(n-1,TRUE);
          importoval += imprataval;
          effetto.put(EFF_IMPORTOVAL,importoval);
        }
        // Crea la nuova riga di questo effetto
        const int rows = effetto.rows_r();
        TRectype& riga = effetto.row_r(rows+1,TRUE);
//        reffetto.put(REFF_NPROGTR,nprog);
        riga.put(REFF_NRIGATR,rows+1);
        riga.put(REFF_DATAFATT, datafatt);
        riga.put(REFF_PROVV,provv);
        riga.put(REFF_ANNODOC,anno);
        riga.put(REFF_CODNUM,codnum);
        riga.put(REFF_NFATT,numdoc);
        riga.put(REFF_IMPFATT,totale_fatt);
        riga.put(REFF_IMPORTO,imprata);
        if (valuta)
        {
          riga.put(REFF_IMPFATTVAL,totale_fatt);
          real totfatlit = totale_fatt*change;
          riga.put(REFF_IMPFATT,totfatlit);
          riga.put(REFF_IMPORTOVAL,imprataval);
        }
      } // Ciclo sulle scadenze
      if (_pagamento) delete _pagamento;
    } // Ciclo sui documenti di questo gruppo
    offset=_effetti_array.items();
  }   // Ciclo sui gruppi
  
  // Cambia lo stato a tutti i documenti raggruppati
  if (good())
    if (write_groups() == no_error)
    {
      _total_bills += _effetti_array.items();
      change_group_status();
    } 
}

void TGenerazioneEffetti_app::generate()
{
  TRelation doc_rel(LF_DOC);
  TRectype da(LF_DOC),a(LF_DOC);
  const long items = _num_sheet->items();
  int year_from = _data_ini.year();
  int year_to = _data_fine.year();
  TString16 codnum;
  TString msg,filt_expr;

  
  _total_bills = 0;
  _group_array.destroy();
  _effetti_array.destroy();
  set_options();
  da.put("DATADOC",_data_ini);
  da.put("PROVV","D");
  da.put("ANNO",year_from);
  a.put("DATADOC",_data_fine);
  a.put("PROVV","D");
  a.put("ANNO",year_to);
  for (long i=0L; i<items;i++)  // Scorre per tutte le numerazioni dello sheet
    if (_num_sheet->checked(i)) // Se la numerazione corrente e' stata selezionata
    {                           // istanzia un cursore per la numerazione corrente, con i limiti di data
      codnum = _num_sheet->row(i).get(1);
      filt_expr = "CODNUM=\"";
      filt_expr << codnum << "\"";
      TCursor doc_cur(&doc_rel,filt_expr,3,&da,&a);
      const long cur_items = doc_cur.items(); // Scorre tutti i documenti che rientrano nell'intervallo selezionato
      if (cur_items == 0) 
      {
        warning_box("Non vi sono effetti da generare per il codice numerazione %s",(const char*)codnum);
        continue;
      }
      msg = "Generazione effetti ";
      msg << codnum << " dal ";
      msg << _data_ini.string() << " al ";
      msg << _data_fine.string();
#ifdef DBG      
      TProgind p(cur_items,msg,TRUE,TRUE,1);
#else
      TProgind p(cur_items,msg,FALSE,TRUE,1);
#endif
      long j = 0;
      // Comportamento:
      // - scorre i documenti della numerazione corrente.
      // - se e' possibile generare subito l'effetto (tipo di pagamento valido,
      //   stato documento valido e non e' abilitato il raggruppamento) lo fa,
      //   altrimenti compone una lista dei documenti da raggruppare
      // - alla fine del ciclo per numerazioni scorre la lista residua e genera gli
      //   effetti rimasti
#ifdef DBG      
      for (;j<cur_items && !p.iscancelled();j++)
#else      
      for (;j<cur_items;j++)
#endif      
      { 
        p.setstatus(j+1);
        doc_cur = j;
        if (_doc->read(doc_cur.curr()) != NOERR) continue;// legge il documento
        if (codnum != _doc->get(DOC_CODNUM)) continue; // patch del cazzo
        if (doc_tipo_stato_ok())    // controlla che il tipo documento e lo stato siano coerenti con la ELD selezionata
        {
          if (valid_type(_doc->pagamento().tipo_rata(0))) // il tipo di pagamento puo' generare effetti ?
          {
            const bool raggruppabile = _doc->get_bool(DOC_RAGGREFF);
            if (!_raggruppa || (_raggruppa && !raggruppabile))
              generate_bill();
            else
              add_to_group_list();
          }
        }
        else
        {
          // ATTENZIONE!: se non ha ancora cominciato a generare effetti, eventuali
          // documenti con tipo o stato differenti vengono semplicemente scartati
          // senza segnalare errori
          if (_total_bills > 0)
            _error = elaboration_error;
        }
        if (!good())    
          display_error();
      }
#ifdef DBG      
      if (p.iscancelled()) break;
#endif
    }
  // Effettua il raggruppamento di eventuali documenti rimasti
  if (good()) 
    group_bills();
  
  // Mostra eventuali errori residui
  if (!good()) 
    display_error();
    
  if (_total_bills > 0)
    message_box("Totale effetti generati: %ld",_total_bills);
  xvt_statbar_set("");  
}

int ve6300 (int argc, char **argv)
{
  TGenerazioneEffetti_app a;
  a.run(argc,argv,"Generazione effetti");
  return TRUE;
}
