#ifndef __PRINTAPP_H
#include <printapp.h>
#endif

#ifndef __CONFIG_H
#include <config.h>
#endif

#ifndef __ASSOC_H
#include <assoc.h>
#endif

#ifndef __UTILITY_H
#include <utility.h>
#endif

#ifndef __SALDACON_H
#include "../cg/saldacon.h"
#endif

#ifndef __DITTE_H
#include <nditte.h>
#endif

#ifndef __CLIFO_H
#include <clifo.h> 
#endif

#ifndef __PCONTI_H
#include <pconti.h>
#endif

#ifndef __PARTITE_H
#include <partite.h>
#endif

#ifndef __SCADENZE_H
#include <scadenze.h> 
#endif

#ifndef _FLD_CAUS_H
#include <caus.h>
#endif

#include <pagsca.h>

#include "sc2.h"
#include "sc2200.h" 
#include "sc2201.h" 

#define SCADCLIFO_ALIAS 51
#define CLIFOSCAD_ALIAS 52
#define SCADPCON_ALIAS  53
#define PCONSCAD_ALIAS  54
#define MaxRowsForTotal 17

#define TPString TParagraph_string   // Comodita'...

struct _LinearTotal : public TObject  // Oggetto base per gli elementi di tlg, tlm, tlp
{                                     // Contiene i totali da stampare su una linea
  real _is, // Importo in scadenza
       _ip, // Importo pagato
       _rd, // Rimesse dirette
       _ri, // Ri.ba/Tratte
       _al; // Altri 
  virtual TObject* dup() const { return new _LinearTotal(*this); }
  _LinearTotal() {_is = 0.0; _ip = 0.0; _rd = 0.0; _ri = 0.0; _al = 0.0; }
};

enum tipo_st {nessuno=0, tutti=1, clienti=2, fornitori=3, altri=4};
enum tipo_pe {daily, monthly, period, single};

const char * tipi_tab[10] = {"Altro","Rimesse dirette","Tratte","Ricevute bancarie","Cessioni","Paghero'",
                 "Lettere di acc.","Tratte accettate","Rapp. interb. dir.","Bonifici"} ;
                 
class TStampaScadenzario : public TPrintapp
{ 
  TRelation     *_rel1, *_rel2, *_rel3;                      // Relazioni di lavoro...
  int           _cur1, _cur11,                               // Identificatori dei cursori di lavoro... 
                _cur2, _cur3, _cur4, _cur41,                 // piu' avanti spiega tutto.
                _cur5, _cur6, _cur7;                          
  TSelection_ext_mask *_m;              
  TPartita      *_p;                                         // Oggetto partita corrente. Viene ricaricato ad ogni cambio partita,
                                                             // ovvero ogni volta che la rata corrente cambia i "connotati"
  TLocalisamfile *_caus, *_partite, *_pagsca;                // File delle causali, serve per reperire le descrizioni, nel caso non ci siano sul file parite
  TString        _annopart,_datareg,_numdoc,_datadoc,        // Anno partita (2 cifre), data registrazione, nr. documento, data doc.
                 _protiva, _codval;                          // Numero di protocollo IVA (TString perche' se vale 0 non stampa nulla), e codice valuta
  TPString       *_descrizione,*_ragsoc,*_des_conto;         // Descrizione (da LF_PARTITE), ragione sociale (da LF_CLIFO)
                                                             // Descrizione conto (da LF_PCON)
  TString   _imp_scad,_imp_pag,_rimdir,_riba,_altri,         // Valori calcolati in preprocess_page: rimesse dirette, Ri.ba e altri
            _cur_gr_s, _cur_co_s, _cur_codcf_s;              // _imp_scad e _imp_pag vengono anch'essi calcolati in preprocess_page.
  TDate     _datai, _dataf, _datas,                          // Data inizio, fine e stampa
            _cur_data;                                       // Data di scadenza riferita al record corrente
  long      _cur_codcf;                                      // Cli/Fo riferito al record corrente
  int       _cur_gr,_cur_co;                                 // Gruppo/Conto riferito al record corrente.
  tipo_st   _tipost;                                         // Tipo di stampa impostato
  bool      _modified,                                       // VERO=E' stato stampato il totale giornaliero (mensile)
            _end_printed,                                    // VERO=riepilogo finale da stampare
            _ratesald,                                       // VERO=stampa anche le rate saldate
            _ordata,                                         // VERO=ordine primario per data, FALSO=ordine primario impostato da _ordcod
            _ordcod,                                         // VERO=ordine per codice, FALSO=ordine per ragione sociale
            _stvaluta;                                       // Vero se abilitata la stampa in valuta;
                                                             // Se _tipost=tutti non e' possibile impostare l'ordine primario per 
                                                             // ragione sociale o descrizione. L'unico tipo stampa possibile e' quello 
                                                             // in ordine di data scadenza
                                                             // Se _tipost=altri scorre il piano dei conti. 
                                                             // Se _tipost=clienti || fornitori scorre clifo.
                          
  real      _w_imp_pag, _w_imp_res;                          // Qui dentro memorizza i valori calcolati in calcola_pagamenti,
                                                             // rispettivamente l'importo pagato e l'importo residuo, relativo alla rata.
  TAssoc_array _tm, _tp;                                     // Dentro a codesti array vi sono i totali per la stampa prospetti:
                                                             // durante l'elaborazione cerca la chiave relativa, aggiungendo o
                                                             // aggiornando l'elemento se necessario; una volta pronto per stampare 
                                                             // il prospetto viene "ordinato" tramite la TAssoc_array::get_keys() 
                                                             // e il sort di TArray_string. _tm e' il totale del mese o del cli/fo/conto 
                                                             // (nel caso di ordinamento diversa da data), _tp e' il totale del periodo.
                                                             // La chiave e' cosi' composta: Tipo pag.+ult. class.+cod valuta
                                                             //
                                                             // Qui memorizza i totali "lineari". Analogamente ai 2 precedenti
                                                             // TAssoc_array, durante l'elaborazione cerca la chiave relativa, aggiungendo o
                                                             // aggiornando l'elemento se necessario; esegue un sort prima di stamparlo.
                                                             // Se l'ordinamento principale non e' per data utilizza tlm per i totali 
                                                             // del cli/fo/conto e tlp per i totali del periodo. La chiave e' per:
                                                             // codice valuta
  TArray      _tl;                                           // Contiene tlg, tlm, tlp (vedi user_create()), cosi' vi accedo tramite un indice.
  TArray      _uns_cache;                                    // cache dei non assegnati: l'elemento [0] contiene un TAssoc_array dei non assegnati normali,
                                                             // l'elemento [1] contiene un TAssoc_array dei non assegnati relativi a nonte dei credito. La
                                                             // chiave per tali TAssoc_array e': TIPO+GRUPPO+CONTO+SOTTOCONTO+ANNO+NUMPART.
                                                             // prima di ricalcolare i non assegnati e' necessario guardare in questa cache, se esiste la
                                                             // chiave relativa alla partita, ed eventualmente utilizzare tale valore in memoria.
  friend bool filter_func(const TRelation *);                // Funzione di filtro [_datai.._dataf]
  
public:             
  // print functions
  virtual bool preprocess_page(int file, int counter);           // Qui stampa i totali e i riepiloghi
  virtual bool preprocess_print(int file, int counter);          // Qui non fa proprio nulla!
  virtual print_action postprocess_print(int file, int counter); // Qui setta i flags per stampare il riepilogo finale!
  virtual print_action postprocess_page(int file, int counter);  // Qui fa molte cose...
  virtual void preprocess_header() {};                           // Very Very Dummy...
  virtual bool user_create();                                    // Qui creo i cursori...
  virtual bool user_destroy();                                   // e qui li distruggo.
  virtual bool set_print(int);                                   // Qui c'e' la maschera di selezione.
  virtual void set_page (int file, int counter);                 // Setta le righe di stampa.
  void    set_page_clifo(int nriga);                             // Frammenti di set_page
  void    set_page_pcon (int nriga);
  void    print_header();                                        // Stampa l'header.
  // Le seguenti funzioni valgono con ordinamento principale per data.
  void    print_rows_riepilogo(int& nriga, bool type, TAssoc_array& tot);          // Stampa effettivamente lo specchietto 
  void    print_riepilogo(int &nriga, bool type);                          // type == FALSE prints month totals, type == TRUE prints period totals.
  void    print_rows_totali(int &nriga, tipo_pe p);                  
  void    print_totali(int &nriga, bool month_changed, bool ended);        // month_changed indica se il mese e' cambiato. 
                                                                           //ended indica il raggiungimento di fine periodo.   
  // La seguente funzione serve per il riepilogo dei totali nel caso l'ordinamento principale non sia per data.
  void    print_totali_c(int &nriga, bool ended);                          // ended indica di stampare anche il riepilogo del periodo
  // Funzioni per calcolo dei pagamenti e di aggiornamento totali
  void    check_add_key_to_tp(int t, char u);                              // controlla e aggiunge i valori relativi alla chiave k ai totali del prospetto
  void    check_add_key_to_tl(tipo_pe p, int t);                           // controlla e aggiunge i valori elaborati all'assoc array indicato dal periodo (_tlxxx)
  // Ritorna TRUE se la partita corrente _p non e' piu' riferita alla scadenza corrente
  bool    scad_changed(char tipo, int gruppo, int conto, long codcf, int anno, TString& nump); 
  // Totalizza i pagamenti non assegnati per la partita corrente (NB si spera che siano tutti nella stessa valuta)
  bool    in_cache(TString& k);                  // ritorna vero se ci sono gia' non assegnati in _uns_cache;
  void    look_in_cache(real& a, real& b, TAssoc_array& uns, TAssoc_array& unsnc, TString& k);
  void    calcola_unassigned(TString& k);  
  void    calcola_pagamenti(real& imp_scad, int riga, int rata);       // calcola i pagamenti effettuati per questa rata e il residuo eventuale
  
  TStampaScadenzario();
};
                  
inline TStampaScadenzario& app() {return (TStampaScadenzario&)main_app();}

bool filter_func(const TRelation *r) 
{                                    // Filtro per rate saldate: se e' saldata e' ok solo quando
  bool            rt      = FALSE;   // e' abilitato il flag; se non e' saldata va sempre bene. 
  TLocalisamfile* scad    = &(r->lfile(LF_SCADENZE));      
  bool            saldata = scad->get_bool(SCAD_PAGATA);
  
  if (!saldata || (saldata && app()._ratesald))
  {
     rt=TRUE;
  }
  return rt;
}

void TStampaScadenzario::check_add_key_to_tl(tipo_pe p, int t)
// Aggiorna i totali per i totali sulla linea
{                                            
  TString kl(_codval);
  _LinearTotal newtot;
  int index = 0;  // Default DAILY
  switch (p) // Seleziona l'assoc array da usare
  { 
    case monthly:
    case single:    
      index = 1;
      break;
    case period:
      index = 2;    
      break;
    default:   // Se p == daily e' gia settato di default (vedi sopra)
      break;        
  }
  TAssoc_array& xassoc = (TAssoc_array&) _tl[index];
  const bool is_key = xassoc.is_key(kl); // Esiste l'elemento ?
  // Se si' allora prendi quello, altrimenti prendine uno nuovo (newtot)
  _LinearTotal& xtotal = (is_key ? (_LinearTotal&) xassoc[kl] : newtot);
  //Aggiorna xtotal (importi in scadenza, pagati, riba, rimesse e altri)
  xtotal._is += (_w_imp_pag + _w_imp_res);
  xtotal._ip += _w_imp_pag;
  switch (t)  
  {
    case 1:  
      xtotal._rd += _w_imp_res;
    break;
    case 2:  
    case 3:
      xtotal._ri += _w_imp_res;
    break;
    default:            
      xtotal._al += _w_imp_res;
    break;
  }
  xassoc.add(kl,xtotal,is_key); 
  
}

void TStampaScadenzario::check_add_key_to_tp(int t, char u)
// Aggiorna i totali per i prospetti finali
{                                     
  TString k;                // compone la chiave, valida per _tm e _tp
  k << t;
  if (u == '\0') u = ' ';   // Per indicare che non c'e' ult class
  k << u << _codval ; 

  if (_tm.is_key(k))                   // Se esiste gia' in _tm allora 
  {                                    // gli aggiunge il valore attuale
    real& r=(real&)_tm[k];
    r += _w_imp_res;
    _tm.add(k,r,TRUE);
  }
  else
    _tm.add(k,_w_imp_res);
  if (_tp.is_key(k))                   // Se esiste gia' in _tp allora 
  {                                    // gli aggiunge il valore attuale
    real& r=(real&)_tp[k];
    r += _w_imp_res;
    _tp.add(k,r,TRUE);
  }
  else
    _tp.add(k,_w_imp_res);
}

bool TStampaScadenzario::scad_changed(char tipo, int gruppo, int conto, long codcf, int anno, TString& nump)
{                                    
  if (_p == NULL) return TRUE;
  TRiga_partite& rp = _p->riga(_p->first());
  if (tipo != rp.get_char(PART_TIPOCF) || gruppo != rp.get_int(PART_GRUPPO) ||
      conto != rp.get_int(PART_CONTO) || codcf != rp.get_long(PART_SOTTOCONTO) ||
      anno != rp.get_int(PART_ANNO) || nump != rp.get(PART_NUMPART))
    return TRUE;
  return FALSE;
}   

bool TStampaScadenzario::in_cache(TString& k)
{   
  int rt = FALSE;
  TAssoc_array& uns   = (TAssoc_array&) _uns_cache[0];
  TAssoc_array& unsnc = (TAssoc_array&) _uns_cache[1];
  if (uns.is_key(k) || unsnc.is_key(k)) rt =TRUE;
  return rt;
}

void TStampaScadenzario::calcola_unassigned(TString& k)
// Calcola i pagamenti non assegnati normali, e quelli riferiti a note di credito
// ovvero quelli con tipo di movimento 2. Vanno percio' tenuti separati due totali:
// _uns_cache[0] per i non assegnati normali, utilizzato per i pagamenti;
// _uns_cache[1] per i non assegnati, riferiti a note di credito, da scalare all'importo in scadenza
{
  TAssoc_array& uns   = (TAssoc_array&) _uns_cache[0];
  TAssoc_array& unsnc = (TAssoc_array&) _uns_cache[1];
  TRecord_array& ra = _p->unassigned();
  real a,b;
  a = 0.0;
  b = 0.0;
  
  for (int r = ra.last_row(); r > 0; r = ra.pred_row(r))
  {     
    const TRectype& rec = ra.row(r);
    const TRiga_partite& sum = _p->riga(rec.get_int(PAGSCA_NRIGP));
    const char * field = (_stvaluta && sum.in_valuta() ? PAGSCA_IMPORTOVAL : PAGSCA_IMPORTO);
    if (sum.get_int(PART_TIPOMOV) == 2) // Is it a credit note ?
      b += rec.get_real(field);
    else
      a += rec.get_real(field);
  }    
  if (a != 0.0) // Aggiungilo a TAssoc_array dei non assegnati    
    uns.add(k,a,TRUE); 
  if (b != 0.0) // Aggiungilo a TAssoc_array delle note di credito
    unsnc.add(k,b,TRUE); 
}

void TStampaScadenzario::look_in_cache(real& a, real& b, TAssoc_array& uns, TAssoc_array& unsnc, TString& k)
//Estrae dai tassoc_array i valori, relativi alla partita corrente, di uns e unsnc, mettendoli in a e b
{
  a = 0.0; b = 0.0;
  TRiga_partite& rp = _p->riga(_p->first());
  k << rp.get_char(PART_TIPOCF) << rp.get_int(PART_GRUPPO) ;
  k << rp.get_int(PART_CONTO) << rp.get_long(PART_SOTTOCONTO);
  k << rp.get_int(PART_ANNO);
  k << rp.get_str(PART_NUMPART);
  if (uns.is_key(k)) a = (real&)uns[k];
  if (unsnc.is_key(k)) b = (real&)unsnc[k];
}

void TStampaScadenzario::calcola_pagamenti(real& imp_scad, int riga, int rata)
{                               
  TAssoc_array& uns = (TAssoc_array&) _uns_cache[0];
  TAssoc_array& unsnc = (TAssoc_array&) _uns_cache[1];
  TRiga_scadenze& rs = _p->rata(riga,rata);   
  const char ssez = _p->riga(riga).sezione();
  const char* field  = (_stvaluta && rs.in_valuta()) ? PAGSCA_IMPORTOVAL : PAGSCA_IMPORTO;
  const char* sfield = (_stvaluta && rs.in_valuta()) ? SCAD_IMPORTOVAL : SCAD_IMPORTO;
  TImporto totalep,     // Totale dei pagamenti
           scdz;        // Importo in scadenza  
  scdz += TImporto(ssez,rs.get_real(sfield));
  totalep += rs.importo_pagato(_stvaluta,7); // Quanto e' stato pagato per questa scadenza?
  for (int p = rs.last(); p > 0; p = rs.pred(p))   // Calcola il totale pagamenti e discrimina 
  {                                          // Il TIPOMOV: 1,5      : fattura insoluto
    const TRectype pag = rs.row(p);          //             2        : nota di credito
    const TRiga_partite& sum = _p->riga(p);  //             3,4,6    : incasso/pagamento/abbuono/pag.insoluto/diff.cambio/rit.prof
    const char sez = sum.sezione();
    const int tipomov = sum.get_int(PART_TIPOMOV);
    switch (tipomov)
    { 
      case 5: // Insoluto (aumenta scdz)
        scdz += TImporto(sez,pag.get_real(field));
        break;
      case 2: // Nota di credito assegnata (storna da scdz)
        scdz -= TImporto(sez,pag.get_real(field));
        break;
      default:
        break;
    }
  }
  char norm = 'D';
  if (_tipost == fornitori) norm = 'A';
  totalep.normalize(norm); scdz.normalize(norm);
  _w_imp_pag = totalep.valore();
  imp_scad = scdz.valore();             
  real a,b;
  TString k; k.cut(0);
  look_in_cache(a,b,uns,unsnc,k);
  if (b > 0.0) // Scala le note di credito dalle scadenze
  {
    real gap = (b > imp_scad ? imp_scad : b);
    imp_scad -= gap;
    b -= gap;
    if (b > 0.0)
      unsnc.add(k,b,TRUE);
    else            
      unsnc.remove(k); // Free some space when 0 reached.
  }
  if (a > 0.0 && imp_scad > _w_imp_pag)
  {                                 
  // Lo scalare dei non assegnati e' progressivo a partire dalla rata piu' vecchia.
  // Se ce n'e' in piu' vengono ignorati.
    real gap  = (a > imp_scad ?  imp_scad : a);
    _w_imp_pag += gap;
    a -= gap;
    if (a > 0.0)
      uns.add(k,a,TRUE);
    else
      uns.remove(k);     // Free some space when 0 reached
  }
  _w_imp_res = imp_scad - _w_imp_pag; 
}


bool TStampaScadenzario::preprocess_page(int file, int counter)
// Se ritorna FALSE salta questa scadenza e va alla prossima.
{   
  TRectype &rc = current_cursor()->curr(LF_SCADENZE);
  char    tipoc    = rc.get_char(SCAD_TIPOCF);     // Tipo
  int     gruppo   = rc.get_int(SCAD_GRUPPO);      // Gruppo
  int     conto    = rc.get_int(SCAD_CONTO);       // Conto
  long    codcf    = rc.get_long(SCAD_SOTTOCONTO); // Codice Cliente/Fornitore/Conto
  int     annop    = rc.get_int(SCAD_ANNO);        // Anno partita
  TString nump     ( rc.get(SCAD_NUMPART));        // Nr partita    
  int     nrigap   = rc.get_int(SCAD_NRIGA);       // Nr riga      
  int     nratap   = rc.get_int(SCAD_NRATA);       // Nr rata
  TDate   datascad = rc.get_date(SCAD_DATASCAD);   // Data scadenza
  int     tipo_pag = rc.get_int(SCAD_TIPOPAG);     // Tipo di pagamento
  char    ult_cla  = rc.get_char(SCAD_ULTCLASS);   // Ulteriore classificazione
  real    imp_scad ;
                   
                                                     // Importo in scadenza (vale IMPORTO o
                                                     // IMPORTOVAL a seconda sia stata impostata la
                                                     // stampa in valuta.
  
  if (_end_printed && (file == LF_CLIFO || file == LF_PCON))
    return FALSE; // Dopo aver stampato i totali finali non deve rientrare per stampare
                  // LF_CLIFO o LF_PCON 
  if (_end_printed)
  {                // Qui controlla se ci sono totali da stampare
    TAssoc_array& tp = (TAssoc_array&)_tl[2];
    if (tp.items() == 0) return FALSE;
  }                
  if (file == LF_SCADENZE && !_end_printed)
  {
    TCursor_sheet& cs = _m->cur_sheet(); // Sheet di selezione (CLI/FO/PCON)
    TCursor* c = cs.cursor();
    TRectype& rec = c->curr();
    if (_tipost == clienti || _tipost == fornitori)
      rec = current_cursor()->curr(LF_CLIFO);
    else
      if (_tipost == altri)
        rec = current_cursor()->curr(LF_PCON);
    if (!cs.checked(c->read(_isequal))) // Se non e' stato selezionato salta alla prossima scdz
      return FALSE;
    _descrizione->restart();
    if (scad_changed(tipoc,gruppo,conto,codcf,annop,nump))
    {
      TRectype rp(LF_PARTITE);
      rp.zero();
      rp.put(PART_TIPOCF,tipoc);
      rp.put(PART_GRUPPO,gruppo);
      rp.put(PART_CONTO,conto);
      rp.put(PART_SOTTOCONTO,codcf);
      rp.put(PART_ANNO,annop);
      rp.put(PART_NUMPART,nump);
      if (_p) delete _p;
      _p = new TPartita(rp);
      
      TString k; // compone la chiave dei tassoc_array dentro a _uns_cache
      k << tipoc << gruppo ;
      k << conto << codcf;
      k << annop << nump;
      if (!in_cache(k))
        calcola_unassigned(k);
      TRiga_partite& row = _p->riga(_p->first());
      _datareg = row.get_date(PART_DATAREG).string(brief);
      _datadoc = row.get_date(PART_DATADOC).string(brief);
      _numdoc  = row.get(PART_NUMDOC);
      _protiva = format("%5ld",row.get_long(PART_PROTIVA));
      *_descrizione = row.get(PART_DESCR); 
      _codval  = row.get(PART_CODVAL);              
      //Per indicare che non e' indicata la valuta o e' LIRA (se la stampa in valuta is disabled)
      if (_codval.empty() || !_stvaluta) _codval = "   "; 
      if (_descrizione->items() == 0)  // Se sulla partita non c'e' descrizione
      {                                // allora va leggerla sulla causale.
        _caus->zero(); _caus->put(CAU_CODCAUS,row.get(PART_CODCAUS));
        if (_caus->read() == NOERR) 
          *_descrizione = _caus->get(CAU_DESCR); 
      }
    }
    calcola_pagamenti(imp_scad,nrigap,nratap);
    if (_w_imp_res == 0.0 && ! _ratesald
       || imp_scad == 0.0) // Se la rata e' stata saldata e non e' abilitato il flag di stampa
      return FALSE;      // oppure l'importo in scadenza e' 0 allora salta alla prossima scadenza
    _annopart.format("%d",annop);
    _annopart.ltrim(2);
    _rimdir="";
    _riba="";
    _altri="";
    _imp_pag = "";
    if (_stvaluta && _codval != "   ")
    {
      _imp_scad = imp_scad.string(".2");
      if (_w_imp_pag != 0.0)
        _imp_pag=_w_imp_pag.string(".2");
    }
    else
    {
      _imp_scad=imp_scad.string(".");
      if (_w_imp_pag != 0.0)
        _imp_pag=_w_imp_pag.string(".");
    }
    switch (tipo_pag)
    {
      case 1:     // Rimesse dirette
        if (_w_imp_res != 0.0) 
        {
          if (_stvaluta && _codval != "   ") _rimdir = _w_imp_res.string(".2");
          else _rimdir=_w_imp_res.string(".");
        }
        break;
      case 2:     // ri.ba / Tratte
      case 3:
        if (_w_imp_res != 0.0)
        {
          if (_stvaluta && _codval != "   ") _riba = _w_imp_res.string(".2");
          else _riba=_w_imp_res.string(".");
        }
        break;
      default:        // Altri
        if (_w_imp_res != 0.0)
        {
          if (_stvaluta && _codval != "   ") _altri = _w_imp_res.string(".2");
          else _altri=_w_imp_res.string(".");      
        }
        break;
    } 
  }           

  if (file==LF_CLIFO)
  {      
    TString xxx(current_cursor()->curr(LF_CLIFO).get(CLI_RAGSOC));
    *_ragsoc = xxx.strip_d_spaces();
  }
  if (file ==LF_PCON)
    *_des_conto = current_cursor()->curr(LF_PCON).get(PCN_DESCR);

  // Se l'ordinamento principale e' per data scadenza stampa il totale del giorno e del mese
  if (_ordata && !_end_printed)                   
    if ((file == LF_SCADENZE && _tipost == tutti) ||
        (file == LF_PCON && _tipost == altri) ||
        (file ==LF_CLIFO && (_tipost == clienti || _tipost == fornitori)))
      {   
        if (_cur_data != botime && _cur_data != datascad) 
        {
          // Se la data di scadenza e' cambiata e non e' il primo record della relazione
          // allora stampa i totali del giorno. 
          // Controlla poi se e' cambiato il mese, in caso affermativo stampa anche i 
          // totali del mese e lo schema riepilogativo mensile
          bool month_changed = _cur_data.month() != datascad.month();
          
          for (int n=1; n<MaxRowsForTotal; n++) reset_row(n);
          n = 1;
          set_row(n++,"");
          print_totali(n,month_changed,FALSE);
          switch (_tipost)
          {
            case clienti:
            case fornitori:
              set_page_clifo(n);
            break;
            case altri:
              set_page_pcon(n);
            break;
          }
          _modified=TRUE;
          // Azzera i totali del giorno in ogni caso.
          TAssoc_array& tlg = (TAssoc_array&) _tl[0];
          tlg.destroy();
          // Controlla se e' cambiato il mese azzera i relativi totali 
          if (month_changed)
          {  
            _tm.destroy(); // Cancella tutti gli elementi del mese dopo averli stampati
            TAssoc_array& tlm = (TAssoc_array&) _tl[1];
            tlm.destroy();
          }
        }
        // Tiene aggiornati tutti i totali mese/giorno/periodo (Totali lineari per valuta)
        check_add_key_to_tl(daily, tipo_pag);
        check_add_key_to_tl(monthly, tipo_pag);
        check_add_key_to_tl(period, tipo_pag);
        // Tiene aggiornati tutti i totali mese/periodo (Totali dei prospetti
        check_add_key_to_tp(tipo_pag, ult_cla);
        _cur_data = datascad;
        _cur_codcf_s = format("%6ld",codcf);
     }      
  // Qui stampa il totale generale e lo specchietto colonnello.
  // Vale con l'ordinamento principale per data di scadenza.
  // N.B viene eseguito solo come ultimo blocco, grazie ad una REPEAT_PAGE in postprocess_print()
  if (_end_printed && file == LF_SCADENZE && _ordata)
  {
    reset_print();
    int n=1;
    set_row(n++,"");
    print_totali(n,TRUE,TRUE);
  }   
  // Qui stampa il totale di ogni cli/fo o conto, nel caso che l'ordinamento
  // primario non sia per data di scadenza
  if (!_ordata && !_end_printed && file!=LF_SCADENZE)  
  {
    bool changed = TRUE;
    // Vale anche con ordinamento primario per descrizione o ragione sociale: se essa cambia
    // cambia anche il trio Gr/Co/So.
    if (_cur_gr != -1 && _cur_co != -1 && _cur_codcf != -1)
      if (_cur_gr != gruppo || _cur_co != conto || _cur_codcf != codcf)
      { 
        for (int n=1; n<MaxRowsForTotal; n++) reset_row(n);
        n = 1;
        set_row(n++,"");
        print_totali_c(n,FALSE);
        if (_tipost == altri)
          set_page_pcon(n);
        else
          set_page_clifo(n);
        _modified=TRUE;
        TAssoc_array& tlm = (TAssoc_array&) _tl[1];
        tlm.destroy();
        _tm.destroy();  // Cancella i valori del cli/fo/conto dopo averli stampati
      }
      else
      { // If group/count/codcf Remains The Same  don't repeat _ragsoc, _des_conto...
        *_ragsoc = "";
        *_des_conto = "";
        _cur_gr_s = "";
        _cur_co_s = "";
        _cur_codcf_s = "";
        changed = FALSE;
      }  
    if (changed)
    {
      _cur_gr_s = format("%3d",gruppo);
      _cur_co_s = format("%3d",conto);
      _cur_codcf_s = format("%6ld",codcf);
    }
    _cur_gr=gruppo;
    _cur_co=conto;
    _cur_codcf=codcf;
    // Tiene aggiornati i totali per cli/fo/conto e periodo (Totali lineari per valuta)
    check_add_key_to_tl(single, tipo_pag);
    check_add_key_to_tl(period, tipo_pag);
    // Tiene aggiornati i totali per cli/fo/conto e periodo (Totali per i prospetti)
    check_add_key_to_tp(tipo_pag, ult_cla);
  }
  // Qui stampa il totale generale e lo schema riepilogativo.
  // Vale con l'ordinamento principale per codice/descrizione/ragione sociale.
  // N.B viene eseguito solo come ultime blocco, grazie ad una REPEAT_PAGE in postprocess_print()
  if (_end_printed && file == LF_SCADENZE && !_ordata)
  {                              
    reset_print();
    int n=1;
    set_row(n++,"");                         
    // Il confronto posso farlo solo su clienti/fornitori/altri, anche perche'
    // per tutti e' obbligatorio l'ordinamento principale per data.
    print_totali_c(n,TRUE);
  }
  return TRUE;
}

bool TStampaScadenzario::preprocess_print(int file, int counter)
{ 
  return TRUE;
}
              
print_action TStampaScadenzario::postprocess_print(int file, int counter)
{  
  print_action rt = NEXT_PAGE;            
  // Setta _end_printed e ripete la pagina. Questa condizione puo' accadere
  // solo alla fine dei record di LF_SCADENZE, percio' solo dopo aver stampato
  // tutti gli altri; sia per LF_CLIFO che per LF_PCON, purche' LF_SCADENZE 
  // sia sempre il file principale della relazione e dell'albero di stampa.
  // Ritornando REPEAT_PAGE resettera' le righe di stampa, e all'interno della
  // preprocess_page formera' le righe dei riepiloghi finali.
  // Se LF_PCON o LF_CLIFO sono i file principali della relazione e' necessario aggiungere
  // una seconda condzione, valevole solamente se l'ordine principale non e' per data scadenza.
  if (!_end_printed)      // Se non e' ancora stato stampato il totale generale...
    if ((file == LF_SCADENZE)) //&& _ordata) || (file !=LF_SCADENZE && !_ordata))
    {             
      rt = REPEAT_PAGE;
      _end_printed = TRUE;
    }
  return rt;
}

print_action TStampaScadenzario::postprocess_page(int file, int counter)
{   
  if (_modified)
    reset_print();
  return NEXT_PAGE;
}

bool TStampaScadenzario::user_create()
{                                                
  _rel1 = new TRelation(LF_SCADENZE);           // Scadenze per data scadenza + CliFo (Codice & Ragione sociale)
  _rel1->add(LF_CLIFO,"TIPOCF=TIPOC|CODCF=SOTTOCONTO",1,0,SCADCLIFO_ALIAS);
  _cur1 = add_cursor(new TSorted_cursor(_rel1,"DATASCAD|SOTTOCONTO|ANNO|NUMPART|NRIGA|NRATA","",2));
  // Per data scadenza + ragione sociale e' necessario un TSorted_cursor con update del file collegato:
  _cur11 = add_cursor(new TSorted_cursor(_rel1,"DATASCAD|UPPER(20->RAGSOC)|ANNO|NUMPART|NRIGA|NRATA","",2));
  
  // Scadenze per CliFo (Codice & Ragione sociale) + data scadenza
  _cur2 = add_cursor(new TSorted_cursor(_rel1,"SOTTOCONTO|DATASCAD|ANNO|NUMPART|NRIGA|NRATA","",2)); 
  _cur3 = add_cursor(new TSorted_cursor(_rel1,"UPPER(20->RAGSOC)|DATASCAD|ANNO|NUMPART|NRIGA|NRATA","",2));
  
  _rel2 = new TRelation(LF_SCADENZE);           // Scadenze per data scadenza + piano dei conti (Codice & Descrizione)
  _rel2->add(LF_PCON,"GRUPPO=GRUPPO|CONTO=CONTO|SOTTOCONTO=SOTTOCONTO",1,0,SCADPCON_ALIAS);
  _cur4 = add_cursor(new TSorted_cursor(_rel2,"DATASCAD|GRUPPO|CONTO|SOTTOCONTO|ANNO|NUMPART|NRIGA|NRATA","",2)); 
  // Per data scadenza + descrizione e' necessario un TSorted_cursor con update del file collegato:
  _cur41 = add_cursor(new TSorted_cursor(_rel2,"DATASCAD|UPPER(19->DESCR)|ANNO|NUMPART|NRIGA|NRATA","",2));
  
  // Scadenze per Conti (Codice & descrizione) + data scadenza 
  _cur5 = add_cursor(new TSorted_cursor(_rel2,"GRUPPO|CONTO|SOTTOCONTO|DATASCAD|ANNO|NUMPART|NRIGA|NRATA","",2)); 
  _cur6 = add_cursor(new TSorted_cursor(_rel2,"UPPER(19->DESCR)|DATASCAD|ANNO|NUMPART|NRIGA|NRATA","",2));
  
  _rel3 = new TRelation(LF_SCADENZE);           // Scadenze (Tutte) per data scadenza
  _cur7 = add_cursor(new TCursor(_rel3,"",2));
  
  _caus    = new TLocalisamfile(LF_CAUSALI);
  _partite  = new TLocalisamfile(LF_PARTITE);
  _pagsca   = new TLocalisamfile(LF_PAGSCA);
  _descrizione = new TParagraph_string("",17);
  _ragsoc = new TParagraph_string("",17);
  _des_conto = new TParagraph_string("",17);
  
  _tl.add(new TAssoc_array);_tl.add(new TAssoc_array);_tl.add(new TAssoc_array);
  _uns_cache.add(new TAssoc_array);_uns_cache.add(new TAssoc_array);
  TConfig conf (CONFIG_DITTA,"cg");
  _m = new TSelection_ext_mask("sc2200a");
  _m->enable(F_VALUTA,conf.get_bool("GesVal")); 
  
  enable_print_menu();               
  return TRUE;
}

bool TStampaScadenzario::user_destroy()
{
// 
// Color che di Printapp conoscon nulla,
// evitar, dovrebber, il delete che trastulla
// onde la nausea, ch'assale il tuo fardello,
// mejo la new lasciar nel suo castello.
//
// Nota del trastullatore:
// Mai fare la delete di un cursore usato
// dalla printapp, poiche' e' lei stessa che
// effettua tale operazione nella propria destroy().
// 
  if (_rel1)  delete _rel1;
  if (_rel2)  delete _rel2;
  if (_rel3)  delete _rel3;
  if (_caus)
    delete _caus;   
  if (_partite)
    delete _partite;   
  if (_pagsca)
    delete _pagsca;   
  if (_descrizione)
    delete _descrizione;
  if (_des_conto)
    delete _des_conto;
  if (_ragsoc)
    delete _ragsoc;
  _tl.destroy();
  _uns_cache.destroy();
  _tm.destroy();
  _tp.destroy();
  if (_m) delete _m;
// la delete di _tl viene fatta nel distruttore dell'applicazione.
  return TRUE;
}

bool TStampaScadenzario::set_print(int)
{ 
  bool rt=FALSE,ok=FALSE;
  KEY t;                      
  while (!ok)  
  {
    if ((t=_m->run())==K_ENTER)
    {       
      if ( !_m->cur_sheet().checked())
        error_box("Non e' stata effettuata alcuna selezione.");
      else 
        ok=TRUE;
    }
    else ok=TRUE;
  }
  
  switch (t)
  {        
   case K_ENTER:
   {
    _end_printed = FALSE;
    reset_files();
    reset_print();
    // Inizializza i membri necessari al calcolo totali nel caso sia stato scelto l'ordine
    // primario secondo la data di scadenza.
    if (_m->get_who() == 'P') _tipost = altri;
    else if (_m->get_who() == 'C') _tipost = clienti;
         else _tipost = fornitori;
    if (_m->get_key() == 1) _ordcod = TRUE;
    else _ordcod = FALSE;
    _cur_data = botime;
    _tm.destroy(); _tp.destroy();                    // Totali per i prospetti
    for (int it=0; it < _tl.items(); it++)
    {
      TAssoc_array& aa = (TAssoc_array&) _tl[it];
      aa.destroy();  // Totali lineari (tlg, tlm, tlp)
    }
    // Inizializza i membri necessari al calcolo totali nel caso sia stato scelto l'ordine 
    // primario per codice/gr.co.so. ragione sociale/descrizione
    _cur_codcf      = -1;
    _cur_gr=_cur_co = -1;
    // Per calcolare i totali nel caso l'ordine primario non sia per data scadenze
    // utilizzera' le variabili _tp_* per il totale generale e _tm_* per i singoli totali.
    // E' necessario resettare anche queste TParagraph_string... Puo' servire in futuro...
    *_ragsoc      = "";
    *_des_conto   = "";                                                                   
    *_descrizione = "";     
    _ratesald = _m->get_bool(F_RATESALDATE);
    _ordata   = _m->get_bool(F_ORDDATA);
    _stvaluta = _m->get_bool(F_VALUTA);
    _datai    = (const char *)(_m->get(F_DATASCADENZAI));
    _dataf    = (const char *)(_m->get(F_DATASCADENZAF));
    _datas    = (const char *)(_m->get(F_DATASTAMPA));

    // N.B I cursori sono tutti TSorted_cursor; alcuni di questi(_cur11 e _cur41) referenziano a campi di sort
    // presenti anche su altri file (LF_CLIFO o LF_PCON), percio' e' necessario che il metodo filtercursor() 
    // (v. TCursor o TSorted_cursor) forzi un update della relazione ad ogni record che viene processato.
    // I cursori 1,2,4 e 6 sono TSorted_cursor anch'essi, siccome la funzione di filtro deve reperire il nr.
    // di record del file inerente alla selezione, e' necessario fare l'update ogni volta.
    // I cursori hanno tutti la stessa funzione di filtro, per la stampa della rate saldate.
    // L'unico TCursor e' _cur7, poiche' e' utilizzato per stampare solo i record di LF_SCADENZE.
    get_cursor(_cur1)->set_filterfunction(filter_func,TRUE); // Cursore 1: Datascad + Clifo (codice)
    get_cursor(_cur11)->set_filterfunction(filter_func,TRUE);// Cursore 1.1: Datascad + Clifo (ragione sociale) 
    get_cursor(_cur2)->set_filterfunction(filter_func,TRUE); // Cursore 2: Clifo(codice) + Datascad
    get_cursor(_cur3)->set_filterfunction(filter_func,TRUE); // Cursore 3: Clifo(ragione sociale) + Datascad
    get_cursor(_cur4)->set_filterfunction(filter_func,TRUE); // Cursore 4: Datascad + PCon  (codice)
    get_cursor(_cur41)->set_filterfunction(filter_func,TRUE);// Cursore 4.1: Datascad + PCon (descrizione)
    get_cursor(_cur5)->set_filterfunction(filter_func,TRUE); // Cursore 5: PCon (Codice) + Datascad  
    get_cursor(_cur6)->set_filterfunction(filter_func,TRUE); // Cursore 6: PCon (descrizione) + Datascad  
    get_cursor(_cur7)->set_filterfunction(filter_func);      // Cursore 7: Datascad
    
    // Setta l'intervallo di data. Si puo' fare per tutti i cursori, poiche' 
    // il file principale e' sempre LF_SCADENZE.
    TRectype da_data(LF_SCADENZE), a_data(LF_SCADENZE);
    da_data.zero();
    a_data.zero();
    da_data.put(SCAD_DATASCAD,_datai);
    a_data.put(SCAD_DATASCAD,_dataf);
    get_cursor(_cur1)->setregion(da_data,a_data);    
    get_cursor(_cur11)->setregion(da_data,a_data);
    get_cursor(_cur2)->setregion(da_data,a_data); 
    get_cursor(_cur3)->setregion(da_data,a_data); 
    get_cursor(_cur4)->setregion(da_data,a_data); 
    get_cursor(_cur41)->setregion(da_data,a_data);
    get_cursor(_cur5)->setregion(da_data,a_data); 
    get_cursor(_cur6)->setregion(da_data,a_data); 
    get_cursor(_cur7)->setregion(da_data,a_data); 
    switch (_tipost)
    {       
     case tutti:
       select_cursor(_cur7);
       add_file(LF_SCADENZE);
       break;
     case clienti:
     case fornitori:
       if (_ordata)                    // Ordine primario per data
       {                               // Controlla l'ordine secondario (codice o ragione sociale)
         if (_ordcod)                  // Per codice...
         {
          select_cursor(_cur1);
          if (_tipost==clienti)
            get_cursor(_cur1)->setfilter("(TIPOC=\"C\")");
          else
            get_cursor(_cur1)->setfilter("(TIPOC=\"F\")");  
         }
         else
         {
          select_cursor(_cur11);    // Per ragione sociale
          if (_tipost==clienti)
          get_cursor(_cur11)->setfilter("(TIPOC=\"C\")");
          else
          get_cursor(_cur11)->setfilter("(TIPOC=\"F\")");  
         }
       }
       else                            // Ordine primario per codice o ragione sociale
       {                               
         if (_ordcod)                  // Codice...
         {
           select_cursor(_cur2);               
           if (_tipost==clienti)
             get_cursor(_cur2)->setfilter("(TIPOC=\"C\")");
           else
             get_cursor(_cur2)->setfilter("(TIPOC=\"F\")");
         }
         else                          // Ragione sociale
         {
           select_cursor(_cur3);
           if (_tipost==clienti)
             get_cursor(_cur3)->setfilter("(TIPOC=\"C\")");
           else
             get_cursor(_cur3)->setfilter("(TIPOC=\"F\")");
         }
       }
       add_file(LF_SCADENZE);
       add_file(LF_CLIFO,LF_SCADENZE);
       break;
     case altri:
       if (_ordata)                            // Ordine primario per data
       {                                       // Controlla l'ordine secondario (Codice o descrizione)
         if (_ordcod)                          // Per codice ...
         {
            select_cursor(_cur4);
            get_cursor(_cur4)->setfilter("(TIPOC=\"\")"); // Filtro per i conti normali!
         }
         else
         {
            select_cursor(_cur41);            // Per descrizione.
            get_cursor(_cur41)->setfilter("(TIPOC=\"\")");
         }
       }
       else                                    // Ordine primario per codice o descrizione
       {
         if (_ordcod)                          // Codice...
         {
           select_cursor(_cur5);
           get_cursor(_cur5)->setfilter("(TIPOC=\"\")"); // Filtro per i conti normali!
         }
         else                                  // Descrizione
         {
           select_cursor(_cur6);
           get_cursor(_cur6)->setfilter("(TIPOC=\"\")"); // Filtro per i conti normali!
         }
       }
       add_file(LF_SCADENZE);
       add_file(LF_PCON,LF_SCADENZE);
       break;
     default:
      break;
    }
    rt=TRUE;
    break;
   }
   default:
    break;
  }     
  if (_p)     delete _p; // Questo deve essere cancellato, altrimenti rimane in memoria,
  _p = NULL;             // provocando casini al prossimo "giro"
  TAssoc_array& a =(TAssoc_array&) _uns_cache[0];
  TAssoc_array& b =(TAssoc_array&) _uns_cache[1];
  a.destroy(); b.destroy(); // Libera i TAssoc_array impiegati.
  return rt;
}                    

void TStampaScadenzario::print_header()
// Setta le righe dell'intestazione
{
  int soh = 1;    
  const long firm = get_firm();

  reset_header ();
  TLocalisamfile ditte(LF_NDITTE);
  ditte.zero();
  ditte.put(NDT_CODDITTA, firm);
  ditte.read();
  if (ditte.bad()) ditte.zero();

  TString s;
  s = ditte.get(NDT_RAGSOC);    
  TString data = _datas.string();
  TString datai = _datai.string();
  TString dataf = _dataf.string();

  switch (_tipost)
  {
   case fornitori:
   case clienti:    
   {                           
    TString rw(201);
    TString s1,s2;
    set_header (soh++, "Ditta : %ld %s@148gData@153g%s  @190gPag. @#",
               firm,  (const char *)s,  (const char *)data);
    if (_tipost==fornitori)   
    {  
     set_header(soh++,"@72g** SCADENZARIO FORNITORI **");
     s1 = "Ns."; s2 = "Banca";
    }
    else                   
    {
     set_header(soh++,"@73g** SCADENZARIO CLIENTI **");
     s1 =""; s2 = "";
    }
    set_header (soh++,"@64gPer il periodo dal %s al %s",(const char*)datai,(const char*)dataf);
    rw.fill('-');
    set_header(soh++, (const char *) rw);                          
    set_header(soh++,"@28gRif.@38gData@47gData@77gCd/Tp@91g%s",(const char*)s1);
    set_header(soh++,"Codice@9gRagione sociale@28gpartita@38gscadenza@47gRegistr."
                     "@56gDescrizione@77gPag.@86gRata@91g%s@97gVal."
                     "@106gImp. in sca.@124gImp. pagati@142gRimesse dir.@160gRi.ba./Tratte@178gAltri",(const char*)s2);
    set_header(soh++,"@28gData e Nr doc. @46gProt. IVA");
    set_header(soh++,(const char *)rw);
    break;
   }
   case altri:   // Piano dei conti
   {
    TString rw(201);
    set_header (soh++, "Ditta : %ld %s@152gData@157g%s  @190gPag. @#",
               firm,  (const char *)s,  (const char *)data);
    set_header(soh++,"@79g** SCADENZARIO **");
    set_header (soh++,"@66gPer il periodo dal %s al %s",(const char*)datai,(const char*)dataf);
    rw.fill('-');
    set_header(soh++, (const char *) rw);                    
    set_header(soh++,"@33gRif.@43gData@53gData@82gCd/Tp");
    set_header(soh++,"Gr. Co. So.@16gDescrizione@33gpartita@43gscadenza@53gRegistr."
                     "@61gDescrizione@82gPag.@89gRata@97gVal."
                     "@106gImp. in sca.@124gImp. pagati@142gRimesse dir.@160gRi.ba./Tratte@178gAltri");
                     
    set_header(soh++,"@36gData e Nr doc. @54gProt. IVA");
  
    set_header(soh++,(const char *)rw);
    break;
   }
   default:
    break;
  }
  set_header(soh,"");
}                          

void TStampaScadenzario::set_page_clifo(int nriga)
// Setta le righe per stampare le scadenze clienti/fornitori
{
  set_row(nriga++,"");           
  set_row(nriga,"@b#6t@r",&_cur_codcf_s);
  set_row(nriga,"@b@9g#a@r",_ragsoc);
  set_row(nriga,"@28g#2t",&_annopart);
  set_row(nriga,"@30g/@7s",FLD(LF_SCADENZE,SCAD_NUMPART));
  set_row(nriga,"@38g@d",FLD(LF_SCADENZE,SCAD_DATASCAD));
  set_row(nriga,"@47g#t",&_datareg);
  set_row(nriga,"@56g#a",_descrizione);
  set_row(nriga,"@77g@4,rs/@1n",FLD(LF_SCADENZE,SCAD_CODPAG),FLD(LF_SCADENZE,SCAD_TIPOPAG));
  set_row(nriga,"@86g@4n",FLD(LF_SCADENZE,SCAD_NRATA));
  if (_tipost == fornitori)
    set_row(nriga,"@91g@5pn",FLD(LF_SCADENZE,SCAD_CODABIPR,"@@@@@"));
  set_row(nriga,"@97g#t", &_codval);
  set_row(nriga,"@101g#17t",&_imp_scad);
  set_row(nriga,"@119g#17t",&_imp_pag);
  set_row(nriga,"@137g#17t",&_rimdir);    // rimesse dirette
  set_row(nriga,"@155g#17t",&_riba);      // Ri.ba. / tratte
  set_row(nriga++,"@173g#17t",&_altri);   // Altri tipi di pagamento
  set_row(nriga,"@31g#t",&_datadoc);
  set_row(nriga,"@40g#t",&_numdoc);
  set_row(nriga,"@49g#-5t",&_protiva);
}

void TStampaScadenzario::set_page_pcon(int nriga)
// Setta le righe per stampare le scadenze degli altri conti. 
{
  set_row(nriga++,"");
  set_row(nriga,"@b#3t@5g#3t@9g#6t@r",&_cur_gr_s,&_cur_co_s,&_cur_codcf_s);
  set_row(nriga,"@b@16g#a@r",_des_conto);
  set_row(nriga,"@33g#2t/@7s",&_annopart,FLD(LF_SCADENZE,SCAD_NUMPART));
  set_row(nriga,"@43g@d@53g#t",FLD(LF_SCADENZE,SCAD_DATASCAD),&_datareg);
  set_row(nriga,"@61g#a",_descrizione);
  set_row(nriga,"@82g@4,rs/@1n@89g@4n",FLD(LF_SCADENZE,SCAD_CODPAG),FLD(LF_SCADENZE,SCAD_TIPOPAG),
          FLD(LF_SCADENZE,SCAD_NRATA));
  set_row(nriga,"@97g#t", &_codval);
  set_row(nriga,"@101g#17t",&_imp_scad);
  set_row(nriga,"@119g#17t",&_imp_pag);
  set_row(nriga,"@137g#17t",&_rimdir);    // rimesse dirette
  set_row(nriga,"@155g#17t",&_riba);      // Ri.ba. / tratte
  set_row(nriga++,"@173g#17t",&_altri);     // Altri tipi di pagamento
  set_row(nriga,"@36g#t@45g#t@54g#-5t",&_datadoc,&_numdoc,&_protiva);
}

void TStampaScadenzario::set_page(int file, int counter)
{ 
  int nriga =1;                    
  print_header();
  switch (_tipost)
  {
   case fornitori:
   case clienti:
        if (file == LF_CLIFO)
          set_page_clifo(nriga);
        break;
   case altri:
        if (file == LF_PCON)
          set_page_pcon(nriga);
        break;
   default:
        break;
  }
}

void TStampaScadenzario::print_rows_riepilogo(int& nriga, bool type, TAssoc_array& tot)
{
  TString_array as;
  TAssoc_array& t = (type ? _tp : _tm);
  const int items = t.items();
  
  t.get_keys(as);
  as.sort();
  for (int i=0; i < items; i++)
  {
    TString k (as.row(i));     
    TString val(k.right(3));
    TString value;
    real& v = (real&)t[k];
    const int tipo = k[0]-'0';
    const char ult = k[1];

    if (_stvaluta && val != "   ") value = v.string(".2");
    else value = v.string(".");
    if (v != 0.0)
      set_row(nriga++,"@0g!@3g%s@25g!@27g%c@32g!@34g%s@39g!@42g%17s@59g!",
              tipi_tab[tipo], ult, (const char*) val, (const char*) value);
    // Cerca la valuta "val" nell'assoc array dei totali per il prospetto
    if (tot.is_key(val))
    {
      real& tv = (real&) tot[val];
      tv += v;
      tot.add(val,tv,TRUE);
    }
    else // Se non esiste aggiunge il valore
      tot.add(val,v);
  }  
}                     

void TStampaScadenzario::print_riepilogo(int &nriga, bool type)
// Setta le righe di stampa per lo schema riepilogativo con ordinamento primario per data
// Con type == 1 stampa il riepilogo del periodo; viceversa quello del mese.
{                 
  TString s(60);       
  TAssoc_array totali;  // Assoc array per i totali distinti per valuta
  TString_array as;
  
  s.fill('-');   
  nriga++;                                
  set_row(nriga++,"%s",(const char *)s);
  if (!type)
  {               
    if (_ordata)                     
    {
      set_row(nriga++,"@0g!@2g%s@59g!",itom(_cur_data.month()));
      set_row(nriga++,"%s",(const char*)s);
    }    
  } else
    {
      set_row(nriga,"!@5gTotali dal %s al ",_datai.string(brief));
      set_row(nriga++,"%s@59g!",_dataf.string(brief));
      set_row(nriga++,"%s",(const char *)s);
    }
  print_rows_riepilogo(nriga, type, totali);
  totali.get_keys(as);
  as.sort();
  const int items = totali.items();
  for (int i=0; i < items; i++)
  {
    TString val(as.row(i));        
    TString value;
    real& tot = (real&)totali[val];
    if (_stvaluta && val != "   ") value = tot.string(".2");
    else value = tot.string(".");

    if (tot != 0.0)
    {
      if (i == 0)
        set_row(nriga++,"%s",(const char *)s);    // Se c'e' almeno un totale != 0 allora stampa il separe'
      set_row(nriga++,"!@34g%s@42g%17s@59g!",(const char*) val, (const char*) value);
    }
    else 
    {
      if (val == "   ") val = "LIT";
      set_row(nriga++,"! Nessun valore totale in %s. @59g!",(const char*) val);
    }
  }
  set_row(nriga++,"%s",(const char *)s);   
}

void TStampaScadenzario::print_rows_totali(int &nriga, tipo_pe p)
{                                                   
  int index = 0;  // Default of DAILY
  switch (p)
  {
    case monthly:                
    case single:
      index = 1;
      break;
    case period:
      index = 2;
      break;
    default:
      break;
  }
  TAssoc_array& xassoc = (TAssoc_array&) _tl[index];
  TString_array as;
  const int items = xassoc.items();
  xassoc.get_keys(as);
  as.sort();
  for (int i=0; i < items; i++)
  {
    TString k(as.row(i)); // Chiave di ordinamento(valuta)
    TString value;
    _LinearTotal& v = (_LinearTotal&)xassoc[k];
    if (k != "   ")
      set_row(nriga,"@97g%3s",(const char*)k);
    if (_stvaluta && k != "   ") value = v._is.string(".2");
    else value = v._is.string(".");
    if (v._is != 0.0)
      set_row(nriga,"@101g%17s",(const char*) value);
    if (_stvaluta && k != "   ") value = v._ip.string(".2");
    else value = v._ip.string(".");
    if (v._ip != 0.0)
      set_row(nriga,"@119g%17s",(const char*) value);
    if (_stvaluta && k != "   ") value = v._rd.string(".2");
    else value = v._rd.string(".");
    if (v._rd != 0.0)
      set_row(nriga,"@137g%17s",(const char*) value);
    if (_stvaluta && k != "   ") value = v._ri.string(".2");
    else value = v._ri.string(".");
    if (v._ri != 0.0)
      set_row(nriga,"@155g%17s",(const char*) value);
    if (_stvaluta && k != "   ") value = v._al.string(".2");
    else value = v._al.string(".");
    if (v._al != 0.0)
      set_row(nriga,"@173g%17s",(const char*) value);
    set_row(++nriga,"");                           
  }  
  set_row(++nriga,"");                           
}

void TStampaScadenzario::print_totali(int &nriga, bool month_changed, bool ended)
// Setta le righe per stampare i totali (giorno e mese) delle scadenze clienti/fornitori
// Ordinamento primario per data!
{
  set_row(nriga,"@36g** TOTALI DEL @51g%s",_cur_data.string(brief));
  print_rows_totali(nriga, daily);
  if (month_changed)
  {
    set_row(nriga,"@36g** TOTALI DI @51g%s",itom(_cur_data.month()));
    print_rows_totali(nriga, monthly);
    print_riepilogo(nriga,FALSE);                           
  }  
  if (ended)
  {
    set_row(nriga++,"");
    set_row(nriga,"@36g** TOTALI PERIODO");
    print_rows_totali(nriga, period);
    print_riepilogo(nriga,TRUE);
    set_row(nriga++,"");set_row(nriga++,"");
    set_row(nriga++,"");set_row(nriga++,"");
  }
}

void TStampaScadenzario::print_totali_c(int &nriga, bool ended)
// Setta le righe per stampare i totali delle scadenze clienti/fornitori/conti
// Ordinamento primario per codice o ragione sociale!
{
  if (_tipost==clienti)
    set_row(nriga,"@70g** TOTALE CLIENTE");
  else
    if (_tipost ==fornitori)
      set_row(nriga,"@70g** TOTALE FORNITORE"); 
    else                                   // altri...
      set_row(nriga,"@70g** TOTALE CONTO");
  print_rows_totali(nriga, single);
  print_riepilogo(nriga,FALSE);                           
  if (ended)
  {
    set_row(nriga,"@70g** TOTALE GENERALE");
    print_rows_totali(nriga, period);
    print_riepilogo(nriga,TRUE);
    set_row(nriga++,"");set_row(nriga++,"");
    set_row(nriga++,"");set_row(nriga++,"");
  }
}

TStampaScadenzario::TStampaScadenzario()
{
  _rel1=_rel2=_rel3=NULL;
  _p = NULL;
}

int sc2200(int argc, char** argv)
{
  TStampaScadenzario app;
  app.run(argc, argv, "Stampa Scadenzario");    
  return 0;
}
