#include <tabutil.h>

#include <clifo.h>

#ifndef __APPLICAT_H
#include <applicat.h>
#endif

#ifndef __EXPR_H
#include <expr.h>
#endif

#ifndef __STACK_H
#include <stack.h>
#endif

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

#include "velib01.h"
#include "sconti.h"

#ifndef __VE0100C_H
#include "ve0100c.h"
#endif

#ifndef __VERIG_H
#include "verig.h"
#endif

#ifndef __VEUML_H
#include "veuml.h"
#endif

#ifndef __VEUML1_H
#include "veuml1.h"
#endif

#ifndef __VEINI_H
#include "veini.h"
#endif

#ifndef __DEFMASK_H
#include <defmask.h>
#endif


#ifndef __PAGAMENT_H
#include "..\cg\pagament.h"
#endif

#ifndef __SCONTI_H
#include "sconti.h"             
#endif

TSpesa_prest::TSpesa_prest(const char* codice, char tipo)
       : TRectype(LF_TAB)
{
  settab(tipo == RIGA_SPESEDOC ? "SPP" : "PRS");
  if (codice && *codice)
    read(codice);
}

TSpesa_prest::TSpesa_prest(const TRectype& rec)
      : TRectype(rec)
{
}

int TSpesa_prest::read(const char* codice)
{                                   
  TTable t(get("COD"));
  put("CODTAB", codice);
  int err = TRectype::read(t);
  if (err != NOERR)
    yesnofatal_box("Tipo spesa assente : %s", codice);
  return err;  
}

TIVA::TIVA(const char* codice)
       : TRectype(LF_TABCOM)
{
  settab("IVA");
  if (codice && *codice)
    read(codice);
}

TIVA::TIVA(const TRectype& rec)
      : TRectype(rec)
{
}

int TIVA::read(const char* codice)
{                                   
  TTable t("%IVA");
  put("CODTAB", codice);
  int err = TRectype::read(t);
  if (err != NOERR)
    yesnofatal_box("Codice IVA assente : %s", codice);
  return err;  
}

bool ora_hndl( TMask_field& field, KEY key )

{
  if (field.to_check(key))
  {
    TFixed_string ora( field.get( ), 6 );
    
    ora.trim( );
    if (ora.not_empty() || field.required() )
    {
      if ( isdigit( ora[ 0 ] ) )
      {
        if ( ora[ 2 ] != ':')
        {
          if ( ora.len( ) > 4 )
            ora.overwrite( ":", 2 );
          else
            ora.insert( ":", 2 );            
        }
      }
      const bool ok = ((isdigit(ora[0]))&&(isdigit(ora[1]))&&(isdigit(ora[3]))&&(isdigit(ora[4]))) &&
                      ((atoi(&(ora[0]))<24)&&(atoi(&(ora[3]))<60));
      if (ok )
        field.set((ora));          
      else  
        return error_box("Ora errata o formato non valido");
    }
  }
  return TRUE;
}

bool dummy_hndl(TMask_field& field, KEY key)
{
  warning_box( "Al campo %d è arrivato un KEY %d", field.dlg( ), key );
  return TRUE;
}

// Handler per il calcolo delle date di pagamento
bool condpag_hndl( TMask_field& field, KEY key )
{                
  TDocumento_mask& m = (TDocumento_mask &) field.mask( );
  
  if ( field.to_check(key) || (key == K_TAB && !m.is_running()))
  {
    const TString16 condpag(m.get(F_CODPAG));
    TString16 data(m.get(F_DATAINSC));      
    
    if (data.empty())     
      data = m.get(F_DATADOC);      
    if ( condpag.not_empty())
    {
      TPagamento pag(condpag, data); 
      
      pag.set_total( 100, 10, 10 );
      pag.set_rate_auto( );      
      int numrate = pag.n_rate( );
      if (numrate > 5)
        numrate = 5;
      for( int i = 0; i < numrate; i ++ )
      {
        m.show( F_DATASCAD1 + i );
        m.set( F_DATASCAD1 + i, pag.data_rata(i).string());
      }
      for( ; i < 5; i ++ )
        m.hide( F_DATASCAD1 + i );
    }
  }
  return TRUE;
}

bool note_hndl( TMask_field& f, KEY key )
{             
  TDocumento_mask & m = (TDocumento_mask &) f.mask();

  if (key == K_TAB && (f.focusdirty() || !m.is_running()))
  {
    TTable & note = (TTable &) ((TEdit_field &) f).browse()->cursor()->file();
    const TString16 cod(f.get());
    
    if (cod != note.get("CODTAB"))
    {
      note.zero();
      note.put("CODTAB", cod);
      if (note.read() != NOERR)
        note.zero();
    }
    const bool reg_disabled = note.get_bool("B0");
    
    if (reg_disabled)
      message_box("Registrazione disbilitata : %s", (const char *) note.get("S0"));
    m.enable(DLG_SAVEREC, !reg_disabled);
  }
  return TRUE;
}

// Handler per il calcolo delle date di pagamento
bool data_hndl( TMask_field& field, KEY key )
{                
  TDocumento_mask& m = (TDocumento_mask &) field.mask( );
  if (key == K_ENTER && field.dirty())
  {          
    TLocalisamfile doc(LF_DOC);           
    const TDate datadoc(m.get(F_DATADOC));
    
    doc.curr() = m.doc().head(); 
    const TString16 codnum(doc.get("CODNUM"));
    const int anno = doc.get_int("ANNO");
    const char tipo_num = doc.get_char("PROVV");
    
    if (doc.read(_isgreat) == NOERR &&
        codnum == doc.get("CODNUM") &&
        anno == doc.get_int("ANNO") &&
        tipo_num == doc.get_char("PROVV") &&
        datadoc > doc.get_date("DATADOC"))
      return field.error_box("Data documento superiore alla data del documento successivo");
    
    doc.curr() = m.doc().head(); 
    doc.read();
    if (doc.prev() == NOERR &&
        codnum == doc.get("CODNUM") &&
        anno == doc.get_int("ANNO") &&
        tipo_num == doc.get_char("PROVV") &&
        datadoc < doc.get_date("DATADOC"))
      return field.error_box("Data documento inferiore alla data del documento precedente");
  }
  return TRUE;
}
  
// handler delle righe

HIDDEN void row_set_handler( TMask& m, const int field, const int index )
{
  switch ( index )
  {
    case 1:
      m.set_handler( field, dummy_hndl );
      break;
    default:
      yesnofatal_box( FALSE, "Funzione di handler sulla riga non definita( %d ).", index );
  }
}
                                     
HIDDEN TString16 curr_um;                                                     
HIDDEN real curr_fc(1.0);

HIDDEN bool iva_handler( TMask_field& f, KEY key )
{   
  if (key == 0 || (key == K_ENTER && f.get().empty()))
  {
    TDocumento_mask & mask = (TDocumento_mask &) f.mask().get_sheet()->mask();
    const TString16 codiva = mask.condv().clifo().get(LF_CFVEN, "ASSFIS");

    if (codiva.not_empty())
      f.set(codiva);
    f.check();
  }
  return TRUE;
}
                                       
HIDDEN bool codart_handler( TMask_field& f, KEY key )
{
  TMask& row_mask = f.mask();   
              
  if (key == K_TAB && (f.focusdirty() || row_mask.get(FR_DESCR).empty()))
  {            
    TDocumento_mask & mask = (TDocumento_mask &) row_mask.get_sheet()->mask();
    TCond_vendita & condv = mask.condv();
    
    condv.set_testa(&mask);
    condv.set_riga(&row_mask);

    TLocalisamfile & anamag = ((TEdit_field &) f).browse()->cursor()->file();
    TLocalisamfile & umart  = ((TEdit_field &) row_mask.field(FR_UMQTA)).browse()->cursor()->file();
    
    condv.set_anamag(anamag);
    condv.set_umart(umart);
    TString80 codart(f.get());   
    anamag.setkey(1);
    anamag.put("CODART", codart);
    if (anamag.read() != NOERR)
    {
      TLocalisamfile codalt(LF_CODCORR);
      
      codalt.setkey(2);
      codalt.put("CODARTALT", codart);
      if (codalt.read() == NOERR)
      {                              
        codart = codalt.get("CODART");
        anamag.zero();
        anamag.put("CODART", codart);
        if (anamag.read() == NOERR)
          f.set(codart);
      }
    }   

    if (anamag.good())
    {
      const TString16 lingua = mask.get(F_CODLIN);                              
      const TString codart(row_mask.get(FR_CODART));
      TString desc(anamag.get("DESCR"));
      
      if (lingua.not_empty())
      {
        TLocalisamfile deslin(LF_DESLIN);
        
        deslin.setkey(2);
        deslin.put("CODART", codart);
        deslin.put("CODLIN", lingua);
        if (deslin.read() == NOERR)
          desc = deslin.get("DESCR");
      }
      row_mask.set(FR_DESCR, desc);
                                     
      umart.setkey(1);              
      umart.zero(); 
      umart.put("CODART", codart);
      if (umart.read(_isgteq) == NOERR && codart == umart.get("CODART"))
      {
        curr_um = umart.get("UM");       
        curr_fc = umart.get_real("FC");
      }
      else
      {
        curr_um.cut(0);                  
        curr_fc = 1.0;
      }
      row_mask.set(FR_UMQTA, curr_um);
    }

    condv.ricerca();
    const int pos = row_mask.id2pos(FR_CODIVA);

    if (pos >= 0)
      iva_handler(row_mask.fld(pos), 0);
  }
  return TRUE;
}

HIDDEN bool umart_handler( TMask_field& f, KEY key )
{
  // Se qualcuno cerca di modificare la maschera
  if ( key == K_TAB && f.focusdirty())
  { 
    TMask& row_mask = f.mask( );               
    TDocumento_mask & mask = (TDocumento_mask &) row_mask.get_sheet()->mask();
    TLocalisamfile & anamag = ((TEdit_field &) row_mask.field(FR_CODART)).browse()->cursor()->file();
    TLocalisamfile & umart  = ((TEdit_field &) f).browse()->cursor()->file();
    TCond_vendita & condv = mask.condv();
    
    condv.set_testa(&mask);
    condv.set_riga(&row_mask);
    condv.set_anamag(anamag);
    condv.set_umart(umart);
    condv.ricerca(TRUE);

    const TString16 um(f.get());
    real fc(1.0);
    
    if (um.not_empty() && curr_um.not_empty() && um != curr_um)
    {           
      umart.setkey(2);                               
      umart.put("CODART", row_mask.get(FR_CODART));
      umart.put("UM", um);
      if (umart.read() == NOERR)
      {
        real qta(row_mask.get_real(FR_QTA));
        qta *= curr_fc;          
        fc = umart.get_real("FC");
        qta /= fc;
        row_mask.set(FR_QTA, qta);
      }
        
    }
    curr_um = um;
    curr_fc = fc;
  }   
  return TRUE;
}

HIDDEN bool descr_handler( TMask_field& f, KEY key )
{
  if (key == K_TAB && f.focusdirty())
  {            
    const TString s(f.get());
    if (s.find('\n') < 0)
    {
      TLocalisamfile & anamag = ((TEdit_field &) f).browse()->cursor()->file();
      
      anamag.zero();
      anamag.setkey(2);
      anamag.put("DESCR", ((TZoom_field &) f).get_first_line());
      if (anamag.read() == NOERR)
      {                         
        f.mask().set(FR_CODART, anamag.get("CODART"));
        f.mask().field(FR_CODART).set_dirty();
        f.mask().check_field(FR_CODART);
      }
    }
  }       
  return TRUE;
}

HIDDEN bool qta_handler( TMask_field& f, KEY key )
{                   
  // Se qualcuno cerca di modificare la maschera
  if ( key == K_TAB && f.focusdirty())
  { 
    TMask& row_mask = f.mask( );               
    TDocumento_mask & mask = (TDocumento_mask &) row_mask.get_sheet()->mask();
    TLocalisamfile & anamag = ((TEdit_field &) row_mask.field(FR_CODART)).browse()->cursor()->file();
    TLocalisamfile & umart  = ((TEdit_field &) row_mask.field(FR_UMQTA)).browse()->cursor()->file();
    TCond_vendita & condv = mask.condv();
    
    condv.set_testa(&mask);
    condv.set_riga(&row_mask);
    condv.set_anamag(anamag);
    condv.set_umart(umart);
    condv.ricerca(FALSE, TRUE);
  } 
  return TRUE;
}
  
HIDDEN void sppr_calc(TRectype & rec, const TString & valuta_doc, const real & cambio, real & prezzo)
{     
  const TString16 sppr_valuta(rec.get("S4"));
        
  if (sppr_valuta != valuta_doc && cambio != 0.0)
  {                           
    TTable val("%VAL");
          
    val.put("CODTAB", sppr_valuta);
    if (val.read() == NOERR)
    {
      const real sppr_cambio = val.get_real("R10");
      if (sppr_cambio != ZERO)
        prezzo *= sppr_cambio;
    }
    prezzo /= cambio;
  }
}
  
HIDDEN bool sppr_handler( TMask_field& f, KEY key )
{ 
  TMask& row_mask = f.mask();               
  
  if (key == K_TAB && (f.focusdirty() || row_mask.get(FR_DESCR).empty()))
  {                  
    const int pos = row_mask.id2pos(FR_PREZZO);
    
    if (pos >= 0)
    {
      TMask & mask = row_mask.get_sheet()->mask();
      TRectype & spprrec = ((TEdit_field &) row_mask.field(FR_CODART)).browse()->cursor()->file().curr();
      
      if (spprrec.get("CODTAB") == row_mask.get(FR_CODART) && spprrec.get_char("S6") != 'P')
      {
        const real cambio = mask.get(F_CAMBIO);
        real prezzo = row_mask.get(FR_PREZZO);
        const TString16 doc_valuta(mask.get(F_CODVAL));
        sppr_calc(spprrec, doc_valuta, cambio, prezzo);
        row_mask.set(FR_PREZZO, prezzo);          
        const int posiva = row_mask.id2pos(FR_CODIVA);
        if (posiva >= 0)
          iva_handler(row_mask.fld(posiva), 0);
      }
    }
  } 
  return TRUE;
}

///////////////////////////////////////////////////////////
// Funzioni per il calcolo dei prezzi netti/lordi
///////////////////////////////////////////////////////////

real lordo2netto(real& lordo, const TString& codiva, int ndec)
{
  TTable tabiva("%IVA");
  real aliquota = 0.0;
  
  tabiva.put("CODTAB", codiva);
  if (tabiva.read() == NOERR) aliquota = tabiva.get_real("R0");
  
  return lordo2netto(lordo,aliquota, ndec);
}

real netto2lordo(const real& netto, const TString& codiva, int ndec)
{
  TTable tabiva("%IVA");
  real aliquota = 0.0;
  
  tabiva.put("CODTAB", codiva);
  if (tabiva.read() == NOERR) aliquota = tabiva.get_real("R0");
  
  return netto2lordo(netto,aliquota, ndec);
}

real lordo2netto(real& lordo, const real& iva, int ndec)
{
  real netto;
  real imposta = 0.0;
  real imposta_rec = 0.0;
  if (!iva.is_zero())
  {
    imposta = (lordo * iva) / (iva + 100.0); // Calcola l'imposta...
    if (imposta < ZERO)
      imposta.floor(ndec);
    else
      imposta.ceil(ndec);
  }
  netto = lordo - imposta; // Questo e' l'importo netto
  imposta_rec = (netto * iva) / 100.0; // Ricalcola l'imposta con il nuovo imponibile
  if (imposta_rec < ZERO)
    imposta_rec.floor(ndec);
  else
    imposta_rec.ceil(ndec);
  if (imposta != imposta_rec) // In questo caso corregge l'importo lordo 
    lordo = netto + imposta_rec;
  return netto;
}

real netto2lordo(const real& netto, const real& iva, int ndec)
{
  real lordo;
  real imposta = 0.0;
  if (!iva.is_zero())
  {
    imposta = (netto * iva) / 100.0; // Calcola l'imposta
    if(imposta < ZERO)
      imposta.floor(ndec);
    else
      imposta.ceil(ndec);
  }
  lordo = imposta + netto; // prezzo lordo
  return lordo;
}

real prezzo_scontato(const real& prezzo, const char * sconto)
{    
  real scontato = prezzo;
  
  if (sconto && *sconto)
  {
    TCond_vendita cv;      

    cv.set_sconto(sconto);
    scontato = cv.sconto_val() * prezzo;
  }               
  return scontato;
} 

enum TTipo_calcolo { _nessun_calcolo, _qtaprezzo, _valore, _percentuale, _scontoimp, _scontoperc};

real iva(real imponibile, const TIVA & iva,int ndec)   
{            
   real val = imponibile * iva.aliquota() / 100.0;
                  
   if (ndec < 20)
   {               
     if (val < ZERO)
       val.floor(ndec);
     else               
       val.ceil(ndec);
   }
   return val;
}

real TDocumento::spese_incasso(real & imp, int ndec, TTipo_importo t) const
{        
  real imp_spese;   
  real percentuale = get_real("PERCSPINC");
  static TArray spese_inc;
  
  if (percentuale > ZERO)
  {
    if (spese_inc.objptr(_rim_dir) == NULL)
    {
      TConfig conf(CONFIG_STUDIO);
      
      for (TTipo_pag p = _rim_dir; p < _nessun_pag; p = (TTipo_pag) ((int)p + 1)) 
      {
        real r(conf.get("IMPSPINC", "ve", p));
        spese_inc.add(r, p);
      }
    }   
    TPagamento & pag = ((TDocumento *)this)->pagamento();  
    const int nrate = pag.n_rate();
    
    for (int i = 0; i < nrate; i++)
    {
      const TTipo_pag p = (TTipo_pag) pag.tipo_rata(i);
      imp_spese += (real &) spese_inc[p];
    }              
    imp_spese *= percentuale / 100.0;
    
    real cambio = get_real("CAMBIO");
        
    if (cambio == ZERO)
      cambio = 1.0;
//    if (netto == FALSE)
//    {     
      static TString16 codiva;
      static long firm = -1;
      long new_firm = main_app().get_firm();
          
      if (firm != new_firm)
      {
        TConfig conf(CONFIG_DITTA);
          
        codiva = conf.get("SPINCODIVA", "ve");
        firm = new_firm;
      }
      real iva_spese(iva(imp_spese, TRiga_documento::iva(codiva), ndec));                      
//    }
    if (t == _lordo)
      imp_spese += iva_spese;
    else
      if (t == _imposta)
        imp_spese = iva_spese;
    imp_spese /= cambio;
    imp_spese.round(ndec);
  }
  return imp_spese;
}

real TDocumento::bolli(real & imp, int ndec, TTipo_importo t) const
{                         
  real tot_bolli;
  static TArray sca_bolli;
  static TArray imp_bolli;
  static int nscagl;
  TLocalisamfile clifo(LF_CLIFO);
  bool estero = 2;

  if (get_bool("ADDBOLLI"))
  {
    real cambio = get_real("CAMBIO");
        
    if (cambio == ZERO)
      cambio = 1.0;
  
    real importo = imp*cambio;
    TPagamento & pag = ((TDocumento*)this)->pagamento();  
    const int nrate = pag.n_rate();
    real old_bolli = -1.00;
    real iva_bolli;    
    real imp_orig = imposta();
    real sp_orig = spese();
  
    for (int j = 0; j < 5 && tot_bolli != old_bolli; j++)
    {
      old_bolli = tot_bolli + iva_bolli;  
      const real imposte = imp_orig * cambio + iva_bolli;
      const real imp_spese = sp_orig * cambio + tot_bolli - iva_bolli;
      const real imponibile = importo - imposte - imp_spese;
      
      tot_bolli = ZERO;      
      pag.set_total(imponibile, imposte, imp_spese);  
      pag.set_rate_auto();
  
      for (int i = 0; i < nrate; i++)
      {
        const TTipo_pag p = (TTipo_pag) pag.tipo_rata(i);
        real imp = pag.importo_rata(i);
        
        switch (p)
        {         
          case _ric_ban: 
            {
              if (sca_bolli.objptr(0) == NULL)
              {
                TConfig conf(CONFIG_STUDIO);
                
                for (nscagl = 0; nscagl < 7; nscagl++) 
                {
                  real s(conf.get("SPBOSCA", "ve", nscagl + 1));
                  real i(conf.get("SPBOIMP", "ve", nscagl + 1));
                                                      
                  if (s == ZERO && i == ZERO)
                    break;
                  sca_bolli.add(s, nscagl);
                  imp_bolli.add(i, nscagl);
                }
              }   
              for (int i = 0; i < nscagl - 1; i++)
                if ((real &) sca_bolli[i] >= imp)
                  break;
              if (imp_bolli.items() > 0)
                tot_bolli += (real &) imp_bolli[i];
            }
            break;
          case _tratta:
          case _tratta_acc:
            {             
              if (estero == 2)
              {
                clifo.put("TIPOCF", get("TIPOCF"));
                clifo.put("CODCF", get("CODCF"));
                
                if (clifo.read() != NOERR)
                  clifo.zero();              
                  
                const TString16 stato(clifo.get("STATOPAIV"));
                
                estero = stato.not_empty() && stato != "IT";
                if (!estero)
                  estero = clifo.get("STATOCF").not_empty() || clifo.get("COMCF")[0] == 'Z';
              }
              real r(imp);
              
              r.ceil(-3);
              if (estero)
                r *= 0.009;
              else
                r *= 0.012;
              r.round(-2);
              tot_bolli += r;
            }
            break;
          case _cessione:
          case _paghero:
          case _let_cred:
          case _rim_dir:
          case _rid:
          case _bonfico:
          default:
            break;
        }
      }
//      if (netto == FALSE)
//      {     
        static TString16 codiva;
        static long firm = -1;
        long new_firm = main_app().get_firm();
          
        if (firm != new_firm)
        {
          TConfig conf(CONFIG_DITTA);
          
          codiva = conf.get("SPBOCODIVA", "ve");
          firm = new_firm;
        }
        iva_bolli = iva(tot_bolli, TRiga_documento::iva(codiva), ndec);                      
//       tot_bolli += iva_bolli;
//      }
      importo += (tot_bolli + iva_bolli - old_bolli);
    }        
    if (t == _lordo)
      tot_bolli += iva_bolli;
    else
      if (t == _imposta)
        tot_bolli = iva_bolli;
    tot_bolli /= cambio;
    tot_bolli.round(ndec);
  }

  return tot_bolli;
}
   
///////////////////////////////////////////////////////////
// Formula generica
///////////////////////////////////////////////////////////
                
HIDDEN enum _formula {_somma, _bolli, _bolli_int, _spinc, _prezzo, _importo, _sconto, _iva, _provv, _tipo, _imponibili, _imposte};

TExpr_documento::TExpr_documento(const char* expression, TTypeexp type,
                                 TDocumento * doc, TRiga_documento * row)
              : TExpression(type), _doc(doc), _row(row)
{
  if (!set(expression, type))
    error_box("Wrong expression : %s", expression);
}         

int TExpr_documento::parse_user_func(const char * name, int nparms) const
{    
  if (strcmp(name, "SOMMA") == 0)
    return nparms > 0 || nparms < 3 ? _somma : -1;
  else
    if (strcmp(name, "BOLLI") == 0)
      return nparms > 0 || nparms < 4 ? _bolli : -1;
    else
      if (strcmp(name, "_BOLLI") == 0)
        return nparms > 0 || nparms < 3 ? _bolli_int : -1;
      else
        if (strcmp(name, "SPESEINC") == 0)
          return nparms > 0 || nparms < 4 ? _spinc : -1;
        else
          if (strcmp(name, "PREZZO") == 0)
            return  nparms < 4 ? _prezzo : -1;
          else
            if (strcmp(name, "IMPORTO") == 0)
              return  nparms < 4 ? _importo : -1;
            else
              if (strcmp(name, "SCONTO") == 0)
                return nparms < 2 ? _sconto : -1;
              else
                if (strcmp(name, "IVA") == 0)
                  return nparms < 2 ? _iva : -1;
                else
                  if (strcmp(name, "PROVV") == 0)
                    return nparms < 2 ? _provv : -1;
                  else
                    if (strcmp(name, "TIPO") == 0)
                      return nparms == 0 ? _tipo : -1;
                    else 
                      if (strcmp(name, "IMPONIBILI") == 0)
                        return nparms < 3 ? _imponibili : -1;
                      else 
                        if (strcmp(name, "IMPOSTE") == 0)
                          return nparms < 3 ? _imposte : -1;
                        else
                          return -1;
}    

void TExpr_documento::evaluate_user_func(int index, int nparms, TEval_stack & stack, TTypeexp type) const
{                 
  switch (index)
  { 
    case _somma:
      {    
        const TString cond(nparms == 2 ? stack.pop_string() : "STR(1)");
        const TString & fieldname = stack.pop_string();
        real somma;
          
        if (_doc != NULL)
        {
          TExpr_documento cond_expr(cond, _strexpr, _doc);
          const int nrows = _doc->rows();
          const int nvars = cond_expr.numvar();
            
          for (int i = nrows; i > 0 ; i--)
          {
            TRiga_documento & riga = (TRiga_documento &) (*_doc)[i];
              
            for (int j = nvars - 1; j >= 0; j--)
            {
              const char* s = cond_expr.varname(j);
              TFieldref f(s,0);
              cond_expr.setvar(j, f.read(riga));
            }                       
            cond_expr.set_row(&riga);
            if ((bool)cond_expr)
              somma += riga.get_real(fieldname);
          }
        }
        stack.push(somma);
      }      
      break;
    case _spinc:
      {
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;
        bool netto = FALSE;
        
        if (nparms > 2)
          ndec = (int) stack.pop_real().integer();
        if (nparms > 1)
          netto = !stack.pop_real().is_zero();
          
        real & r = stack.peek_real();
                  
        if (_doc)          
          r = _doc->spese_incasso(r, ndec, netto ? _netto : _lordo);
        else
          r = ZERO;
      }    
      break;
    case _bolli:
      {
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;
        bool netto = FALSE;
        
        if (nparms > 2)
          ndec = (int) stack.pop_real().integer();
        if (nparms > 1)
          netto = !stack.pop_real().is_zero();
          
        real & r = stack.peek_real();
          
        if (_doc)
        {
          r += _doc->spese_incasso(r, ndec);
          r = _doc->bolli(r, ndec, netto ? _netto : _lordo);
        }
        else
          r = ZERO;
      } 
      break;
    case _bolli_int:
      {
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;
        
        if (nparms > 2)
          ndec = (int) stack.pop_real().integer();
          
        real & r = stack.peek_real();
        
        if (_doc)
        {
          real r1 = _doc->spese_incasso(r, ndec);
          r += r1;
          r1 += _doc->bolli(r, ndec);
          r = r1;
        }
        else
          r = ZERO;
      } 
      break;
    case _prezzo:
      {  
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;
        bool lordo = FALSE;
        bool scontato = FALSE;
  
        if (nparms > 2)
          ndec = (int) stack.pop_real().integer();
        if (nparms > 1)
          lordo = !stack.pop_real().is_zero();
        if (nparms > 0)
          scontato = !stack.peek_real().is_zero();
        else  
          stack.push(ZERO);
          
        real & val = stack.peek_real();
        if (_row)
          val = _row->prezzo(scontato, lordo, ndec);
        else val = ZERO;

      }
      break;
    case _importo:
      {  
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;
        bool lordo = FALSE;
        bool scontato = FALSE;
  
        if (nparms > 2)
          ndec = (int) stack.pop_real().integer();
        if (nparms > 1)
          lordo = !stack.pop_real().is_zero();
        if (nparms > 0)
          scontato = !stack.peek_real().is_zero();
        else  
          stack.push(ZERO);

        real & val = stack.peek_real();   
        if (_row)
          val = _row->importo(scontato, lordo, ndec);
        else
          val = ZERO;
      }
      break;
    case _sconto:
      {                                       
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;

        if (nparms > 0)
          ndec = (int) stack.peek_real().integer();
        else
          stack.push(ZERO);

        real & val = stack.peek_real();
        if (_row)
        {
          if (_row->tipo().tipo() == RIGA_SCONTI)
            val = -_row->importo(FALSE, FALSE, ndec);
          else
            val = _row->importo(FALSE, FALSE, ndec) - _row->importo(TRUE, FALSE, ndec);
        } 
        else
          val = ZERO;
      }
      break;
    case _iva:
      {  
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;

        if (nparms > 0)
          ndec = (int) stack.peek_real().integer();
        else
          stack.push(ZERO);

        real & val = stack.peek_real();
                      
        if (_row)              
//          val = _row->iva(ndec);
          val = _row->imposta();
        else
          val = ZERO;
      }
      break;
    case _provv:
      {  
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;

        if (nparms > 0)
          ndec = (int) stack.peek_real().integer();
        else
          stack.push(ZERO);

        real & val = stack.peek_real();

        if (_row)
        {
          val = _row->importo(TRUE, FALSE, ndec) * _row->get_real("PERCPROV") / 100.0; 
          val.round(ndec);
        }
        else
          val = ZERO;
      }
      break;
    case _tipo:
      {
        TString s; 
        if (_row)
          s << _row->tipo().tipo();
        stack.push(s);
      }
      break;  
    case _imponibili:
      {
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;
        bool spese = FALSE;
    
        if (nparms > 1)
          ndec = (int) stack.pop_real().integer();
        if (nparms > 0)
          spese = !stack.pop_real().is_zero();
        else  
          stack.push(ZERO);
        real & val = stack.peek_real();
        val = _doc->imponibile(spese, ndec);
      }
      break;
    case _imposte:
      {
        int ndec = _doc && _doc->in_valuta() ? 3 : 0;
        bool spese = FALSE;
    
        if (nparms > 1)
          ndec = (int) stack.pop_real().integer();
        if (nparms > 0)
          spese = !stack.pop_real().is_zero();
        else  
          stack.push(ZERO);
        real & val = stack.peek_real();
        val = _doc->imposta(spese, ndec);
      }
      break;
    default:
      TExpression::evaluate_user_func(index, nparms, stack, type);
      break;
  }
}    

TObject* TExpr_documento::dup() const
{
  TExpr_documento* o = new TExpr_documento(*this);
  return o;
}    

///////////////////////////////////////////////////////////
// Formula generica
///////////////////////////////////////////////////////////

TFormula_documento::TFormula_documento(TTipo_formula tipo, const char* codice)
               : TRectype(LF_TABCOM), _expr(NULL)
{               
  _tab = tipo == _documento ? "FRD" : "FRR";
  settab(_tab);
  _tab.insert("%");
  if (codice && *codice)
    read(codice);
}

TFormula_documento::TFormula_documento(const TRectype& rec)
               : TRectype(rec), _expr(NULL)
{
  _tab = "%";
  _tab << rec.get("COD");
  _expr = new TExpr_documento(expr_string(), expr_type());
}

TFormula_documento::~TFormula_documento()
{
  if (_expr) delete _expr;
}

int TFormula_documento::read(const char* codice)
{           
  if (_expr != NULL)
  {
    delete _expr;
    _expr = NULL;
  }

  TTable t(_tab);

  put("CODTAB", codice);
  
  int err = TRectype::read(t);
  
  if (err == NOERR)             
  {
    const TString e(expr_string());
    
    _expr = new TExpr_documento(e, expr_type());
  }
  else
  {
    zero();
    put("CODTAB", codice);
  }
  return err;  
}


///////////////////////////////////////////////////////////
// Tipo documento
///////////////////////////////////////////////////////////

TAssoc_array TTipo_documento::_formule_documento;

TTipo_documento::TTipo_documento(const char* tipodoc)
               : TRectype(LF_TABCOM)
{
  settab("TIP");
  if (tipodoc && *tipodoc)
    read(tipodoc);
}

TTipo_documento::TTipo_documento(const TRectype& rec)
               : TRectype(rec)
{
  read_formule();
}

TTipo_documento::~TTipo_documento()
{ }

int TTipo_documento::read(const char* tipodoc)
{                                   
  TTable t("%TIP");
  put("CODTAB", tipodoc);
  int err = TRectype::read(t);
  _formule = "";
  if (err == NOERR)
    read_formule();
  else
    yesnofatal_box("Tipo documento errato: %s", tipodoc);
  return err;  
}

void TTipo_documento::read_formule()

{               
  TFilename prof(profile_name());
  
  prof.ext("ini");    
  
  TConfig profile(prof);
  
  _formule = profile.get("CAMPICALC", "MAIN");
  _formule.add(profile.get("CALCOLI", "MAIN"));
  _totale = profile.get("TOTALE", "MAIN");
  _totale_netto = "_";
  _totale_netto << _totale;
  if (_totale.not_empty() && _formule.find(_totale) < 0)
  {
    error_box("Campo totale documento (%s) sconosciuto nel tipo documento %s", (const char *) _totale, (const char *) codice());
    _totale.cut(0);
  }
  _basesconto = profile.get("BASESCONTO", "MAIN");
  if (_basesconto.not_empty() && _formule.find(_basesconto) < 0)
  {
    error_box("Campo sconto documento (%s) sconosciuto nel tipo documento %s", (const char *) _basesconto, (const char *) codice());
    _basesconto.cut(0);
  }
  _spese = profile.get("SPESE", "MAIN");
  if (_spese.not_empty() && _formule.find(_spese) < 0)
  {
    error_box("Campo spese (%s) sconosciuto nel tipo documento %s", (const char *) _spese, (const char *) codice());
    _spese.cut(0);
  }
}

TFormula_documento * TTipo_documento::succ_formula(bool restart)
{                                 
  if (restart)
    _formule.restart();
  const TString16 formula(_formule.get());
  if (formula.not_empty())
  {
    TFormula_documento * o = (TFormula_documento*)_formule_documento.objptr(formula);
    if (o == NULL)
    {
      o = new TFormula_documento(_documento, formula);
      _formule_documento.add(formula, o);
    }
    return o;
  }
  else
    return NULL;
}  

///////////////////////////////////////////////////////////
// Tipo riga di un documento
///////////////////////////////////////////////////////////

TAssoc_array TTipo_riga_documento::_formule_riga;

TTipo_riga_documento::TTipo_riga_documento(const char* tiporig)
                    : TRectype(LF_TABCOM), _mask(NULL)
{
  settab("TRI");
  _name = "verig";
  _name << codice();
  if (tiporig && *tiporig)
    read(tiporig);
}

TTipo_riga_documento::TTipo_riga_documento(const TRectype& rec)
                    : TRectype(rec), _mask(NULL)
{
  _name = "verig";
  _name << codice();
  read_formule();
}

TTipo_riga_documento::~TTipo_riga_documento()
{
  if (_mask) delete _mask;
}

int TTipo_riga_documento::read(const char* tiporig)
{                                   
  TTable t("%TRI");
  put("CODTAB", tiporig);
  int err = TRectype::read(t);
  if (err == NOERR)
    read_formule();
  else
    yesnofatal_box("Tipo riga documento errato: %s", tiporig);
  return err;  
}

void TTipo_riga_documento::read_formule()

{               
  TFilename prof(profile_name());
  
  prof.ext("ini");    
  
  TConfig profile(prof);
  
  _formule = profile.get("CAMPICALC", "MAIN");
  _formule.add(profile.get("CALCOLI", "MAIN"));
  _imponibile = profile.get("IMPONIBILE", "MAIN");
  if (_imponibile.not_empty() && _formule.find(_imponibile) < 0)
  {
    error_box("Campo imponibile (%s) sconosciuto nel tipo riga %s", (const char *) _imponibile, (const char *) codice());
    _imponibile.cut(0);
  }            
}

TFormula_documento * TTipo_riga_documento::succ_formula(bool restart)
{                                 
  if (restart)
    _formule.restart();
  const TString16 formula(_formule.get());
  if (formula.not_empty())
  {
    TFormula_documento * o = (TFormula_documento*)_formule_riga.objptr(formula);
    if (o == NULL)
    {
      o = new TFormula_documento(_riga, formula);
      _formule_riga.add(formula, o);
    }
    return o;
  }
  else
    return NULL;
}  

TVariable_mask * TTipo_riga_documento::mask()
{ 
  if (mask_loaded())
    return _mask;
  _mask = new TVariable_mask(mask_name());
  TFilename proname(profile_name());
  
  proname.ext( "ini" );  
     
  TConfig pro( proname );
  int numhandler = pro.get_int( "NHANDLER", "HANDLERS" );
  
  for( int i = 1; i <= numhandler; i ++ )
  {
    TString chiave;
    chiave.format( "%d", i );
    TToken_string riga = pro.get( chiave, "HANDLERS" );
    row_set_handler( *_mask, riga.get_int( 0 ), riga.get_int( 1 ) );
  }
                  
  const int pos = _mask->id2pos(FR_CODART);
  
  if (pos >= 0)
  {
    const TMask_field & f = _mask->field(FR_CODART);
    if (f.is_edit())
    {
      TBrowse * browse = ((TEdit_field &) f).browse();
      const char tipo_r = tipo();
        
      if (browse )
      {                 
        const TCursor * cur = browse->cursor();
        if (cur)
        {
          const int num = cur->file().num();
          if (num == LF_ANAMAG)
          {
            _mask->set_handler( FR_CODART, codart_handler );
            _mask->set_handler( FR_UMQTA, umart_handler );
            _mask->set_handler( FR_DESCR, descr_handler ); 
            _mask->set_handler( FR_QTA, qta_handler ); 
          }
          else
            if (tipo_r == RIGA_SPESEDOC || tipo_r == RIGA_PRESTAZIONI)
              _mask->set_handler( FR_CODART, sppr_handler ); 
        }
      }
    }
  }
  const int posiva = _mask->id2pos(FR_CODIVA);
  
  if (posiva >= 0)
    _mask->set_handler( FR_CODIVA, iva_handler );
    
  return _mask;
}

///////////////////////////////////////////////////////////
// Riga documento per vendite
///////////////////////////////////////////////////////////

TAssoc_array TRiga_documento::_tipi;
TAssoc_array TRiga_documento::_spese;
TAssoc_array TRiga_documento::_ive;

TRiga_documento::TRiga_documento(TDocumento* doc, const char * tipo)
               : TAuto_variable_rectype(LF_RIGHEDOC), _doc(doc), _iva_calc(FALSE)
{
  if (tipo)
    set_tipo(tipo);
}

TRiga_documento::TRiga_documento(const TRiga_documento& rec, TDocumento* doc,
                                 const char * tipo)
               : TAuto_variable_rectype(rec), _doc(doc), _iva_calc(FALSE)
{
  if (tipo)
    set_tipo(tipo);
}
                  
const TTipo_riga_documento& TRiga_documento::tipo() const
{
  const TString16 tiporig(get("TIPORIGA"));
  CHECK(tiporig.not_empty(), "Tipo riga documento nullo");
  TTipo_riga_documento* o = (TTipo_riga_documento*)_tipi.objptr(tiporig);
  if (o == NULL)
  {
    if (_tipi.items() == 0)
    {
      TTable tri("%TRI");  // Tabella dei tipi riga
      for (tri.first(); !tri.eof(); tri.next())
      {
        const TString16 codice = tri.get("CODTAB");
        _tipi.add(codice, new TTipo_riga_documento(tri.curr()));
      }  
    }  
    o = (TTipo_riga_documento*)_tipi.objptr(tiporig);
    if (o == NULL)
    {
      o = new TTipo_riga_documento(tiporig);
      _tipi.add(tiporig, o);
    }
  }
  return *o;
}  

const TSpesa_prest & TRiga_documento::spesa() const
{                           
  const char tipor = tipo().tipo();
  
  CHECK(tipor == RIGA_SPESEDOC || tipor == RIGA_PRESTAZIONI, "Tipo riga incompatibile con le spese");
  static long firm = -1;
  long new_firm = main_app().get_firm();
          
  if (firm != new_firm)
  {
    _spese.destroy();
    firm = new_firm;
  }                      
          
  const TString16 codice(get("CODART"));
  TString16 index; index << tipor << codice;
    
  TSpesa_prest * s = (TSpesa_prest *) _spese.objptr(index);
  if (s == NULL)
  {
    s = new TSpesa_prest(codice, tipor);
    _spese.add(index, s);
  }                
  return *s;
}                      

const TIVA & TRiga_documento::iva(const char *codice)
{
  TIVA * v = (TIVA *) _ive.objptr(codice);
  if (v == NULL)
  {
    v = new TIVA(codice);
    _ive.add(codice, v);
  }                
  return *v;
}                      

bool TRiga_documento::sola_descrizione() const
{       
  char t = tipo().tipo();
  if (t <= ' ' && get("QTA").empty() && get("PREZZO").empty())
    t = RIGA_DESCRIZIONI;
  return t == RIGA_DESCRIZIONI;
}

void TRiga_documento::forza_sola_descrizione() 
{     
  // In realta' il test serve anche a caricare la lista dei tipi riga!
  if (!tipo_valido() || tipo().tipo() != RIGA_DESCRIZIONI)
  {
    _tipi.restart();
    for (const TObject* o = _tipi.get(); o; o = _tipi.get())
    {
      const TTipo_riga_documento* trd = (const TTipo_riga_documento*)o;
      if (trd->tipo() == RIGA_DESCRIZIONI)
      {       
        put("TIPORIGA", trd->codice());
        break;
      }
    }
    zero("QTA");
    zero("PREZZO");
  }
}

TRectype & TRiga_documento::operator =(const TRectype & r) 
{
  TRectype::operator=(r); 
  reset_fields(*this);
  set_fields(*this);
  return *this;
}
                                   
TRectype & TRiga_documento::operator =(const char * r) 
{
  TRectype::operator=(r); 
  reset_fields(*this);
  set_fields(*this);
  return *this;
}
                                   
// Ritorna TRUE se le due righe del documento possono essere sommate
bool TRiga_documento::raggruppabile(const TRiga_documento& r, TToken_string& campi) const
{                                    
  bool ok = TRUE;
  TString campo;
  for (const char* c = campi.get(0); c && ok; c = campi.get())
  {
    campo = get(c);            // Separare le due get!
    ok &= campo == r.get(c);
  }  
  return ok;  
}

TRiga_documento& TRiga_documento::operator +=(const TRiga_documento& r)
{
  TToken_string campi("QTA|NCOLLI|QTAEVASA");
  for (const char* c = campi.get(0); c && ok; c = campi.get())
  {
    real num(r.get_real(c));
    if (!num.is_zero())
    {
      num += get_real(c);
      put(c, num);
    }  
  }   
  if (!get_bool("RIGAEVASA"))
  {                                              
    const real qta = get_real("QTA");
    const real qtaeva = get_real("QTAEVASA");
    if (qtaeva >= qta)
      put("RIGAEVASA", "X");
  }
  
  return *this;
}

void TRiga_documento::set_fields(TAuto_variable_rectype & rec)
{ 
  if (get("TIPORIGA").not_empty())
  {                                                                 
    TTipo_riga_documento & tipo_riga = (TTipo_riga_documento &) tipo();
    
    for (const TFormula_documento * f = tipo_riga.first_formula(); f; f = tipo_riga.succ_formula())
    {        
      TExpr_documento * exp = f->expr();
      add_field(new TDocumento_variable_field(f->name(), exp));
      if (exp)
      {
        exp->set_doc(_doc);
        exp->set_row(this);
      }
    }
  }
}

real TRiga_documento::prezzo(bool scontato, bool lordo, int ndec) const
{   
  real prezzo = get_real("PREZZO");
  if (scontato)
    prezzo = prezzo_scontato(prezzo, get("SCONTO"));
  prezzo.round(ndec);
  if (lordo)
    prezzo = netto2lordo(prezzo, get("CODIVA"), ndec);
  prezzo.round(ndec);
  return prezzo;
}
                               
real TRiga_documento::importo(bool scontato, bool lordo, int ndec) const
{
  real importo;
  
  TTipo_calcolo c = _nessun_calcolo;
  const char tipor = tipo().tipo();
  const real qta = get_real("QTA");         
  TString16 field_perc;
  TCond_vendita cv;      
    
  switch (tipor)
  {
    case RIGA_MERCE:
      c = _qtaprezzo;
      break;
    case RIGA_PRESTAZIONI:
    case RIGA_SPESEDOC:
      {   
        const TSpesa_prest & s = spesa(); 
                             
        switch (s.tipo())
        {
          case 'Q':
            c = _qtaprezzo;
            break;
          case 'V':
            c = _valore;
            break;
          case 'P':
            c = _percentuale;
            field_perc = s.field_perc();
            break;
          default:
            break;  
        }
      }
      break;
    case RIGA_SCONTI: 
      cv.set_sconto(get("SCONTO"));
          
      if (cv.get_sconto().not_empty())
        c = _scontoperc;
      else
        c = _scontoimp;
      break;
    case RIGA_OMAGGI: 
      if (_iva_calc && get_bool("ADDIVA"))
        c = _qtaprezzo;
    default:
      break;
  }
  switch (c)     
  {
    case _qtaprezzo:
      importo = prezzo(scontato, lordo, ndec) * qta;
      break;
    case _valore:
      importo = prezzo(scontato, lordo, ndec);
      break;
    case _percentuale: 
        importo = doc().get_real(field_perc) * get_real("PSPESA") / 100;
      break;
    case _scontoimp:
      importo = -prezzo(FALSE, lordo, ndec);
      break;
    case _scontoperc:     
      importo = doc().basesconto() * (cv.sconto_val() - 1.0);
     break;                  
    default:
      break;
  }
  importo.round(ndec);
    
  return importo;
}       
  
real TRiga_documento::iva(int ndec) const
{                  
  ((TRiga_documento *) this)->_iva_calc = TRUE;
  real iva(::iva(imponibile(), iva(), ndec));
  ((TRiga_documento *) this)->_iva_calc = FALSE;
  return iva;
}

real TRiga_documento::imponibile() const 
{
  const TString16 field(tipo().imponibile());
  
  if (field.not_empty())
    return get_real(field);
  else
    return importo(TRUE, FALSE, doc().in_valuta() ? 3 : 0);
}

real TRiga_documento::imposta(bool round) const
{ 
  return iva(round ? (doc().in_valuta() ? 3 : 0) : 20);
}

void TRiga_documento::dirty_fields(bool dirty_document)
{
  for (TDocumento_variable_field * f = (TDocumento_variable_field *) first_variable_field(); 
       f != NULL; f = (TDocumento_variable_field *) succ_variable_field()) 
    f->set_dirty();
  if (dirty_document) 
    ((TDocumento &)doc()).dirty_fields();  
}

bool TRiga_documento::doc_dependent() const 

{                     
  if (tipo_valido())
  {
    const char tipor = tipo().tipo();
               
    if (tipor == RIGA_SPESEDOC)
      return spesa().tipo() == 'P';
    else
      if (tipor == RIGA_SCONTI)
        return get("SCONTO").not_empty();
  }                 
  return FALSE;
}

void TRiga_documento::put_str(const char* fieldname, const char* val)
{          
  TString v(val);
  if (strcmp(fieldname, "TIPORIGA") == 0 && TRectype::get("TIPORIGA") != v)
  {
    TAuto_variable_rectype::put_str(fieldname, v);
    reset_fields(*this);
    set_fields(*this);
  }
  else
  {
    TAuto_variable_rectype::put_str(fieldname, v);
    dirty_fields();
  }
}

void TRiga_documento::zero(const char * fieldname)
{
  if (strcmp(fieldname, "TIPORIGA") == 0)
    reset_fields(*this);
  TAuto_variable_rectype::zero(fieldname);
  dirty_fields();
}

void TRiga_documento::zero(char c)
{
  reset_fields(*this);
  TAuto_variable_rectype::zero(c);
}

void TRiga_documento::autosave(TSheet_field & f)
{
  const int num = numero() - 1;
  
  if (num >= 0 && num < f.items())
  { 
    TToken_string & row = f.row(num);
    
    put( "STATORIGA", row.get( f.cid2index(FR_STATORIGA) ) );
    put( "TIPORIGA", row.get( f.cid2index(FR_TIPORIGA )) );
    TString16 codmag(row.get(f.cid2index(FR_CODMAG)));
      
    codmag.left_just(3);
    codmag << row.get( f.cid2index(FR_CODDEP ));
    put( "CODMAG",  codmag);
    put( "CODART", row.get( f.cid2index(FR_CODART )) );
    TString s(row.get(f.cid2index(FR_DESCR))); 
    int split_pos = s.find('\n');
                                         
    const int descr_len = length("DESCR");
    if (split_pos < 0 && s.len() > descr_len)
      split_pos = descr_len;
    if (split_pos > 0)
    {
      put( "DESCR", s.left(split_pos));
      put("DESCLUNGA", "X");  
      put("DESCEST", s.mid(split_pos));
    }
    else
    {
      put("DESCR", s);
      put("DESCLUNGA", "");
      zero("DESCEST");
    }
    
    put( "PREZZO", row.get( f.cid2index(FR_PREZZO )) );
    put( "UMQTA", row.get( f.cid2index(FR_UMQTA )) );
    TMask * m = ((TTipo_riga_documento &)tipo()).mask();
    const int pos = m->id2pos(FR_QTA);
    if (pos >= 0 && m->fld(pos).field()->name() == "PSPESA")
      put( "PSPESA", row.get( f.cid2index(FR_QTA )) );
    else
      put( "QTA", row.get( f.cid2index(FR_QTA )) );
    put( "QTAEVASA", row.get( f.cid2index(FR_QTAEVASA )) );
    put( "RIGAEVASA", row.get( f.cid2index(FR_RIGAEVASA )) );
    put( "TARA", row.get( f.cid2index(FR_TARA )) );
    put( "PNETTO", row.get( f.cid2index(FR_PNETTO )) );
    put( "NCOLLI", row.get( f.cid2index(FR_NCOLLI )) );
    put( "DAEVADERE", row.get( f.cid2index(FR_DAEVADERE )) );  
    put( "SCONTO", row.get( f.cid2index(FR_SCONTO )) );
    put( "PERCPROV", row.get( f.cid2index(FR_PERCPROV )) );
    put( "IMPFISSO", row.get( f.cid2index(FR_IMPFISSO )) );
    put( "CODIVA", row.get( f.cid2index(FR_CODIVA )) );
    put( "ADDIVA", row.get( f.cid2index(FR_ADDIVA )) );
    put( "ASPBENI", row.get( f.cid2index(FR_ASPBENI )) );  
  }
}
    
void TRiga_documento::autoload(TSheet_field & f)
{ 
  const int num = numero() - 1;
  
  TToken_string & row = f.row(num);
    
  row.add( get( "STATORIGA" ), f.cid2index(FR_STATORIGA ));
  row.add( get( "TIPORIGA" ), f.cid2index(FR_TIPORIGA ));
  const TString codmag(get("CODMAG"));
  row.add( codmag.left(3), f.cid2index(FR_CODMAG ));
  row.add( codmag.mid(3), f.cid2index(FR_CODDEP ));
  row.add( get( "CODART" ), f.cid2index(FR_CODART ));
  TString s(get("DESCR"));
  if (get_bool("DESCLUNGA"))
    s << get("DESCEST");
  row.add(s, f.cid2index(FR_DESCR ));
  row.add( get( "UMQTA" ), f.cid2index(FR_UMQTA ));
  row.add( get( "PREZZO" ), f.cid2index(FR_PREZZO ));
  TMask * m = ((TTipo_riga_documento &)tipo()).mask();
  const int pos = m->id2pos(FR_QTA);
  if (pos >= 0 && m->fld(pos).field()->name() == "PSPESA")
    row.add( get( "PSPESA" ), f.cid2index(FR_QTA ));
  else
    row.add( get( "QTA" ), f.cid2index(FR_QTA ));
  row.add( get( "QTAEVASA" ), f.cid2index(FR_QTAEVASA ));
  row.add( get( "RIGAEVASA" ), f.cid2index(FR_RIGAEVASA ));
  row.add( get( "TARA" ), f.cid2index(FR_TARA ));
  row.add( get( "PNETTO" ), f.cid2index(FR_PNETTO ));
  row.add( get( "NCOLLI" ), f.cid2index(FR_NCOLLI ));
  row.add( get( "DAEVADERE" ), f.cid2index(FR_DAEVADERE ));
  row.add( get( "SCONTO" ), f.cid2index(FR_SCONTO ));
  row.add( get( "PERCPROV" ), f.cid2index(FR_PERCPROV ));
  row.add( get( "IMPFISSO" ), f.cid2index(FR_IMPFISSO ));
  row.add( get( "CODIVA" ), f.cid2index(FR_CODIVA ));
  row.add( get( "ADDIVA" ), f.cid2index(FR_ADDIVA ));
  row.add( get( "ASPBENI" ), f.cid2index(FR_ASPBENI ));
}

TDocumento_mask::TDocumento_mask(const char* name, TDocumento * doc, int num,
                                 int max)
               : TVariable_mask(name, num, max), _progs_page(-1), _last_prog(-1),
                 _doc(doc)
{                                                              
  const int pos = id2pos(BASE_PIEDE + 1);
  
  if (pos >= 0)
  {
    _progs_page = find_parent_page(fld(pos));
    _last_prog = 0;
    while (id2pos(BASE_PIEDE + _last_prog + 1) >= 0)
      _last_prog++;
  }
}

void TDocumento_mask::next_page(int p)
{     
  TMask::next_page(p);
  if (curr_page() == _progs_page)
  {                    
    begin_wait();
    autosave(get_relation());
    for (int i = _last_prog; i > 0; i--)
    {
      const short id = BASE_PIEDE + i;
      const TString16 name(field(id).field()->name());
      
      set(id , doc().get(name)); 
    }
    end_wait();
  }
}

bool TDocumento_mask::on_key(KEY key)
{ 
  if (key == K_SHIFT + K_F12)
  {
    field(F_STATO).enable(!field(F_STATO).enabled());
    return TRUE;
  }
  return TVariable_mask::on_key(key); 
}

///////////////////////////////////////////////////////////
// Documento per vendite
///////////////////////////////////////////////////////////
 
TAssoc_array TDocumento::_tipi;

TDocumento::TDocumento()
          : TAuto_variable_rectype(LF_DOC), _rows(LF_RIGHEDOC, "NRIGA"), _nuovo(TRUE),
            _condv(NULL), _rel(NULL), _sconto(NULL), _esenzione(NULL)
{                      
  set_memo_fld("G1");
}

TDocumento::TDocumento(char provv, int anno, const char* codnum, long numdoc,
                       TCond_vendita * condv, TRelation * rel)
          : TAuto_variable_rectype(LF_DOC), _rows(LF_RIGHEDOC, "NRIGA"), _nuovo(TRUE),
            _condv(condv), _rel(rel), _sconto(NULL), _esenzione(NULL)
{
  set_memo_fld("G1");
  if (numdoc <= 0)
  { 
    numdoc = 0;
    set_key(*this, provv, anno, codnum, numdoc);
    
    TRiga_documento* key = new TRiga_documento(this);
    set_key(*key, provv, anno, codnum, numdoc);
    _rows.set_key(key);  // ok
  }
  else
    read(provv, anno, codnum, numdoc);
}

TDocumento::TDocumento(const TRectype& rec, TCond_vendita * condv, TRelation * rel)
          : TAuto_variable_rectype(LF_DOC), _rows(LF_RIGHEDOC, "NRIGA"), _nuovo(FALSE),
            _condv(condv), _rel(rel), _sconto(NULL), _esenzione(NULL)
{
  set_memo_fld("G1");
  read(rec);
}

TDocumento::~TDocumento()

{
  if (_sconto != NULL) delete _sconto;
  if (_esenzione != NULL) delete _esenzione;
}

// Funzione statica utile a tutti gli utenti di LF_DOC e LF_RIGHEDOC
void TDocumento::set_key(TRectype& rec, char provv, int anno, const char* codnum, long numdoc)
{
  CHECK(provv == 'D' || provv == 'P', "Provvisorio o Definitivo?");
  CHECKD(anno > 1900, "Anno non valido: ", anno);
  CHECK(codnum && *codnum, "Codice numerazione nullo");
  CHECKD(numdoc >= 0, "Numero documento non valido ", numdoc);

  rec.put("PROVV",  provv); 
  rec.put("ANNO",   anno);
  rec.put("CODNUM", codnum);
  rec.put("NDOC",   numdoc);
}

// Funzione statica utile a tutti gli utenti di LF_DOC e LF_RIGHEDOC
void TDocumento::copy_data(TRectype& dst, const TRectype& src)
{                           
  // Memorizza tutti i campi chiave
  const char provv       = dst.get_char("PROVV");
  const int anno         = dst.get_int("ANNO");  
  const TString16 codnum = dst.get("CODNUM");  
  const long numdoc      = dst.get_long("NDOC");  
  const int nriga        = dst.num() == LF_RIGHEDOC ? dst.get_int("NRIGA") : 0;
  // Copia tutto il record
  dst = src;              
  // Ripristina tutti i campi chiave
  set_key(dst, provv, anno, codnum, numdoc);
  if (nriga > 0)
    dst.put("NRIGA", nriga);
}

TRiga_documento& TDocumento::insert_row(int row, const char *tipo)
{       
  TRiga_documento * r = new TRiga_documento((const TRiga_documento &) _rows.key(), this); // ok
  r->set_numero(row);     
  if (tipo)
   r->set_tipo(tipo);
  _rows.insert_row(r); // ok
  return *r;
}

TRiga_documento& TDocumento::new_row(const char *tipo)
{
  TRiga_documento & r = (TRiga_documento&)_rows.row(-1, TRUE); // ok
  if (tipo)
   r.set_tipo(tipo);
  return r;
}

int TDocumento::read(const TRectype& rec)
{                           
  head() = rec;  
  TRiga_documento* key = new TRiga_documento(this); 
  const char pr = tipo_numerazione();
  const int an = anno();
  const TString16 cn = numerazione();
  const long nu = numero();
  CHECK(nu > 0, "Numero documento nullo.");
  set_key(*key, pr, an, cn, nu);
  
  TLocalisamfile doc(LF_DOC);        
  int err = TRectype::read(doc);
  if (err == NOERR)
  {
    _nuovo = FALSE;
    _rows.read(key); //ok
  }  
  else
  {
    _nuovo = TRUE;
    head() = rec;
    destroy_rows();
    _rows.set_key(key);  // ok
  }
  set_riga_sconto();
  return err;
}

int TDocumento::read(char provv, int anno, const char* codnum, long numdoc)
{                           
  TRectype rec(LF_DOC);
  CHECK(numdoc > 0, "Numero documento nullo.");
  set_key(rec, provv, anno, codnum, numdoc);
  return read(rec);
}

long TDocumento::renum(long numdoc)
{
  if (numdoc <= 0)
  {
    const char      tn = tipo_numerazione();
    const int       an = anno();
    const TString16 nu = numerazione();
    numdoc  = get_next_key(tn, an, nu);
  }   
  char num[16]; sprintf(num, "%ld", numdoc);
  renum_key("NDOC", num);       // Aggiorna testata
  _rows.renum_key("NDOC", num); // Aggiorna righe  ok
  return numdoc;
}

void TDocumento::set_riga_sconto()
{                                 
  const TString80 sconto(get("SCONTOPERC"));  
  
  if (sconto.empty())
  {        
    if(_sconto != NULL)
      delete _sconto;
    _sconto = NULL;
  }
  else
  {                
    if (_sconto == NULL)
    {                   
      static TString _tipo_riga;
      static long firm = -1;
      long new_firm = main_app().get_firm();
          
      if (firm != new_firm)
      {
        TConfig conf(CONFIG_DITTA);
          
        _tipo_riga = conf.get("SCOTRIGA", "ve");
        firm = new_firm;
      }
      _sconto = new TRiga_documento(this, _tipo_riga); 
      _sconto->put("DESCR","Sconto");
    }
    _sconto->put("SCONTO", sconto);
  }
}

void TDocumento::set_riga_esenzione()
{
}
                                 
void TDocumento::dirty_fields()
{
  for (TDocumento_variable_field * f = (TDocumento_variable_field *) first_variable_field(); 
       f != NULL; f = (TDocumento_variable_field *) succ_variable_field()) 
    f->set_dirty();
  for (int i = rows(); i > 0; i--)
  {
    TRiga_documento & r = (TRiga_documento &) row(i);
        
    if (r.doc_dependent())
      r.dirty_fields(FALSE);
  }  
}

int TDocumento::write(bool re) const
{              
  const bool nuovo = _nuovo || numero() <= 0;  // E' nuovo di zecca!
  if (nuovo && re)                             // quindi ...
    re = FALSE;                                // ... non fare la rewrite

  TLocalisamfile doc(LF_DOC);
  int err = NOERR;
  if (re)
  {
    err = _rows.write(re); 
    if (err == NOERR)
    {
      err = TRectype::rewrite(doc);
      if (err != NOERR)
        err = TRectype::write(doc);
    }    
  }
  else
  {             
    if (nuovo)
    {         
      TDocumento& myself = *(TDocumento*)this;
      if (numero() <= 0)
        myself.renum();
      do
      {
        err = TRectype::write(doc);
        if (err == _isreinsert)
          myself.renum();
      } while (err == _isreinsert);
      myself._nuovo = FALSE;
    }
    else 
    {
      err = TRectype::write(doc);
      if (err != NOERR)
        err = TRectype::rewrite(doc);
    }    
    
    if (err == NOERR)
      err = _rows.write(re); 
  }
  return err;  
}

int TDocumento::remove() const
{
  TLocalisamfile doc(LF_DOC);
  int err = _rows.remove();
  if (err == NOERR)
    err = TRectype::remove(doc);
  return err;  
}

const bool TDocumento::in_valuta() const
{
  const TString& val = valuta();
  return (val.not_empty() && val != "LIT");
}

TRiga_documento & TDocumento::row(int index)
{     
  const int nrows = _rows.rows();
  TRiga_documento * r = NULL;
  
  if (index <= nrows)
    r = &((TRiga_documento &) _rows.row(index, FALSE));
  else
  {                                           
    CHECKD((index == nrows + 1 && (_sconto != NULL || _esenzione != NULL)) ||    (index == nrows + 2 && _sconto != NULL && _esenzione != NULL),
           "Riga documento non esistente ", index);
    if (index == nrows + 1)
    {
      r = _sconto != NULL ? _sconto : _esenzione;
    }
    if (index == nrows + 2)
      r = _esenzione;
  }       
  return *r;
}

long TDocumento::get_next_key(char provv, int anno, const char* codnum) const
{               
  static long n = 0;
  
  if (n == 0)
  {
    TLocalisamfile doc(LF_DOC);
    TRectype& curr = doc.curr();
    set_key(curr, provv, anno, codnum, 9999999L);
  
    const int err = doc.read(_isgreat);
                
    if (err != _isemptyfile)
    {
      if (err == NOERR)
        doc.prev();
      if (curr.get_char("PROVV") == provv && 
          curr.get_int("ANNO") == anno && 
          curr.get("CODNUM") == codnum)
         n = curr.get_long("NDOC");
    }     
  }      
  
  n++;
  return n;   
}

const TTipo_documento& TDocumento::tipo() const
{
  const TString16 tipodoc(get("TIPODOC"));
  CHECK(tipodoc.not_empty(), "Tipo documento nullo");
  TTipo_documento * o = (TTipo_documento*)_tipi.objptr(tipodoc);
  if (o == NULL)
  {
    o = new TTipo_documento(tipodoc);
    _tipi.add(tipodoc, o);
  }
  return *o;
}  

bool TDocumento::raggruppabile(const TDocumento& doc, TToken_string& campi) const
{                
  bool ok = raggruppabile() && doc.raggruppabile();
  if (ok)
  {
    TString campo;
    for (const char* c = campi.get(0); c && ok; c = campi.get())
    {
      campo = get(c);
      ok &= campo == doc.get(c);
    }
  }  
  return ok;
} 

void TDocumento::set_fields(TAuto_variable_rectype & rec)
{                                                        
  if (get("TIPODOC").not_empty())
  {          
    TTipo_documento & tipo_doc = (TTipo_documento &) tipo(); 
    const TString16 tot_doc(tipo_doc.totale_doc());
    
    for (const TFormula_documento * f = tipo_doc.first_formula(); f; f = tipo_doc.succ_formula())
    {     
      TExpr_documento * exp = f->expr(); 
      if (tot_doc == f->name())
      {   
        TString work_tot_doc(tot_doc);
        
        work_tot_doc.insert("_");                          
        TString netto_def(exp->string());              
                                
        if (netto_def.find("IMPONIBILI") == -1)
        {
          if (netto_def.not_empty())
            netto_def << "+";
          netto_def << "IMPONIBILI()";
        }

        if (netto_def.find("IMPOSTE") == -1)
        {
          if (netto_def.not_empty())
            netto_def << "+";
          netto_def << "IMPOSTE()";
        }

        TExpr_documento * netto_exp = new TExpr_documento(netto_def, _numexpr, this);
                                           
        add_field(new TDocumento_variable_field(work_tot_doc, netto_exp));
        
        TExpr_documento * tot_exp = new TExpr_documento(
                                    format("%s + _BOLLI(%s)", (const char *) work_tot_doc,
                                           (const char *) work_tot_doc), _numexpr, this);

        add_field(new TDocumento_variable_field(f->name(), tot_exp));
      }
      else
        add_field(new TDocumento_variable_field(f->name(), exp));
      if (exp)
        exp->set_doc(this);
    }                      
  }
}

real TDocumento::imponibile(bool spese, int ndec) const
{
  real val;                      
  if (ndec > 99)
    ndec = in_valuta() ? 3 : 0;
    
  for (int i = rows(); i > 0; i--)
    val += ((TRiga_documento &) ((TDocumento *)this)->row(i)).imponibile();
  if (spese)
  {
    real tot_doc = val + imposta(FALSE, ndec);
    val += spese_incasso(tot_doc, ndec, _netto);
    tot_doc += spese_incasso(tot_doc, ndec);
    val += bolli(tot_doc, ndec, _netto);
  }
  val.round(ndec);
  return val;
}

real TDocumento::imposta(bool spese, int ndec) const
{ 
  TAssoc_array ive;

  if (ndec > 99)
    ndec = in_valuta() ? 3 : 0;

  for (int i = rows(); i > 0; i--)
  {      
    TRiga_documento & r = ((TRiga_documento &) ((TDocumento *)this)->row(i));
    real iva(r.imposta(FALSE));    
    const TString & cod_iva = r.get("CODIVA");
      
    real * tot = (real *) ive.objptr(cod_iva);
    if (tot == NULL)
      ive.add(cod_iva, iva);
    else 
      *tot += iva;
  }
  real val;

  ive.restart();
  for (real * iva = (real *) ive.get(); iva != NULL; iva = (real *) ive.get())
  {
    if (*iva < ZERO)
      iva->floor(ndec);
    else               
      iva->ceil(ndec);
    val += *iva;
  }
  if (spese)
  {
    real tot_doc = val + imponibile(FALSE, ndec);
    val += spese_incasso(tot_doc, ndec, _imposta);
    tot_doc += spese_incasso(tot_doc, ndec);
    val += bolli(tot_doc, ndec, _imposta);
  }
  val.round(ndec);
  return val;
}

real TDocumento::totale_doc() const
{
  const TString16 field(tipo().totale_doc());

  if (field.not_empty())
    return get_real(field);
  else
  {
    real r = imponibile() + imposta();
    r += spese_incasso(r, in_valuta() ? 3 : 0);
    r += bolli(r, in_valuta() ? 3 : 0);
    return r;
  }
}

real TDocumento::totale_netto() const
{
  const TString16 field(tipo().totale_netto());

  if (field.not_empty())
    return get_real(field);
  else
    return imponibile() + imposta();
}

real TDocumento::basesconto() const
{
  const TString16 field(tipo().basesconto());

  if (field.not_empty())
    return get_real(field);
  else
    return ZERO;
}

real TDocumento::spese() const
{
  const TString16 field(tipo().spese());

  if (field.not_empty())
    return get_real(field);
  else
    return ZERO;
}

TPagamento & TDocumento::pagamento()
{ 
  const TString16 codpag(get("CODPAG"));
  
  if (codpag != _pag.code())
  {                        
    _pag.set_code(codpag);
    _pag.read();
  }
  return _pag;                                                        
}                                   

void TDocumento::put_str(const char* fieldname, const char* val)
{          
  TString v(val);
  if (strcmp(fieldname, "TIPODOC") == 0 && TRectype::get("TIPODOC") != v)
  {
    TAuto_variable_rectype::put_str(fieldname, v);
    reset_fields(*this);
    set_fields(*this);
  }
  else          
  {
    TAuto_variable_rectype::put_str(fieldname, v);
    dirty_fields();                                
    if (strcmp(fieldname, "SCONTOPERC") == 0) 
      set_riga_sconto();
  }
}

void TDocumento::zero(const char * fieldname)
{
  if (strcmp(fieldname, "TIPODOC") == 0)
    reset_fields(*this);
  TAuto_variable_rectype::zero(fieldname);
  dirty_fields();
}

void TDocumento::zero(char c)
{
  reset_fields(*this);
  TAuto_variable_rectype::zero(c);
}

TRectype & TDocumento::operator =(const TRectype & r) 
{
  TRectype::operator=(r); 
  reset_fields(*this);
  set_fields(*this);
  return *this;
}
                                   
TRectype & TDocumento::operator =(const char * r) 
{
  TRectype::operator=(r); 
  reset_fields(*this);
  set_fields(*this);
  return *this;
}

void TDocumento::update_spese_aut(TString_array & spese_aut, bool preserve_old, TSheet_field * sh)

{
  const bool interactive = sh != NULL;       
  if (tipo().spese_aut())
  {                
    const int nrows = _rows.rows();
    for (int i = nrows; i > 0; i--)
    {
      TRiga_documento & r = row(i);
    
      if (r.tipo().tipo() == RIGA_SPESEDOC && r.generata())
      { 
        if (preserve_old)
          return;
        destroy_row(i, TRUE); 
        if (interactive)
          sh->destroy(i - 1);
      }
    }
    TString16 cod_iva_cli;
    const int nspese = spese_aut.items();
    if (nspese > 0)
    {
      TLocalisamfile cfven(LF_CFVEN);
      cfven.put("TIPOCF", get("TIPOCF"));
      cfven.put("CODCF", get("CODCF"));
      if (cfven.read() == NOERR)
        cod_iva_cli = cfven.get("ASSFIS");
      for (i = 0; i < nspese; i++)
      {                     
        const TString & s = spese_aut.row(i);
        
        TSpesa_prest sp(s);
        TString16 tipo(sp.tipo_riga());
        TRiga_documento & riga = new_row(tipo);
    
        riga.put("CODART", s);
        riga.set_generata();
        riga.put("DESCR", sp.descrizione());
        switch (sp.tipo())
        {                 
          case 'Q':
            {                              
              real qta = sp.qta();
                                                    
              if (qta == ZERO)
                qta = 1.0;                                       
              riga.put("QTA", qta);                 
            }
          case 'V':
            {
              const real cambio = get_real("CAMBIO");
              const TString16 valuta = get("CODVAL");
              real prezzo = sp.prezzo();
        
              sppr_calc(sp, valuta, cambio, prezzo);
              riga.put("PREZZO", prezzo);
              riga.put("UMQTA", sp.um());
            }
            break;
          case 'P':
          default:
            break;
        }
        if (cod_iva_cli.empty())
          riga.put("CODIVA", sp.cod_iva());
        else
          riga.put("CODIVA", cod_iva_cli);
        if (interactive)
        {              
          const int nrow = sh->insert(-1, FALSE);
          riga.autoload(*sh);        
          sh->check_row(nrow);
//          sh->post_insert(nrow);    
        }
      }         
    }
  }
}
                                   
///////////////////////////////////////////////////////////
// Lista di documenti
///////////////////////////////////////////////////////////

TDate TLista_documenti::num2date(char provv, int anno, const char* codnum, long num) const
{
  TLocalisamfile doc(LF_DOC);
  CHECK(num > 0, "Numero documento nullo.");
  TDocumento::set_key(doc.curr(), provv, anno, codnum, num);
  
  if (doc.read(_isgteq) != NOERR)     // In caso d'errore ...
    doc.last();                       // prendi l'ultimo
  
  return doc.get("DATADOC");
}

int TLista_documenti::read(char provv, char tipocf, long clifo, int anno, 
                           TToken_string& tipidoc, TToken_string& statidoc,
                           const TDate& dd, const TDate& ad,
                           const char* codnum, long dn, long an)
{       
  CHECK(provv == 'D' || provv == 'P', "Provvisorio o Definitivo?");
  CHECK(tipocf == 'C' || tipocf == 'F', "Il tipo deve essere Cliente o Fornitore");
  CHECKD(clifo > 0L, "Codice cliente non valido", clifo);
  CHECKD(anno > 1900, "Anno non valido: ", anno);
  CHECK(!tipidoc.empty_items(), "Lista dei tipi documento vuota");
  CHECK(!statidoc.empty_items(), "Lista degli stati documento vuota");
  
  TRelation doc(LF_DOC);
  TRectype start(LF_DOC), stop(LF_DOC);

  start.put("TIPOCF", tipocf);
  stop.put("TIPOCF", tipocf);
  
  start.put("CODCF", clifo);
  stop.put("CODCF", clifo);
  
  start.put("PROVV", provv);
  stop.put("PROVV", provv);

  start.put("ANNO", anno);
  stop.put("ANNO", anno);
  
  if (dn > 0)  
  {
    start.put("DATADOC", num2date(provv, anno, codnum, dn));
    start.put("NDOC", dn);
  }
  else
  {
    if (dd.ok() && dd > botime)
      start.put("DATADOC", dd);
  }
  
  if (an > 0)  
  {
    stop.put("DATADOC", num2date(provv, anno, codnum, an));
    stop.put("NDOC", an);
  }
  else
  {
    if (ad.ok() && ad < eotime)
      stop.put("DATADOC", ad);
  }
  
  TString filter(16);
  if (codnum && *codnum)       
  {
    bool numfilter = FALSE;
    
    if (start.get("DATADOC").empty())
      numfilter = TRUE;
    else  
      start.put("CODNUM", codnum);

    if (stop.get("DATADOC").empty())
      numfilter = TRUE;
    else  
      stop.put("CODNUM", codnum);
      
    if (numfilter)
      filter << "CODNUM=\"" << codnum << '"'; 
  }

  TCursor cur(&doc, filter, 2, &start, &stop);
  const TRectype& head = cur.curr();
  
  _documenti.destroy();
  for (cur = 0; cur.ok(); ++cur)
  {                
    const TString16 tipodoc  = head.get("TIPODOC");
    const TString16 statodoc = head.get("STATO");
    if (tipidoc.get_pos(tipodoc) >= 0 && statidoc.get_pos(statodoc) >= 0)
    {
      TDocumento* d = new TDocumento(head);
      _documenti.add(d);
    }  
  }
  
  return _documenti.items();
}                            

int TLista_documenti::write(bool re) const
{
  int err = NOERR;                                      
  for (int i = 0; i < _documenti.items() && err == NOERR; i++)
    err = doc(i).write(re);
  return err;
}

///////////////////////////////////////////////////////////
// Cliente/Fornitore per vendite
///////////////////////////////////////////////////////////

void TLista_clifo::TClifo::init(const TRectype& rec, const TRectype& ven)
{                              
  _codice = rec.get_long(CLI_CODCF);
  CHECK(_codice > 0, "Codice cliente nullo");

  if (!ven.empty())
  {
    _agente = ven.get_long(CLI_CODAG);
    _zona   = ven.get_long(CLI_CODZONA);
  }
  else
    _agente = _zona = 0;
}

bool TLista_clifo::TClifo::read(char tipo, long cod)
{
  TRelation clifo(LF_CLIFO);
  clifo.add(LF_CFVEN, "TIPOCF=TIPOCF|CODCF=CODCF");
  
  TRectype& curr = clifo.curr();
  curr.put(CLI_TIPOCF, tipo);
  curr.put(CLI_CODCF, cod);
  if (clifo.read() == NOERR)
    init(curr, clifo.curr(LF_CFVEN));
  else
    zero();  
  
  return ok();  
}

TLista_clifo::TClifo::TClifo(const TRectype& rec)
{
  CHECK(rec.num() == LF_CLIFO, "Record non clienti");
  const char tipo = rec.get_char(CLI_TIPOCF);
  const long codice = rec.get_long(CLI_CODCF);
  read(tipo, codice);
}

int TLista_clifo::leggi(long dc, long ac, long da, long aa, long dz, long az)
{
  TRelation clifo(LF_CLIFO);
  clifo.add(LF_CFVEN, "TIPOCF=TIPOCF|CODCF=CODCF");
  
  TRectype start(LF_CLIFO), stop(LF_CLIFO);
  
  start.put(CLI_TIPOCF, tipo()); 
  if (dc > 0) 
    start.put(CLI_CODCF, dc); 

  stop.put(CLI_TIPOCF, tipo()); 
  if (ac > 0) 
    stop.put(CLI_CODCF, ac);
  
  TString filter(32);
  if (da > 0) 
    filter << '(' << LF_CFVEN << "->" << CLI_CODAG << ">=" << da << ')';
  if (aa > 0) 
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODAG << "<=" << aa << ')';
  }
  if (dz > 0) 
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODZONA << ">=" << dz << ')';
  }  
  if (az > 0) 
  {
    if (filter.not_empty()) filter << "&&";
    filter << '(' << LF_CFVEN << "->" << CLI_CODZONA << "<=" << az << ')';
  }  
  
  TCursor cur(&clifo, filter, 1, &start, &stop);
  const TRectype& cli = cur.curr();
  const TRectype& ven = cur.curr(LF_CFVEN);
  for (cur = 0; cur.ok(); ++cur)
  {
    TClifo* c = new TClifo(cli, ven);
    _clifo.add(c);
  }
  
  if (dc > 0 || ac > 0) ordina_per_codice(); else
  if (da > 0 || aa > 0) ordina_per_agente(); else
  if (dz > 0 || az > 0) ordina_per_zona();
  
  return _clifo.items();
}

int TLista_clifo::sort_by_code(const TObject** o1, const TObject** o2)
{
  TLista_clifo::TClifo* c1 = (TLista_clifo::TClifo*)*o1;
  TLista_clifo::TClifo* c2 = (TLista_clifo::TClifo*)*o2;
  const long d = c1->codice() - c2->codice();
  return d == 0L ? 0 : (d > 0 ? +1 : -1);
}

int TLista_clifo::sort_by_agent(const TObject** o1, const TObject** o2)
{
  TLista_clifo::TClifo* c1 = (TLista_clifo::TClifo*)*o1;
  TLista_clifo::TClifo* c2 = (TLista_clifo::TClifo*)*o2;
  const long d = c1->agente() - c2->agente();
  return d == 0L ? 0 : (d > 0 ? +1 : -1);
}

int TLista_clifo::sort_by_zone(const TObject** o1, const TObject** o2)
{
  TLista_clifo::TClifo* c1 = (TLista_clifo::TClifo*)*o1;
  TLista_clifo::TClifo* c2 = (TLista_clifo::TClifo*)*o2;
  const long d = c1->zona() - c2->zona();
  return d == 0L ? 0 : (d > 0 ? +1 : -1);
}

int TLista_clifo::ordina_per_codice()
{
  _clifo.sort(sort_by_code);
  return _clifo.items();
}

int TLista_clifo::ordina_per_agente()
{
  _clifo.sort(sort_by_agent);
  return _clifo.items();
}

int TLista_clifo::ordina_per_zona()
{
  _clifo.sort(sort_by_zone);
  return _clifo.items();
}

int TLista_clifo::find(long cod) const
{
  for (int i = items()-1; i >= 0; i--)
    if (clifo(i).codice() == cod) break;
  return i;  
}

int TLista_clifo::add(long cod)
{
  int pos = find(cod);
  if (pos < 0)
  {
    TClifo* c = new TClifo(tipo(), cod);
    pos = _clifo.add(c);
  }
  return pos;
}

///////////////////////////////////////////////////////////
// TElaborazione
///////////////////////////////////////////////////////////

TElaborazione::TElaborazione(const char* cod) : TRectype(LF_TABCOM)
{               
  settab("ELD");
  if (cod && *cod)
    read(cod);
}

int TElaborazione::read(const char* cod)
{            
  CHECK(cod && *cod, "Codice elaborazione nullo");
  TTable eld("%ELD");
  put("CODTAB", cod);
  const int err = TRectype::read(eld);
  if (err != NOERR)
    yesnofatal_box("Codice elaborazione non valido: %s", cod);
  return err;  
}

///////////////////////////////////////////////////////////
// TFatturazione bolle
///////////////////////////////////////////////////////////

TFatturazione_bolle::TFatturazione_bolle(const char* cod) 
                   : TElaborazione(cod)
{
}

void TFatturazione_bolle::tipi_validi(TToken_string& tipi) const
{
  const TString& s2 = get("S2");
  tipi.cut(0);
  TString16 t;
  for (int i = 0; i < 5; i++)
  {
    t = s2.mid(i*4, 4);
    t.trim();
    if (t.not_empty())
      tipi.add(t);
  }
  CHECK(!tipi.empty_items(), "Nessun tipo documento valido");
}

void TFatturazione_bolle::stati_validi(TToken_string& stati) const
{
  const TString& s7 = get("S7");
  stati.cut(0);
  TString16 s;
  for (int i = 0; i < 5; i++)
  {
    s = s7.mid(i*4, 1);
    s.trim();
    if (s.not_empty())
      stati.add(s);
  }
  CHECK(!stati.empty_items(), "Nessuno stato documento valido");
}


bool TFatturazione_bolle::raggruppa(TDocumento& doc_in, TDocumento& doc_out)
{                 
#ifdef DBG
  const TString tipi = get("S2");
  const TString& tipodoc = doc_in.tipo().codice();                 
  for (int i = 0; i < 5; i++)
  {                  
    if (tipodoc == tipi.mid(i*4, 4))
      break;
  } 
  if (i >= 5)
  {
    NFCHECK("Tipo documento non valido: '%s'", (const char*)tipodoc);
    return FALSE;
  }  
#endif
  
  const char stato_finale_in = get_char("S4");
  doc_in.stato(stato_finale_in);
  
  const TString& tipo_out = get("S8");
  doc_out.put("TIPODOC", tipo_out);
  
  const char stato_finale_out = get_char("S9");
  doc_out.stato(stato_finale_out);           
  
  if (gestione_riferimenti())
  { 
    // Determina ed eventualmente crea la riga di riferimento
    const int riga_rif = riferimenti_in_testa() ? 1 : doc_out.rows()+1;
    if (riga_rif > doc_out.rows())
    {
      TRiga_documento& rout = doc_out.new_row();
      rout.forza_sola_descrizione();
    }  
    
    TRiga_documento& rout = doc_out[riga_rif];     
    
    // Costruisce la stringa di riferimento
    TString riferimento(80);
    riferimento = doc_in.tipo().riferimento();
    if (riferimento.empty()) 
      riferimento = doc_in.tipo().descrizione();
    riferimento << " n. " << doc_in.numero();
    riferimento << " del " << doc_in.data().string();
    
    // Setta la descrizione se vuota
    if (rout.get("DESCR").empty())
      rout.put("DESCR", riferimento);
    else
    {                               
      // Altrimenti aggiungi il riferimento al memo
      TString memo(1024);
      memo = rout.get("DESCEST");
      if (memo.empty())
        rout.put("DESCLUNGA", "X");
      else
        memo << '\n';
      memo << riferimento;
      rout.put("DESCEST", memo);
    }  
  }

  const bool ignora_desc = ignora_descrizioni();

  TToken_string campi_riga(80);   
  const bool ragg_rig = raggruppa_righe();
  if (ragg_rig)
  {
    campi_riga = "CODART|UMQTA";  // Uguali sempre
    // Uguali opzionalmente
    if (riga_uguale(0)) campi_riga.add("CODMAG");
    if (riga_uguale(1)) campi_riga.add("CODIVA");
    if (riga_uguale(2)) campi_riga.add("PREZZO|SCONTO");
  }
  
  for (int r = 1; r <= doc_in.rows(); r++)
  {                
    const TRiga_documento& rin = doc_in[r];
    const bool rindesc = rin.sola_descrizione(); // La riga di input e' descrittiva
    if (ignora_desc && rindesc)
      continue;
    
    bool elaborata = FALSE;
    
    // Raggruppo le righe se e' settato il falg di raggruppamento e 
    // se la riga non contiene solo una descrizione
    if (ragg_rig && !rindesc)                    // Se devo raggruppare le righe ...
    {              
      const int last = doc_out.rows();
      for (int o = 1; o <= last; o++)            // ... cerca una riga compatibile
      {
        TRiga_documento& rout = doc_out[o];  
        if (rout.sola_descrizione())             // Ignora le righe descrittive
          continue;
        if (rin.raggruppabile(rout, campi_riga)) // Se esiste una riga compatibile ...
        {
          rout += rin;                           // ... sommaci la quantita' ecc.
          elaborata = TRUE;                      // Ricorda di averla gia' elaborata
          break;
        }  
      }  
    }
    if (!elaborata)                              // Se la riga non e' stata gia' sommata ...
    {
      TRiga_documento& rout = doc_out.new_row(); // ... crea una riga nuova e ...
      doc_out.copy_data(rout, rin);              // ... copiaci tutti i campi della riga sorgente.
    }  
  }
  
  return TRUE;
}

bool TFatturazione_bolle::elabora(TLista_documenti& doc_in, TLista_documenti& doc_out,
                                  const TDate& data_elab)
{       
  bool ok = TRUE;
  
  TToken_string campi_doc(128);             // Lista di campi che devono essere uguali
  campi_doc = "TIPOCF|CODCF|CODVAL|CODLIN"; // Uguali sempre
      
  // Uguali opzionalmente
  const char* cond[] = { "CAMBIO", "SCONTO", "TIPODOC", "CODNUM",
                         "CODPAG", "CODABIA|CODCABA", "CODLIST", "CODAG",
                         "CODSPMEZZO", "CODPORTO", "CAUSTRASP", "CODVETT1|CODVETT2|CODVETT3",
                         NULL };
  for (int u = 0; cond[u]; u++)  
    if (doc_uguale(u)) campi_doc.add(cond[u]);

  for (int id = 0; id < doc_in.items() && ok; id++)
  {                                             
    TDocumento& campione = doc_in[id];
    const int tot = doc_out.items();
    int od = tot;                    
    
    if (campione.raggruppabile())    // Se il documento ha il flag di raggruppabilita' ...
    {
      for (od = 0; od < tot; od++)   // ... cerca un documento compatibile.
      {
        if (campione.raggruppabile(doc_out[od], campi_doc))
          break;
      }
    }  
    if (od >= tot)   // Se non ho trovato un documento compatibile ...
    {                // ... creane uno nuovo (certamente compatibile)                  
      const char    provv  = tipo_numerazione();
      const int     anno   = campione.anno();
      const TString codnum = codice_numerazione_finale();
      TDocumento* new_doc  = new TDocumento(provv, anno, codnum, -1);
      // Copia i dati della testata
      TDocumento::copy_data(new_doc->head(), campione.head());   
      
      new_doc->put("DATADOC", data_elab);
 
      // Aggiungilo alla lista dei documenti in uscita
      od = doc_out.add(new_doc);               
    }                      
    
    ok = raggruppa(campione, doc_out[od]);
   }
  return ok;
}

