#include <dongle.h>
#include <modaut.h>
#include <prefix.h>
#include <tabutil.h>
#include <utility.h>

#include "veini.h"
#include "velib.h"
#include "sconti.h"             
#include "vepriv.h"
#include "veuml.h"

#include <cfven.h>
#include "../cg/cg2103.h"
#include "../cg/cglib01.h"
#include "../mg/mglib.h"
#include "../mg/anamag.h"
#include "../mg/movmag.h"
#include "../pr/prlib.h"
#include "../sv/svlib01.h"
#include "../db/dblib.h"

#include <clifo.h>

// calcola il prezzo per le spese
void sppr_calc(const TRectype & rec, const TString & valuta_doc, const real & cambio, real & prezzo, exchange_type controeuro)
{     
  const TString16 sppr_valuta(rec.get("S4"));                        
        
  if (sppr_valuta != valuta_doc)
  {                                          
    const bool prezzo_un = rec.get_char("S6") == 'Q';
    if (prezzo_un)
    {
      TPrice val(prezzo, sppr_valuta);
    
      val.change_value(valuta_doc, cambio, controeuro);
      prezzo = val.get_num();
    }
    else
    {
      TCurrency val(prezzo, sppr_valuta);
    
      val.change_value(valuta_doc, cambio, controeuro);
      prezzo = val.get_num();
    }
  }
}

///////////////////////////////////////////////////////////
// Movimento di magazzino
///////////////////////////////////////////////////////////

class TMov_mag_doc : public TMov_mag
{
  TString_array _codmagc;
  
protected:
  virtual const char * codmag_rauto(int r) const;

public:
  void add_magc(const char* magc) { _codmagc.add(magc); }
  TMov_mag_doc() { }
  virtual ~TMov_mag_doc() {}
};

const char* TMov_mag_doc::codmag_rauto(int r) const
{
  TRecord_array & b = body();

  if (r > b.rows()) // Can't check non-existent rows
    return NULL;

  TRectype & row = b[r];
  const char tr = row.get_char(RMOVMAG_TIPORIGA);

  if (tr != 'D' && tr != 'A') // These are customer's added rows
    return NULL;
  
  int j = -1; // Indice per reperire il mag. collegato da _codmagc
  for (int i = r; i > 0; i--) // Scorre dalla riga r in su e conta quante righe D
    if (b[i].get_char(RMOVMAG_TIPORIGA) == 'D')
      j++;
      
  if (j >= 0)
  	return _codmagc.row(j);
  else
  	return NULL;
}

/////////////////////////////////////////////////////////////
// TRiepilogo IVA
/////////////////////////////////////////////////////////////
TRiepilogo_iva& TRiepilogo_iva::copy(const TRiepilogo_iva& a)
{ 
  (TRectype &) _codiva = (TRectype &) a._codiva;
  _imp = a._imp;
  _imp_spese = a._imp_spese;
  _imp_spese_row = a._imp_spese_row;
  _iva = a._iva;
  _iva_spese = a._iva_spese;
  _sconto_perc = a._sconto_perc;
  _sconto_imp = a._sconto_imp;
  _iva_sconto = a._iva_sconto;
  _tipo = a._tipo;
  return *this;
}

TRiepilogo_iva::TRiepilogo_iva(const TCodiceIVA & codiva) : _codiva(codiva)
{
  const TString& t =_codiva.tipo();
  if (t == "VE")
    _tipo = 2; else
  if (t == "ES")
    _tipo = 4; else
  if (t == "NI")      
    _tipo = 8; else
  if (t == "NS")       
    _tipo = 16;
  else
    _tipo = 1;
}  

///////////////////////////////////////////////////////////
// Agenti
///////////////////////////////////////////////////////////
class TAgenti_cache : public TRecord_cache
{                                                
protected:
  virtual TObject* rec2obj(const TRectype& rec) const { return new TAgente(rec);}
  
public:
  const TAgente& agente(const char* chiave) { return (const TAgente &) get(chiave);}

  TAgenti_cache() : TRecord_cache(LF_AGENTI) {}
  virtual ~TAgenti_cache() { }
};

HIDDEN TAgenti_cache * _agenti = NULL; 

///////////////////////////////////////////////////////////
// Documento per vendite
///////////////////////////////////////////////////////////
 
long TDocumento::_firm = -1;
TAssoc_array TDocumento::_tipi;
TAssoc_array TDocumento::_numerazioni;
TString4 TDocumento::_codiva_spese;
TString4 TDocumento::_codiva_bolli;
short TDocumento::_has_mag = 3;
short TDocumento::_has_stat_ven = 3;
short TDocumento::_has_provv = 3;
TCodgiac_livelli * TDocumento::_livelli=NULL;

// HIDDEN TStats_agg _st_agg;
HIDDEN TAssoc_array _docs_to_agg;

void TDocumento::init()
{
  add_file(LF_RIGHEDOC, "NRIGA");
  set_memo_fld("G1");

  _tipocf = new TRecfield(*this, "TIPOCF");
  _codcf = new TRecfield(*this, "CODCF");
  _cod_occas = new TRecfield(*this, "OCFPI");
  _provv_agente = new TProvvigioni_agente;
  
  _sconto = _esenzione = NULL;
  _stato_originale = ' ';
  _dirty_deny = false;
	_spese_updated = false;
  
  check_modules();

  TConfig c(CONFIG_DITTA, "ve");
}

TDocumento::TDocumento()
          : TMultiple_rectype(LF_DOC)
{                      
  init();
}

TDocumento::TDocumento(const TDocumento & d)
          : TMultiple_rectype(LF_DOC)
{                                                                             
  init();
  copy(d);
}

TDocumento::TDocumento(char provv, int anno, const char* codnum, long numdoc)
          : TMultiple_rectype(LF_DOC)
{
  init();
  if (numdoc <= 0)
  { 
    numdoc = 0;
    set_key(*this, provv, anno, codnum, numdoc);
  }
  else
    read(provv, anno, codnum, numdoc);
}

TDocumento::TDocumento(const TRectype& rec)
          : TMultiple_rectype(LF_DOC)
{
  init();
  read(rec);
}

TDocumento::~TDocumento()
{
  delete _tipocf;
  delete _codcf;
  delete _cod_occas;

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

const TString& TDocumento::codiva_spese() const 
{ ((TDocumento *)this)->test_firm(); return _codiva_spese;} 

const TString& TDocumento::codiva_bolli() const 
{ ((TDocumento *)this)->test_firm(); return _codiva_bolli;} 

void TDocumento::check_modules()
{ 
  if (_has_mag == 3)
  {               
    _has_mag = dongle().active(MGAUT);
    _has_stat_ven = dongle().active(SVAUT);
    _has_provv = dongle().active(PRAUT);
  }
}

void TDocumento::set_variables(TExpression * e) const
{ 
  const int items = e->numvar();
  for  (int i = 0; i < items; i++)
  {                          
    const TFieldref field(e->varname(i), LF_DOC);

    switch (field.file())
    {
      case LF_CLIFO :
        e->setvar(i, clifor().get(field.name()));
        break;
      case LF_CFVEN :
        e->setvar(i, clifor().vendite().get(field.name()));
        break;
      default:
        e->setvar(i, get(field.name()));
        break;
    }   
  }
}  

void TDocumento::test_firm()
{                
  const long new_firm = prefix().get_codditta();
  
  if (_firm != new_firm)
  {
    TConfig conf(CONFIG_DITTA, "ve");        
    _codiva_spese = conf.get("SPINCODIVA");
    _codiva_bolli = conf.get("SPBOCODIVA");
    _firm = new_firm;
  }
}
                    
real TDocumento::spese_incasso(int ndec, TTipo_importo t) const
{        
  static TArray spese_inc;

  real imp_spese;   
  const real percentuale = get(DOC_PERCSPINC);
  
  if (percentuale > ZERO)
  {
    if (ndec == AUTO_DECIMALS)
      ndec = decimals();
    if (spese_inc.objptr(_rim_dir) == NULL)
    {
      TConfig conf(CONFIG_STUDIO, "ve");
      for (TTipo_pag p = _rim_dir; p < _nessun_pag; p = TTipo_pag((int)p + 1)) 
      {
        const real r(conf.get("IMPSPINC", "ve", p));
        spese_inc.add(r, p);
      }
    }   

    // Calcola l'eventuale importo minimo per effetti (solitamente zero)
    const TRectype& cfven = clifor().vendite();
    const real impmineff = cfven.get_real(CFV_IMPMINEFF);

    // Somma le spese di ogni singola rata
    const TPagamento& pag = ((TDocumento *)this)->pagamento();  
    const int nrate = pag.n_rate();
    for (int i = 0; i < nrate; i++)
    {
      if (pag.importo_rata(i) >= impmineff)  // Se la rata supera il minimo contrattuale
      {
        const TTipo_pag tp = (TTipo_pag)pag.tipo_rata(i);
        imp_spese += (const real&)spese_inc[tp];
      }
    }              
    imp_spese = imp_spese * percentuale / CENTO;
    
    if (t == _lordo || t == _imposta)
    {
      TString4 codiva_es; iva_esente(codiva_es);                       
      const real iva_spese(TRiga_documento::iva(codiva_es.full() ? (const TString &)codiva_es : codiva_spese()).imposta(imp_spese, ndec));                      
      if (t == _lordo)
        imp_spese += iva_spese;
      else
        if (t == _imposta)
          imp_spese = iva_spese;
    }         

    const real cambio = get(DOC_CAMBIO);
    if (!cambio.is_zero())
    {                      
      // Converte le spese nella valuta del documento
      const exchange_type ce = get_bool(DOC_CONTROEURO) ? _exchange_contro : _exchange_base;
      imp_spese = change_currency(imp_spese, "", ZERO, _exchange_undefined,
                                  get(DOC_CODVAL), cambio, ce, -1);
    }
    
    imp_spese.round(ndec);
  }
  return imp_spese;
}
                                      
void TDocumento::iva_esente(TString& codiva_es) const                                     
{
  codiva_es.cut(0);

  const int rows = physical_rows();    
  for (int r = 1; codiva_es.empty() && r <= rows; r++)
  {
    const TRiga_documento& riga = ((TDocumento*)this)->row(r); 
    const TString4 str_codiva(riga.get(RDOC_CODIVA));
    
    if (str_codiva.full())
    {
      const TCodiceIVA codiva(str_codiva);
      const TString& tipoiva = codiva.tipo();      
      if (tipoiva.empty())
        break;
      if (tipoiva == "NI")
        codiva_es = str_codiva;
    }     
  }                                    
}

real TDocumento::bolli(real & imp, int ndec, TTipo_importo t) const
{                         
  real tot_bolli;
  static TArray sca_bolli;
  static TArray imp_bolli;
  static real bolli_es;
  static real impmin_bolli;
  static int nscagl;

  if (get_bool("ADDBOLLI"))
  {
    if (sca_bolli.objptr(0) == NULL)
    {
      TConfig conf(CONFIG_STUDIO, "ve");
      
      bolli_es = (real) conf.get("BOLLIES", "ve");
      impmin_bolli = (real) conf.get("IMPMINBOLLI", "ve");
      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);
      }
    } 
    if (ndec == AUTO_DECIMALS)
      ndec = decimals();
    
    TCurrency_documento imp_val(imp);
    imp_val.change_to_firm_val();
    real importo = imp_val.get_num();
    
    TPagamento & pag = ((TDocumento*)this)->pagamento();  
    const int nrate = pag.n_rate();
    real old_bolli = -1.00;
    real iva_bolli;    
    
    TCurrency_documento imp_orig_val(imposta());
    imp_orig_val.change_to_firm_val();
    const real imp_orig = imp_orig_val.get_num();
    
    TCurrency_documento spese_val(spese());
    spese_val.change_to_firm_val();
    const real sp_orig = spese_val.get_num();
    bool estero = FALSE; // Assumiamo per ora non estero
    TString16 codiva_es;
    
    iva_esente(codiva_es);                       

    for (int j = 0; j < 5 && tot_bolli+iva_bolli != old_bolli; j++)
    {
      old_bolli = tot_bolli + iva_bolli;  
      const real imposte = imp_orig + iva_bolli;
      const real imp_spese = sp_orig + tot_bolli - iva_bolli;
      const real imponibile = importo - imposte - imp_spese;
      tot_bolli = ZERO;      
      if (!tipo().nota_credito())
      {
        real imponibile_esente;
        for (int r = physical_rows(); r > 0; r--)
        {
          const TRiga_documento& riga = ((TDocumento*)this)->row(r); 
          const TCodiceIVA codiva(riga.get(RDOC_CODIVA));
                
          if (codiva.tipo().not_empty())
            imponibile_esente += riga.imponibile();
        }
        if (imponibile_esente >= impmin_bolli)
          tot_bolli = bolli_es;
      }    
      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: 
            {
            	int i;
            	
              for (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 (j == 0) // Dobbiamo inizializzare la variabile 'estero'
              { 
                TString16 key; 
                key.format("%c|%ld", get_char(DOC_TIPOCF), get_long(DOC_CODCF));
                const TRectype& clifo = cache().get(LF_CLIFO, key);

                const TString& stato_iva = clifo.get(CLI_STATOPAIV);
                estero = stato_iva.not_empty() && stato_iva != "IT";
                if (!estero)
                {
                  const TString& stato_cf = clifo.get(CLI_STATOCF);
                  estero = (stato_cf.not_empty() && stato_cf != "IT") || clifo.get_char(CLI_COMCF) == 'Z';
                }
              }
              real r(imp);
              const int ndec = decimals();
              r.ceil(ndec == 0 ? -3 : 0);
              if (estero)
                r *= 0.009;
              else
                r *= 0.012;
              r.round(ndec == 0 ? -2 : ndec);
              tot_bolli += r;
            }
            break;
          case _cessione:
          case _paghero:
          case _let_cred:
          case _rim_dir:
          case _rid:
          case _bonfico:
          default:
            break;
        }
      }
      iva_bolli = TRiga_documento::iva(codiva_bolli()).imposta(tot_bolli, ndec);                      
      importo += (tot_bolli + iva_bolli - old_bolli);
    }        
    if (t == _lordo)
      tot_bolli += iva_bolli;
    else
      if (t == _imposta)
        tot_bolli = iva_bolli;

    if (in_valuta())
    {                      
      const real cambio = get_real("CAMBIO");
      const exchange_type ce = get_bool(DOC_CONTROEURO) ? _exchange_contro : _exchange_base;
      tot_bolli = change_currency(tot_bolli, "", ZERO, _exchange_undefined,
                                  get(DOC_CODVAL), cambio, ce, -1);
    }
    tot_bolli.round(ndec);
  }

  return tot_bolli;
}

const TString & TDocumento::commessa_principale() const
{
	if (codice_commessa().blank())
	{
		const int row = physical_rows();

		for (int i = 1; i <= rows(); i++)
		{
			const TRiga_documento &r = ((TRiga_documento &) ((TDocumento *)this)->row(i));

			if (!r.codice_commessa().blank())
				return r.codice_commessa();
		}
	}
	return codice_commessa();
}

bool TDocumento::modificabile() const
{                     
  const char stato_attuale = stato();

  if (stato_attuale <= ' ')
    return TRUE;
  
  const TString& stati_modifica = tipo().stati_iniziali_modifica();
  return stati_modifica.blank() || stati_modifica.find(stato_attuale) >= 0;
}      

bool TDocumento::cancellabile() const
{
  const char stato_attuale = stato();

  if (stato_attuale <= ' ')
    return TRUE;
  
  const TString& stati_cancellazione = tipo().stati_iniziali_cancellazione();
  return stati_cancellazione.blank() || stati_cancellazione.find(stato_attuale) >= 0;
}      

bool TDocumento::stampabile() const
{
  const char stato_attuale = stato();
  if (stato_attuale <= ' ')
    return TRUE;

  if (stato_attuale == tipo().stato_finale_stampa())  
    return FALSE;
  
  const TString& stati_stampa = tipo().stati_iniziali_stampa();
  return stati_stampa.blank() || stati_stampa.find(stato_attuale) >= 0;
}      

bool TDocumento::bloccato() const
{
  const char stato_attuale = stato();

  if (stato_attuale <= ' ')
    return FALSE;
  
  char stato_bloccato = tipo().stato_bloccato();

  if (stato_bloccato <= ' ')
    return FALSE;
  
  return stato_attuale >= stato_bloccato;
}      

// 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(DOC_PROVV,  provv); 
  rec.put(DOC_ANNO,   anno);
  rec.put(DOC_CODNUM, codnum);  
  rec.put(DOC_NDOC,   numdoc);
}

// Funzione statica utile a tutti gli utenti di LF_DOC e LF_RIGHEDOC
void TDocumento::copy_data(TRectype& dst, const TRectype& src)
{                           
  const bool is_riga = dst.num() == LF_RIGHEDOC;

  // Memorizza tutti i campi chiave
  const char provv       = dst.get_char(RDOC_PROVV);
  const int anno         = dst.get_int(RDOC_ANNO);  
  const TString4 codnum  = dst.get(RDOC_CODNUM);  
  const long numdoc      = dst.get_long(RDOC_NDOC);  
  const int nriga        = is_riga ? dst.get_int(RDOC_NRIGA) : 0;
  const long idriga      = is_riga ? dst.get_long(RDOC_IDRIGA) : 0;  
  // Copia tutto il record     
  dst = src;                          
  // Ripristina tutti i campi chiave
  set_key(dst, provv, anno, codnum, numdoc);
 	dst.init_memo(RECORD_NON_FISICO);
  if (is_riga)
  {
    dst.put(RDOC_NRIGA, nriga);
    dst.put(RDOC_IDRIGA, idriga);
    dst.zero(RDOC_MOVMAG);
    const TString memo = src.get(RDOC_DESCEST);
    dst.put(RDOC_DESCEST, memo);
  }
  else
  {
    dst.zero(DOC_MOVMAG);
    dst.zero(DOC_NUMREG);
    dst.zero(DOC_NUMREGCA);
  }
}

// Funzione statica utile a tutti gli utenti di LF_RIGHEDOC
void TDocumento::copy_data(TRiga_documento& dst, const TRiga_documento& src)
{                           
  copy_data((TRectype&)dst, (const TRectype&)src);
  dst.put(RDOC_CODCMS, src.codice_commessa());
  dst.put(RDOC_FASCMS, src.fase_commessa());
  dst.put(RDOC_CODCOSTO, src.codice_costo());
}
  
void TDocumento::copy_contents(const TDocumento& src, bool copy_header)
{
	if (copy_header)
		copy_data(head(), src.head()); 
  destroy_rows();  
  const int rows = src.physical_rows(); 
  for (int i = 1; i <= rows ; i++)
  {
    const TRiga_documento& s = src[i];
    TRiga_documento & r = new_row(s.tipo().codice());
    copy_data(r, s);
    r.set_original_rdoc_key(s);
  }
}

TRiga_documento& TDocumento::insert_row(int row, const char *tipo)
{       
  TRiga_documento& r = (TRiga_documento&)TMultiple_rectype::insert_row(row);
  if (tipo && *tipo)
    r.set_tipo(tipo);
  return r;
}

TRiga_documento& TDocumento::new_row(const char *tipo)
{
  TRiga_documento& r = (TRiga_documento&)TMultiple_rectype::new_row();
  if (tipo && *tipo)
    r.set_tipo(tipo);
  return r;
}

int TDocumento::read(TBaseisamfile& f, word op, word lockop)
{ 
  int err = TMultiple_rectype::read(f, op ,lockop); 

  _cli_for.zero();
  _occas.zero();

  set_riga_sconto();
  if (is_fattura())
    set_riga_esenzione();
  _stato_originale = stato();
	_spese_updated = get_bool(DOC_SPESEUPD);
  
  if (err == NOERR && tipo_valido() && tipo().statistiche() && _has_stat_ven)
  {       
    TString80 key(get(DOC_PROVV)); key << get(DOC_ANNO); key << get(DOC_CODNUM); key << get(DOC_NDOC);
    TObject* o = _docs_to_agg.objptr(key);

    const bool is_nota_credito = tipo().nota_credito();

    if ( lockop >= _lock && o == NULL)
    {          
      TStats_agg * st_agg = new TStats_agg;
      for (int i = physical_rows(); i > 0; i--)
        if (is_nota_credito)
          st_agg->add(row(i));
        else
          st_agg->sub(row(i));
      _docs_to_agg.add(key, st_agg, TRUE);
    }
    else 
      if (lockop == _unlock && o != NULL)
        _docs_to_agg.remove(key);
  }
  
  if (err == NOERR && _has_provv)
    _old_agente = get(DOC_CODAG);
  else
    _old_agente.cut(0);

  return err;
}

int TDocumento::read(char provv, int anno, const char* codnum, long numdoc, word op, word lockop)
{                           
  CHECK(numdoc > 0, "Numero documento nullo."); 
  zero();
  set_key(*this, provv, anno, codnum, numdoc);
  return read(op, lockop);
}

long TDocumento::renum_ndoc(long numdoc)
{
  if (numdoc <= 0)
  {
    const char     tn = tipo_numerazione();
    const int      an = anno();
    const TString4 nu = numerazione();
    numdoc  = get_next_key(tn, an, nu);
  }   
  put(DOC_NDOC, numdoc);       // Aggiorna testata
  TMultiple_rectype::renum_key(); // 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 TString16 _tipo_riga_sc;
      if (_tipo_riga_sc.empty())
      {
        TConfig conf(CONFIG_STUDIO, "ve");
        _tipo_riga_sc = conf.get("TRSCONTI", "ve");
        // Se non esiste il tipo riga lo cerca, lo setta di default ed avvisa        
        if (_tipo_riga_sc.empty())
        {
          _tipo_riga_sc = "08";
          conf.set("TRSCONTI", _tipo_riga_sc);
          warning_box("Il tipo riga sconti di testa non risultava impostato.\n L'applicazione usera' automaticamente il tipo %s", (const char*) _tipo_riga_sc);
        }
      }
      _sconto = new TRiga_documento(this, _tipo_riga_sc); 
      _sconto->put("DESCR","Sconto");
    }
    _sconto->put("SCONTO", sconto);
  }
}

void TDocumento::set_riga_esenzione()
{                                  
  TCli_for & c = clifor();   
  const TCodiceIVA codes(c.vendite().get(CFV_ASSFIS));
  const TString16 v_esenzione(c.vendite().get(CFV_VSPROT));
  const TString16 v_data_esenzione(c.vendite().get(CFV_VSDATAREG));
  const TString16 n_registrazione(c.vendite().get(CFV_NSPROT)); 
  const TString16 n_data_registrazione(c.vendite().get(CFV_NSDATAREG));
  bool esente = codes.tipo().not_empty() && v_esenzione.not_empty() &&
                v_data_esenzione.not_empty() && n_registrazione.not_empty() &&  
                n_data_registrazione.not_empty();

  if (esente)                         
  {
    esente = false;
    const TString8 codiva = codes.codice();
    for (int i = physical_rows(); !esente && i > 0; i--)
      esente = row(i).get(RDOC_CODIVA) == codiva;
  }
  
  if (!esente)
  {        
    if(_esenzione != NULL)
      delete _esenzione;
    _esenzione = NULL;
  }
  else 
  {
    static TString4 _tipo_riga_es; 
    static TString80 _des_esenz;
    static real _bollo_es;
    if (_tipo_riga_es.empty())
    {
      TConfig conf(CONFIG_STUDIO, "ve");   
      _tipo_riga_es = conf.get("TRESENZ", "ve"); 
      _bollo_es = (real)conf.get("BOLLIES", "ve");
      if (_tipo_riga_es.empty())           
      {
        _tipo_riga_es = "05";
        conf.set("TRESENZ", _tipo_riga_es);
        warning_box("Il tipo riga esenzione non risultava impostato.\n L'applicazione usera' automaticamente il tipo %s", (const char*) _tipo_riga_es);
      }                         
      _des_esenz = conf.get("DESESENZ", "ve"); 
      if (_des_esenz.not_empty())
        _des_esenz.insert(" ");
      _des_esenz.insert("Fattura non imponibile");
    }
    if (_esenzione == NULL)
      _esenzione = new TRiga_documento(this, _tipo_riga_es); 
    TString d(256); d = _des_esenz;

    d << format(" come da vostra dichiarazione n. %s del %s da noi annotata al n. %s il %s.",
                (const char *) v_esenzione, (const char *) v_data_esenzione, 
                (const char *) n_registrazione, (const char *) n_data_registrazione);
    
    _esenzione->put(RDOC_DESCR, d.left(50));                                                     
    _esenzione->put(RDOC_DESCLUNGA, "X");
    _esenzione->put(RDOC_DESCEST, d.mid(50));
  }
}
                                 
void TDocumento::dirty_fields()
{
  if (!_dirty_deny)
  {
    for (TDocumento_variable_field * f = (TDocumento_variable_field *) first_variable_field(); 
         f != NULL; f = (TDocumento_variable_field *) succ_variable_field()) 
      f->set_dirty();   
    dirty_tabella_iva();

    if (loaded_rows(LF_RIGHEDOC)) // Se ho gia' caricato delle righe in memoria
    {
		  TRecord_array& righe = body(LF_RIGHEDOC);
      for (int i = righe.last_row(); i > 0; i = righe.pred_row(i))
      {
        TRiga_documento & r = (TRiga_documento &) righe[i];     
        r.dirty_fields(FALSE);
      }  
    }
    _dirty_deny = true;
  }
}

int TDocumento::write_rewrite(TBaseisamfile & f, bool re) const
{         
  TDocumento & myself = *((TDocumento *)this);
  const bool new_doc = nuovo() || numero() <= 0;  // E' nuovo di zecca!       
  
  if (new_doc)
  {
    char stato_finale = tipo().stato_finale_inserimento();
    if (stato() == '\0' && stato_finale > ' ')
      myself.stato(stato_finale);
  }                       
  else
    myself._stato_originale = stato();    

  const bool doc_bloccato = bloccato();
  const char stato_doc(stato());

  int err = NOERR;

  if (!doc_bloccato)
  {
		myself.put(DOC_SPESEUPD, _spese_updated);
    if(tipo().spese_aut() && !_spese_updated)
    {
      TString16 name("CODSP0");
      TString_array spese;        
      const TRectype & ven_rec = clifor().vendite();
      for (int i = 1; i <= 4; i++)
      {                     
        name.rtrim(1); name << i;
        const TString16 s(ven_rec.get(name));
        
        if (s.not_empty()) 
          spese.add(s);
      }               
      myself.update_spese_aut(spese);
    }
    myself.update_conai();
    myself.set_row_ids();
//	  const int rows = physical_rows();
  
    long num = get_long("MOVMAG");            
  
    const bool check_movmag = dongle().active(MGAUT) && tipo().mov_mag();
    if (check_movmag)
    {           
      const bool do_movmag = tipo().stato_with_mov_mag(stato_doc) && get(DOC_CAUSMAG).not_empty();

      TMov_mag_doc mov;
      TLocalisamfile m(LF_MOVMAG);
      mov.zero();
      if (num == 0 && do_movmag)
      {
        err = mov.write(m);
        if (err != NOERR)
          return err;
        num = mov.get_long(MOVMAG_NUMREG);
        myself.put("MOVMAG", num);
      }                          
      if (num > 0)
      {                                
        const bool scarica_residuo = tipo().scarica_residuo();
      
        mov.put(MOVMAG_NUMREG, num);   
        while (mov.read(m, _isequal, _testandlock) == _islocked)
          message_box("Movimento di magazzino n. %ld in uso da parte di un'altro utente", num);
        if (do_movmag)
        {
          TRecord_array & b = mov.body();
          const int mag_rows = mov.rows();
          int i;            

          for (i = mag_rows; i > 0; i--)
          { 
            TRectype & r = b[i];
            if (r.get_char(RMOVMAG_TIPORIGA) == riga_dadocumento)
            {
              b.destroy_row(i);             
              if (b.exist(i + 1) &&
                  b[i + 1].get_char(RMOVMAG_TIPORIGA) == riga_automatica)
                b.destroy_row(i + 1);             
            }
            else
              if (r.get_bool(RMOVMAG_ESPLOSA))
                b.destroy_row(i);             
          }
          b.pack();
          
          const TDate d(get("DATADOC"));
          TString8 codes; codes.format("%04d", mov.codice_esercizio(d));
          mov.put(MOVMAG_ANNOES, codes);
          mov.put(MOVMAG_DATAREG, d);
          mov.put(MOVMAG_DATACOMP, d);
          mov.put(MOVMAG_DOCPROVV, get("PROVV"));
          mov.put(MOVMAG_ANNODOC, get("ANNO"));;       
          mov.put(MOVMAG_CODNUM, get("CODNUM"));          
          long numdoc = get_long("NDOC");
          if (numdoc <= 0L)
            numdoc = myself.renum_ndoc(numdoc);
          
          mov.put(MOVMAG_NUMDOC, numdoc);     
          const long ex_numdoc = get_long("NUMDOCRIF");
          if (ex_numdoc == 0)
          {
            mov.put(MOVMAG_EXNUMDOC, numdoc);     
            mov.put(MOVMAG_EXDATADOC, d);     
          }
          else
          {
            mov.put(MOVMAG_EXNUMDOC, ex_numdoc);     
            const TDate ex_d(get("DATADOCRIF"));
            mov.put(MOVMAG_EXDATADOC, ex_d);     
          }
          mov.put(MOVMAG_CATVEN, get("CATVEN"));
          mov.put(MOVMAG_CODLIST, get("CODLIST"));
          mov.put(MOVMAG_CODCONT, get("CODCONT"));
          mov.put(MOVMAG_CODCAMP, get("CODCAMP"));
          mov.put(MOVMAG_CODCAUS, get("CAUSMAG")); 
          const TString4 codnum(numerazione());
          mov.put(MOVMAG_DESCR, format("%s %s n. %ld del %s", (const char *) tipo().get("S1"),(const char *)codnum, numdoc, (const char *) d.string()));
          mov.put(MOVMAG_TIPOCF, get("TIPOCF"));
          mov.put(MOVMAG_CODCF, get("CODCF"));

          int j = 1;
          real cambio = ZERO;

          if (get(DOC_CODVAL).not_empty())
            cambio = get_real(DOC_CAMBIO);
          if (cambio == ZERO)
            cambio = 1.0;
					const TString8 cod_caus(mov.get(MOVMAG_CODCAUS));
					TCausale_magazzino caus(cod_caus);
          const bool esplodente = caus.esplodente();
          const bool scarica_alt = caus.scarica_alternativi();
          TString16 cod_caus_riga;
          TDistinta_tree dist;
          TString80 codart;
      
          for (i = 1; i <= physical_rows(); i++)
          {            
            TRiga_documento & r = myself.row(i);
            const bool articolo = r.is_articolo();
            bool valid_row = articolo;

            cod_caus_riga = r.get("CAUSMAG");
            if (articolo)
	            codart = r.get("CODARTMAG");
						else
						{
              codart = r.get("CODART");
	            if (codart.full() && (cod_caus_riga.full() ? TCausale_magazzino(cod_caus_riga).esplodente() : esplodente))
	              valid_row = dist.set_root(TCodice_articolo(codart));
						}
            if (valid_row)
            {                  
              long r_num = r.get_long("MOVMAG");
              if (r_num == 0)
              {
                r_num = num;
                r.put("MOVMAG", r_num);
              }
  
              const real qta = scarica_residuo ? r.qtaresidua(): r.quantita();
    
              if (r_num == num && qta != ZERO)
              {
                TRectype & rm = mov.insert_row(j++);
              
                mov.add_magc(r.get("CODMAGC"));
              
                rm.put(RMOVMAG_IMPIANTO, r.get("IMPIANTO"));;       
                rm.put(RMOVMAG_LINEA, r.get("LINEA"));          
                rm.put(RMOVMAG_CODMAG, r.get("CODMAG"));
								if (articolo && (cod_caus_riga.full() ? TCausale_magazzino(cod_caus_riga).scarica_alternativi() : scarica_alt))
								{
									const TRectype art = cache().get(LF_ANAMAG, codart);
									const TString & alt = art.get(ANAMAG_CODARTALT);

									if (alt.full())
										codart = alt;
								}
                rm.put(RMOVMAG_CODART, codart);
                rm.put(RMOVMAG_LIVGIAC, r.get("LIVELLO"));
                rm.put(RMOVMAG_UM, r.get("UMQTA"));
              
                rm.put(RMOVMAG_QUANT, qta);
    
                TCurrency_documento prezzo(r.prezzo(TRUE, FALSE), *this, TRUE);
  
                prezzo.change_to_firm_val();
                rm.put(RMOVMAG_PREZZO, prezzo.get_num());
                rm.put(RMOVMAG_CODCAUS, cod_caus_riga);
                rm.put(RMOVMAG_TIPORIGA, (char) riga_dadocumento);   
              }
            }
          }
          mov.rewrite(m);
        }
        else
        {
          mov.remove(m);
          for (int i = physical_rows(); i > 0; i--)
          {
            TRiga_documento & r = myself.row(i);

            r.zero("MOVMAG");
          }
          myself.zero("MOVMAG");
        }
      } 
    }
  } 

  {
    TLocalisamfile anamag(LF_ANAMAG);
    TLocalisamfile codalt(LF_CODCORR);
    codalt.setkey(2);
    bool docevaso = TRUE;
    const TDate datacons(get_date(DOC_DATACONS));
    const TString80 codcms(get(DOC_CODCMS));
    const TString80 fascms(get(DOC_FASCMS));
    const TString80 codcos(get(DOC_CODCOSTO));
    
    for (int i = physical_rows(); i > 0; i--)
    {
      TRiga_documento& r = myself.row(i);
      if ((r.is_merce() || r.is_omaggio()) && !r.is_checked())
      { 
				if (r.get(RDOC_CODARTMAG) == NULL_CODART)
					r.put(RDOC_CODARTMAG, "");
				else
				{
					const TString & codart = r.get("CODART");
					anamag.put("CODART", codart);
					if (anamag.read() == NOERR)
						r.put("CODARTMAG", codart);
					 else
					{
						codalt.put("CODARTALT", codart);
						if (codalt.read() == NOERR)
							r.put("CODARTMAG", codalt.get("CODART"));
					}
					r.checked();
				}
      }
      if (r.is_evadibile() && is_ordine())
      {
        docevaso &= r.is_evasa();
        const TDate dcons = r.get_date(RDOC_DATACONS);
        if (!dcons.ok())
          r.put(RDOC_DATACONS, datacons);
      }               
                        
      if (r.get(RDOC_CODCMS).blank() && r.get(RDOC_FASCMS).blank() && r.get(RDOC_CODCOSTO).blank())
      {
        r.put(RDOC_CODCMS, codcms);
        r.put(RDOC_FASCMS, fascms);
        r.put(RDOC_CODCOSTO, codcos);
      }
    }
    if (is_ordine())
      ((TDocumento *)this)->put(DOC_DOCEVASO, docevaso); // Tutte le righe evase   -> doc evaso
  }                            // Almeno una riga aperta -> doc aperto

  err = TMultiple_rectype::write_rewrite(f, re);
  
  if (!doc_bloccato && err == NOERR)
  {
    if (clifor().occasionale())
    {
      if (get("OCFPI").not_empty())
      {
        TLocalisamfile o(LF_OCCAS);
        TOccasionale & occ = occas();
        
        err = occ.write(o);
        if (err == _isreinsert)
          err = occ.rewrite(o);
      }
    }
    if (_has_provv && tipo().provvigioni() && tipo().stato_provvigioni() <= stato()) 
    {
      const TString16 agente = get(DOC_CODAG);
      if (agente != _old_agente)
      {   
        if (_old_agente.not_empty())                                   
        {
          const int anno = get_int(DOC_ANNO);
          const TString16 codnum = get(DOC_CODNUM);
          const long numdoc = get_long(DOC_NDOC);
          TProvvigioni_agente provv;
          if (provv.read(_old_agente, anno, codnum, numdoc) == NOERR)
            provv.remove();
        }    
        myself._old_agente = agente;
      }
      if (agente.not_empty())
        myself.write_provvigioni();
    }
    if (tipo().statistiche() && _has_stat_ven)
    {
      TString80 key(get(DOC_PROVV)); key << get(DOC_ANNO); key << get(DOC_CODNUM); key << get(DOC_NDOC);
      TStats_agg * st_agg = (TStats_agg *) _docs_to_agg.objptr(key);
      const bool is_nota_credito = tipo().nota_credito();
     
      if (st_agg == NULL)       
      {
        st_agg = new TStats_agg;
        _docs_to_agg.add(key, st_agg, TRUE);
      }
      int i;
      
      for (i = physical_rows(); i > 0; i--)
        if (is_nota_credito)
          st_agg->sub(myself.row(i));
        else
          st_agg->add(myself.row(i));
      st_agg->update();
      for (i = physical_rows(); i > 0; i--)
        if (is_nota_credito)
          st_agg->add(myself.row(i));
        else
          st_agg->sub(myself.row(i));
    }
  }
  return err;  
}
                    // eliminare anche il mov di mag. ??????
int TDocumento::remove(TBaseisamfile& f) const
{
  if (!cancellabile() && !yesno_box("Documento non cancellabile,\nContinuare ugualmente"))
    return NOERR;
  const bool check_movmag = dongle().active(MGAUT) && tipo().mov_mag();
	const bool doc_bloccato = bloccato();
    
  if (check_movmag)
  {
    const long num = get_long("MOVMAG");            
  
    if (num > 0)
    {
      TMov_mag_doc mov;
      TLocalisamfile m(LF_MOVMAG);
      mov.put(MOVMAG_NUMREG, num);   
      while (mov.read(m, _isequal, _testandlock) == _islocked)
        message_box("Movimento di magazzino in uso da parte di un'altro utente");
			if (doc_bloccato)
			{
				const int rows = mov.rows();

				for (int i= 1; i <= rows; i++)
					mov.body()[i].zero(RMOVMAG_TIPORIGA);
				mov.rewrite(m);
			}
			else
				mov.remove(m);
    }
  }
	if (!doc_bloccato)
	{
		if (tipo().statistiche() && _has_stat_ven)
		{
			TString80 key(get(DOC_PROVV)); key << get(DOC_ANNO); key << get(DOC_CODNUM); key << get(DOC_NDOC);
			TStats_agg * st_agg = (TStats_agg *) _docs_to_agg.objptr(key);
     
			if (st_agg != NULL)
			{
				st_agg->update();
				_docs_to_agg.remove(key);
			}
		}
		if (_has_provv && tipo().provvigioni()) 
		{
			const TString8 agente(get(DOC_CODAG));
			const int anno = TDocumento::anno();
			const TString4 codnum(numerazione());
			const long ndoc = numero();
    
			// Legge le provvigioni per questo documento
			_provv_agente->read(agente, anno,codnum,ndoc); 
			// Le rimuove
			_provv_agente->remove();
		}
	}
  return TMultiple_rectype::remove(f);
}

int TDocumento::decimals(bool price) const
{                               
  const TString4 codval(get(DOC_CODVAL));
  const TExchange exc(codval);
  const int ndec = exc.decimals(price);
  return ndec;
}

void TDocumento::flush_rows()
{
  remove_body(LF_RIGHEDOC);
}

TProvvigioni_agente& TDocumento::calc_provvigioni(const bool generata)
{
  CHECK (_provv_agente, "Bad TProvvigione_agente object");

  TString16  agente(get(DOC_CODAG));
  const int  anno = TDocumento::anno();
  TString16  codnum(numerazione());
  const long ndoc = numero();
  TDate      datadoc(data());

  while (_provv_agente->read(agente, anno,codnum,ndoc) == _islocked) // Legge le provvigioni per questo documento
    if (!yesno_box("Dati agente %s in uso da un altro utente. Riprovare?", (const char*) agente))
      return *_provv_agente;

  const TString16 codval(TDocumento::valuta());
  const real change(cambio());
  const real perc = _provv_agente->perc_fatt(); 
  TCurrency_documento  tot_doc(totale_doc(), *this);
  TCurrency_documento  tot_netto(totale_netto(), *this);
  TCurrency_documento  tot_provv(provvigione(), *this);
  TCurrency_documento  provv_fat((tot_provv.get_num() / 100.0) * perc, *this); // Provvigione sul fatturato (rata 0)
//  const int ndec = decimals();
//  provv_fat.round(ndec);
  TCurrency_documento provv_pag = tot_provv - provv_fat;       // Provvigione sul pagato (da suddivere secondo il pagamento)
  const bool valuta = in_valuta();
              
  // Calcolo rate per provvigioni e documento
  TPagamento& pag1 = pagamento(); // Per rate documento
  TPagamento* pag2 = new TPagamento(pag1.code(), datadoc.string()); // Per rate documento
  
  // Rilegge il pagamento, nel caso in cui venga chiamata la scrittura del documento
  // corrente dopo che � stata effettuata una contabilizzazione; la contabilizzazione
  // nel caso di anticipo aggiunge una rata in pi�
  if (pag1.n_rate() > pag2->n_rate())
    pag1.read();
    
  TCurrency_documento totspese(spese(), *this);
  TCurrency_documento totimposte(imposta(), *this);
  TCurrency_documento totimponibili(tot_doc - totimposte - totspese);
  TCurrency_documento anticipo(get_real(DOC_IMPPAGATO), *this);
  
  if (is_nota_credito()) // Se il documento e' una nota di credito, cambia segno
  {                    
    tot_doc = -tot_doc;
    tot_netto = -tot_netto;
    tot_provv = -tot_provv;
    provv_fat = -provv_fat;
    provv_pag = -provv_pag;
    totspese = -totspese;
    totimposte = -totimposte;
    totimponibili = -totimponibili;
  }
  
  // Considera l'anticipo, come in contabilizzazione ed in generazione effetti
  //
  // Un anticipo su di una nota di credito non dovrebbe comunque mai esistere, non ha senso.
  // Se esiste � un errore in inserimento da parte del cliente.
  if (anticipo.get_num() < abs(tot_doc.get_num()))
  {
    TGeneric_distrib d(anticipo.get_num(), decimals());

    d.add(totimponibili.get_num());
    d.add(totimposte.get_num());
    d.add(totspese.get_num());
    
    const TCurrency_documento pagtotimponibili(totimponibili.get_num() - d.get(), *this);
    const TCurrency_documento pagtotimposte(totimposte.get_num() - d.get(), *this);
    const TCurrency_documento pagtotspese(totspese.get_num() - d.get(), *this);

    TCurrency zero(ZERO);
    if (valuta)
    {
      //real val1 = totimponibili * change;
      TCurrency_documento val2(pagtotimposte); val2.change_to_firm_val();
      TCurrency_documento val3(pagtotspese); val3.change_to_firm_val();
      TCurrency_documento val1(tot_doc); val1.change_to_firm_val(); val1 -= val2 - val3;
      pag1.set_total_valuta(pagtotimponibili, pagtotimposte, pagtotspese, val1, val2, val3);
      TCurrency_documento provv_pag_base(provv_pag) ; provv_pag_base.change_to_firm_val();
      pag2->set_total_valuta(provv_pag, zero, zero, provv_pag_base, zero, zero);
    }
    else
    {
      pag1.set_total(pagtotimponibili, pagtotimposte, pagtotspese);
      pag2->set_total(provv_pag, zero, zero);
    }
    pag1.set_rate_auto();
    pag2->set_rate_auto();
  }
  else
  {
    pag1.zap_rate();
    pag2->zap_rate();
  }
  
  const bool is_anticipo = anticipo.get_num() > ZERO;
  
  if (is_anticipo)
  {
    pag1.add_rata();
    TDate first_scad(pag1.data_rata(0));
        
    if (datadoc == first_scad)
      pag1.set_datarata(0, ++first_scad);
    for (int k=pag1.n_rate()-1; k>0; k--)
      pag1.rata(k) = pag1.rata(k-1);

    if (anticipo >= tot_doc)
      anticipo = tot_doc;

    TCurrency_documento anticipo_base(anticipo); anticipo_base.change_to_firm_val();
          
    pag1.set_rata(0, valuta ? anticipo.get_num() : ZERO, anticipo_base.get_num(), datadoc, 1, "", FALSE);
  }
    
  // Crea le nuove rate provvigionali
  const bool isnew = _provv_agente->items() == 0; // Il documento non ha righe provvigionali
  TRate_doc& rd = _provv_agente->rate(anno, codnum, ndoc, isnew ? TRUE : FALSE);
  
  // A questo punto rd e' vuoto: settiamo i dati del documento:
  TToken_string t;
  t.add(anno); t.add(codnum);t.add(ndoc);
  t.add(datadoc.string()); t.add(tot_doc.string());
  t.add(tot_provv.string());t.add(tot_netto.string());
  t.add(codcf());
  t.add(TDocumento::valuta());t.add(change.string());
  t.add(get(DOC_DATACAMBIO));
  rd.set(t);
  
  // Adesso si possono aggiungere le rate (per quelle gia' esistenti sostituisce solo alcuni valori)
  // - Rata 0 : importo rata = 0; importo provvigione = provvigione all'atto della fattura (percentuale sugli agenti)
  //   data scadenza viene settata uguale alla data documento
  //   la provvigione rimanente va suddivisa in rate a seconda del codice pagamento
  // Nel caso si ha un anticipo documento, l'importo della rata non sar� 0 ma eguale all'anticipo stesso
  // Se poi abbiamo il caso in cui l'anticipo paga completamente il documento
  // non avremo alcuna rata e tutto l'importo della provvigione del doc. andr� comunque
  // sulla rata 0, anche se la provvigione sul fatturato � 0 (% a 0)
  
  const int nrate = pag1.n_rate();

  if (nrate == 1 && is_anticipo) // significa che l'anticipo paga tutto ora, quindi provv_fat vale tutta la provvigione del documento
    provv_fat = tot_provv;
  
  const bool first_rata_ok = provv_fat.get_num() != ZERO;
  
  // Impostazione prima rata solo se la provvigione sul fatturato � diversa da 0
  if (first_rata_ok)
  {    
    TRata& rt = rd.row(0,TRUE);
    rt.set_rata(0);  rt.set_datascad(datadoc);  rt.set_tipopag(1);
    rt.set_imprata(anticipo.get_num());  rt.set_impprovv(provv_fat.get_num());
    if (generata)
      rt.set_generata();
  }
  
  // Setta le rate rimanenti
  int i;
  
  for (i = 1; i <= nrate; i++)
  {
    if (i == nrate && is_anticipo)
      break;
      
    const int index = is_anticipo ? i : i - 1;
    TRata& rt = rd.row(first_rata_ok ? i : i - 1, TRUE);
    rt.set_rata(i);
    rt.set_datascad(pag1.data_rata(index));
    rt.set_tipopag(pag1.tipo_rata(index));
    rt.set_imprata(pag1.importo_rata(index,valuta ? TRUE : FALSE));
    rt.set_impprovv(pag2->importo_rata(i-1,valuta ? TRUE : FALSE));
    if (generata)
      rt.set_generata();
  }
  
  // Rimuove eventuali righe in eccesso
  const int rd_items = rd.items(); // Rate precedenti
  for (i = first_rata_ok ? nrate+1 : nrate; i < rd_items; i++)
    rd.remove_rata(i);
  delete pag2;
  return *_provv_agente;
}

int TDocumento::write_provvigioni() 
{ return calc_provvigioni().write(); }

bool TDocumento::in_valuta() const
{
  const TString& val = valuta();
  return is_true_value(val);
}

TCodgiac_livelli & TDocumento::livelli() const 
{
  if (_livelli == NULL) 
    _livelli = new TCodgiac_livelli();
  return *_livelli;
}

TRiga_documento & TDocumento::row(int index)
{                                   
  TRecord_array & b = body();
  const int nrows = b.rows();
  TRiga_documento * r = NULL;
  
  if (index <= nrows)
  {
    r = &((TRiga_documento &) b.row(index, FALSE));
    CHECKD(r, "Riga documento non esistente ", index);
  }         
  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;
}

const TRiga_documento& TDocumento::physical_row(int index) const 
{                                   
  TRecord_array & b = body();
  return (TRiga_documento&)b.row(index, FALSE);
}

long TDocumento::get_next_key(char provv, int anno, const char* codnum) const
{               
  long 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 char* tipodoc)
{
  TTipo_documento* o = (TTipo_documento*)_tipi.objptr(tipodoc);
  if (o == NULL)
  {
#ifdef DBG
    if (tipodoc == NULL || *tipodoc == '\0')
      NFCHECK("Tipo documento nullo");
#endif
    o = new TTipo_documento(tipodoc);
    _tipi.add(tipodoc, o);
  }
  return *o;
}  

const TTipo_documento& TDocumento::tipo() const
{
  TString4 tipodoc(get(DOC_TIPODOC));
#ifdef DBG
  if (tipodoc.blank())   // Test necessario per lavorare anche su dati rovinati
  {
    const TCodice_numerazione& codnum = codice_numerazione();
    tipodoc = codnum.tipo_doc(0);
    yesnofatal_box("Tipo documento nullo su %d %s %ld\nforzato a %s", 
                   get_int(DOC_ANNO), (const char*)get(DOC_CODNUM), get_long(DOC_NDOC), (const char*)tipodoc);
    ((TDocumento*)this)->set_tipo(tipodoc);
  }
#endif
  return tipo(tipodoc);
}  

const TCodice_numerazione& TDocumento::codice_numerazione(const char* numerazione)
{                         
  TCodice_numerazione* o = (TCodice_numerazione*)_numerazioni.objptr(numerazione);
  if (o == NULL)
  {
    o = new TCodice_numerazione(numerazione);
    _numerazioni.add(numerazione, o);
  }
  return *o;
}  

const TCodice_numerazione& TDocumento::codice_numerazione() const
{                         
  return codice_numerazione(numerazione());
}  

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())
    {
      if (strncmp(c, "CODABI", 6) == 0 || strncmp(c, "CODCAB", 6) == 0)
			{
				long cod = get_long(c);
				ok &= (cod == doc.get_long(c));
			}
			else
			{
				campo = get(c);
				ok &= campo == doc.get(c);
			}
    }
  }  
  return ok;
} 

void TDocumento::set_fields(TAuto_variable_rectype & rec)
{                                                        
  if (tipo_valido())
  {          
    TTipo_documento & tipo_doc = (TTipo_documento &) tipo(); 
    const TString& 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 (exp != NULL) // Puo' succedere che sia NULL con dati incoerenti
      {
        if (tot_doc == f->name())
        {   
          TString16 tot_doc_netto(tot_doc);
          tot_doc_netto.insert("_");                          

          const TFixed_string netto_def(exp->string());              
          TExpr_documento netto_exp(netto_def, _numexpr, this);
                                           
          add_field(new TDocumento_variable_field(tot_doc_netto, netto_exp));
        
          if (netto_def == "IMPONIBILI()+IMPOSTE()")
          {
            TExpr_documento tot_exp("IMPONIBILI(1)+IMPOSTE(1)", _numexpr, this);
            add_field(new TDocumento_variable_field(tot_doc, tot_exp));
          }
          else
          {
            TExpr_documento tot_exp(format("%s + _BOLLI(%s)", (const char *) tot_doc_netto,
                                           (const char *) tot_doc_netto), _numexpr, this);
            add_field(new TDocumento_variable_field(tot_doc, tot_exp));
          }
        }
        else
        {
          exp->set_doc(this);     
          add_field(new TDocumento_variable_field(f->name(), *exp));
        }
      }
    }                      
  }
}

real TDocumento::imponibile(bool spese, int ndec) const
{
  real val;                      
  if (physical_rows() > 0)
  {
    TAssoc_array & table = ((TDocumento *)this)->tabella_iva();
    for (TRiepilogo_iva * ri = (TRiepilogo_iva *) table.get(); ri != NULL;
         ri = (TRiepilogo_iva *) table.get())
      val +=  ri->imponibile(spese);
  
    if (ndec == AUTO_DECIMALS)
      ndec = decimals();
    val.round(ndec);
  }
  return val;
}

void TDocumento::calc_iva_fattura_commerciale()
{
  // Calcolo iva per fatture commerciali:
  // 1) effettua la sommatoria di tutti i real per ogni elementi di _tabella_iva in un TRiepilogo_iva unico
  // 2) azzera _tabella_iva
  // 3) scorpora in % a seconda di cio' che e' indicato in configurazione, e mette i nuovi elementi in _tabella_iva
  // 4) per ogni nuovo elemento di _tabella_iva, ricalcola la relativa imposta.
  TAssoc_array & table = _tabella_iva;
  TRiepilogo_iva t;
  static TString_array tabella_ripartizione;
  const int ndec = decimals();
  
  if (tabella_ripartizione.items() == 0)
  {
    TConfig cnf(CONFIG_STUDIO, "ve");
    for (int k = 1; k <= MAX_IVA_SLICES; k++)
    { 
      TToken_string* tt = new TToken_string();
      tt->add(cnf.get("EXCLUDE_PERC", NULL, k));
      tt->add(cnf.get("EXCLUDE_IVA", NULL, k));
      tabella_ripartizione.add(tt);
    }
  }

  // 1)
  table.restart();
  for (TRiepilogo_iva * ri = (TRiepilogo_iva *) table.get(); ri != NULL;
       ri = (TRiepilogo_iva *) table.get())
  {
    t.imp()           += ri->imp();
    t.imp_spese()     += ri->imp_spese();
    t.imp_spese_row() += ri->imp_spese_row();
    t.iva_spese()     += ri->iva_spese();
    t.iva_sconto()    += ri->iva_sconto();
    t.sconto_perc()   += ri->sconto_perc();
    t.sconto_imp()    += ri->sconto_imp();
  }
  // 2)
  table.destroy();
    
  // 3)
  TArray tda;
    
  tda.add(new TDistrib(t.imp(), ndec));
  tda.add(new TDistrib(t.imp_spese(), ndec));
  tda.add(new TDistrib(t.imp_spese_row(), ndec));
  tda.add(new TDistrib(t.iva_spese(), ndec));
  tda.add(new TDistrib(t.iva_sconto(), ndec));
  tda.add(new TDistrib(t.sconto_perc(), ndec));
  tda.add(new TDistrib(t.sconto_imp(), ndec));
  
  int k;
    
  for (k = 0; k < MAX_IVA_SLICES; k++)
    for (int j = 0; j < 7; j++)
    {
      const real p (tabella_ripartizione.row(k).get(0));
      TDistrib& td = (TDistrib&) tda[j];
      td.add(p);
    }

  // 4)
  for (k = 0; k < MAX_IVA_SLICES; k++)
  {
    TString16 cod(tabella_ripartizione.row(k).get(1));
    cod.trim();
    
    if (cod.empty())
      continue;
        
    const TCodiceIVA civa(cod);
    TRiepilogo_iva * rp = (TRiepilogo_iva *) table.objptr(cod);
    if (rp == NULL)
    {
      rp = new TRiepilogo_iva(civa);
      table.add(cod, rp); 
    }
      
    for (int j = 0; j < 7; j++)
    {
      TDistrib& td  = (TDistrib&) tda[j];
      const real rr = td.get();
        
      switch (j)
      {
        case 0:
          rp->imp() = rr;
          rp->iva() = civa.imposta(rr, ndec);
          break;
        case 1:
          rp->imp_spese() = rr;
          break;
        case 2:
          rp->imp_spese_row() = rr;
          break;
        case 3:
          rp->iva_spese() = rr;
          break;
        case 4:
          rp->iva_sconto() = rr;
          break;
        case 5:
          rp->sconto_perc() = rr;
          break;
        case 6:
          rp->sconto_imp() = rr;
          break;
        default: break;
      }        
    }
  }
}

void TDocumento::update_tabella_iva()
{
  const int items = rows();
  TAssoc_array & table = _tabella_iva;

  if (table.items() > 0 || items == 0)
  {
    if (items == 0)
      table.destroy();
    return;                  
  }

  real tot_doc;
  real tot_sconti;
  real tot_sconti_perc;
  const bool doc_al_lordo = tipo().calcolo_lordo();
  const int ndec = decimals();
  const bool fatt_comm = tipo().fattura_commerciale();
 
  TString4 codiva_es;               
  iva_esente(codiva_es);                       
  for (int i = items; i > 0; i--)
  {
    const TRiga_documento& r = row(i);
    const real imponibile = r.imponibile(doc_al_lordo);
    
    tot_doc += imponibile;
    if (r.is_sconto())         
    {
      tot_sconti += imponibile;    
      if (r.is_sconto_perc())
        tot_sconti_perc += imponibile;
    }
    else
      if (!r.is_descrizione())
      {
        const real imposta = doc_al_lordo ? ZERO :r.imposta(FALSE);
// Aggiorna o aggiunge l'elemento se non esiste    
        const TCodiceIVA & iva = r.iva(); 
        const TString4 cod(iva.codice());
        if (cod.full())
        {
          TRiepilogo_iva * aliquota = (TRiepilogo_iva *) table.objptr(cod);
          if (aliquota == NULL)
          {
            aliquota = new TRiepilogo_iva(iva);
            table.add(cod, aliquota); 
          }
          aliquota->imp() += imponibile;
          if (r.is_spese() && iva.tipo().not_empty())
            aliquota->imp_spese_row() += imponibile;
          aliquota->iva() += imposta;
        }  
        tot_doc += imposta;
      }
  }  
  
  if (table.items() == 0)
    return;
    
  if (tot_sconti != ZERO)
  {
    TGeneric_distrib d(tot_sconti, ndec);
    real tot_sconti_imp = tot_sconti - tot_sconti_perc;
    
    table.restart();
    
    TRiepilogo_iva * ri;
    
    for (ri = (TRiepilogo_iva *) table.get(); ri != NULL;
         ri = (TRiepilogo_iva *) table.get())
     d.add(ri->imp() - ri->imp_spese_row());
  
    table.restart();
    for (ri = (TRiepilogo_iva *) table.get(); ri != NULL;
         ri = (TRiepilogo_iva *) table.get())
    {     
      const TCodiceIVA & ci = ri->cod_iva();    
      const real i(d.get());
      real & imponibile = ri->imp();
      imponibile += i;
      
      TGeneric_distrib s(i, ndec);
      
      s.add(tot_sconti_imp);
      s.add(tot_sconti_perc);
      
      ri->sconto_imp() = s.get();
      ri->sconto_perc() = s.get(); 
      
      real & iva = ri->iva();   
      
      if (doc_al_lordo)
      {
        const real imposta(ci.imposta(i, ndec));

        TGeneric_distrib iva(imposta, ndec);
        iva.add(tot_sconti_imp);
        iva.add(tot_sconti_perc);
      
        imponibile += imposta;
        ri->sconto_imp() += iva.get();
        ri->sconto_perc() += iva.get(); 

        tot_doc += imposta;
      }
      else
      {
        const real imposta(ci.imposta(i, ALL_DECIMALS));
  
        iva += imposta;
        ri->iva_sconto() = ci.imposta(i, ndec);
        tot_doc += imposta;
      } 
    }
  }
                
  if (fatt_comm)
    calc_iva_fattura_commerciale();

  real val = spese_incasso(ALL_DECIMALS, doc_al_lordo ? _lordo : _netto);
  
  if (!val.is_zero())
  {
    const TString& codiva = codiva_es.full() ? (const TString &) codiva_es : codiva_spese();
    TRiepilogo_iva* ri = (TRiepilogo_iva*)table.objptr(codiva);
    if (ri == NULL && codiva.full())
    {
      ri = new TRiepilogo_iva(TCodiceIVA(codiva));
      table.add(codiva, ri);
    }
    if (ri != NULL)  
      ri->imp_spese() += val;
    tot_doc += val;
    
    if (!doc_al_lordo)
    {
      val = spese_incasso(ALL_DECIMALS, _imposta);
      if (ri != NULL)  
        ri->iva_spese() += val;
      tot_doc += val;
    }
  }

  const real rit = ritenute();
	val = bolli(real(tot_doc - rit), ALL_DECIMALS, doc_al_lordo ? _lordo : _netto);
  if (val != ZERO)
  {
    const TString& codiva = codiva_bolli();
    if (codiva.full())
    {
      TRiepilogo_iva* ri = (TRiepilogo_iva*)table.objptr(codiva);
      if (ri == NULL)
      {
        ri = new TRiepilogo_iva(TCodiceIVA(codiva));
        table.add(codiva, ri);
      }
      if (!doc_al_lordo)
      {
        real valiva = bolli(real(tot_doc - rit), ALL_DECIMALS, _imposta);
        ri->iva_spese() += valiva;
        tot_doc += valiva;
      }
      ri->imp_spese() += val;
    }

    tot_doc += val;
  }                       
  
// SCORPORO
  if (doc_al_lordo)
  {
    table.restart();
    for (TRiepilogo_iva* ri = (TRiepilogo_iva*)table.get(); ri != NULL; ri = (TRiepilogo_iva*)table.get())
    {                               
      const TCodiceIVA& iva = ri->cod_iva();

      ri->iva() = iva.scorpora(ri->imp(), ndec);    
      ri->iva_spese() = iva.scorpora(ri->imp_spese(), ndec);
      iva.scorpora(ri->imp_spese_row(), ndec);

      ri->iva_sconto() = iva.scorpora(ri->sconto_imp(), ndec);
      ri->iva_sconto() += iva.scorpora(ri->sconto_perc(), ndec);
    }
  }
}

real TDocumento::imposta(bool spese, int ndec) const
{                       
  real val = ZERO;     
  if (physical_rows() > 0)
  {
    TAssoc_array & table = ((TDocumento *)this)->tabella_iva();
  
    if (ndec == AUTO_DECIMALS)
      ndec = decimals();
  
    for (TRiepilogo_iva * ri = (TRiepilogo_iva *) table.get(); ri != NULL;
         ri = (TRiepilogo_iva *) table.get())
    {    
      real iva = ri->imposta(spese);

      if (ndec == 0)
      {
        if (iva < ZERO)
          iva.floor(ndec);
        else               
          iva.ceil(ndec);
      }
      else
        iva.round(ndec);
      val += iva;
    }
  }
  return val;
}

real TDocumento::totale_doc() const
{
/*  A pag.1 c'e' scritto di non fare cosi'
  const TString16 field(tipo().totale_doc());

  if (field.not_empty())
    return get_real(field);
  else
  {
    real r = imponibile() + imposta();
    const int ndec = decimals();
    
    r += spese_incasso(ndec);
    r += bolli(real(r - ritenute()), ndec);
    return r;
  }
*/
  // Cosi' e' piu' ordinato, strutturato e veloce  
  real r;
  const TString& field = tipo().totale_doc();
  if (field.blank())
  {
    const int ndec = decimals();
    r = imponibile() + imposta();
    r += spese_incasso(ndec);
    r += bolli(real(r - ritenute()), ndec);
  }
  else
    r = get_real(field);

  return r;
}

real TDocumento::totale_netto() const
{
  const TString& field = tipo().totale_netto();
  if (field.not_empty())
    return get_real(field);
  else
    return imponibile() + imposta();
}

real TDocumento::basesconto() const
{
  const TString& field = tipo().basesconto();

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

real TDocumento::spese() const
{
  const TString& field = tipo().spese();
  if (field.not_empty())
    return get_real(field);
  else
    return ZERO;
}

real TDocumento::ritenute(const char tipo, bool lordo, int ndec) const
{
  real val;
  for (int i = rows() ; i > 0; i--)
  {
    TRiga_documento &r = ((TRiga_documento &) ((TDocumento *)this)->row(i));
    
    val += r.ritenuta(tipo, lordo, ndec);
	}
	return val;
}

TPagamento& TDocumento::pagamento()
{ 
  const char tipocf = get_char(DOC_TIPOCF);
  const long codcf = get_long(DOC_CODCF);
  _pag.set_clifo(codcf, tipocf);

  TDate data_in = get_date(DOC_DATAINSC);
  if (data_in.empty())     
    data_in = get_date(DOC_DATADOC);

  const TString8 codpag(get(DOC_CODPAG));
  if (codpag != _pag.code())
  {                        
    _pag.set_code(codpag);
    _pag.read();
    _pag.set_inizio(data_in); // Perche' rispetta rate TRUE?
  }
  else
  {
    if (data_in != _pag.get_datadoc())
      _pag.set_inizio(data_in); // Perche' rispetta rate TRUE?
  }    

  return _pag;                                                        
}                                   

real TDocumento::provvigione(int ndec) const
{ 
  TString16 field = agente().campoprovv();
  if (field.empty())
    field = tipo().totprovv();

  real val;     
  if (field.not_empty())
    val = get_real(field);
  else
  {
    if (ndec == AUTO_DECIMALS)
      ndec = decimals();
    for (int i = rows(); i > 0; i--)
      val += ((TRiga_documento &) ((TDocumento *)this)->row(i)).provvigione(ndec);
  }
  return val;
}

real TDocumento::valore(bool totale, int ndec) const
{
  real val;
  for (int i=rows(); i>0; i--)
  {
    TRiga_documento &r = ((TRiga_documento &) ((TDocumento *)this)->row(i));
    if (r.is_merce() || r.is_spese() || r.is_prestazione())
      val += r.valore(totale, ndec); 
  }
  return val;
}  

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

const TString& TDocumento::get_str(const char* fieldname) const
{
  if (_dirty_deny && variable_field(fieldname) != NULL)
    (bool&) _dirty_deny = false;
  
  return TMultiple_rectype::get_str(fieldname);
}

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

TCli_for & TDocumento::clifor() const
{ 
  const char tipo = tipocf();
  const long codice = codcf();
  if (_cli_for.empty() || _cli_for.tipo() != tipo || _cli_for.codice() != codice)
    ((TDocumento *) this)->_cli_for.read(tipo, codice);
  return (TCli_for &)_cli_for;
}

TOccasionale & TDocumento::occas() const
{
  const TString80 occ_code(cod_occas());

  if (_occas.empty() || occ_code != _occas.codice())
  { 
    TLocalisamfile o(LF_OCCAS);

    ((TDocumento *) this)->_occas.zero();
    ((TDocumento *) this)->_occas.put(OCC_CFPI, occ_code);

    TRectype oc(_occas);

    if (((TDocumento *) this)->_occas.read(o) != NOERR)
      ((TDocumento *) this)->_occas = oc;
  }
  return (TOccasionale &) _occas;
}
                                        
const TAgente & TDocumento::agente() const
{        
	if (_agenti == NULL)
		_agenti = new TAgenti_cache;
  return _agenti->agente(get(DOC_CODAG));
}                                     

TDocumento & TDocumento::copy(const TDocumento & d) 
{                                
  TMultiple_rectype::operator=((TMultiple_rectype &)d); 
  reset_fields(*this);
  set_fields(*this);
	put(DOC_SPESEUPD, d.get(DOC_SPESEUPD));
	_spese_updated =  d._spese_updated;
  for (int i = physical_rows(); i > 0; i--)
  {
    TRiga_documento & r = row(i);
    r.set_doc(this);
    r.set_fields(r);
  }
  set_riga_sconto();
  if (is_fattura())
    set_riga_esenzione();
  _occas = d.occas();
  return *this;
}
                                   
TRectype & TDocumento::operator =(const TRectype & r) 
{
  return TMultiple_rectype::operator=(r); 
}
                                   
TRectype & TDocumento::operator =(const char * r) 
{
  return TMultiple_rectype::operator=(r); 
}

TRecord_array& TDocumento::body(int logicnum) const
{        
  const bool reset_data_cons = loaded_rows(logicnum) == 0;

  if (reset_data_cons)
    ((TDocumento*)this)->_dirty_deny = true;

  TRecord_array& r = TMultiple_rectype::body(logicnum);

  if (reset_data_cons)
  {
    const TDate datacons(get(DOC_DATACONS));
    const TString80 codcms(get(DOC_CODCMS));
    const TString80 fascms(get(DOC_FASCMS));
    const TString80 codcos(get(DOC_CODCOSTO));
    const bool order = is_ordine();   

    for (int i = r.last_row(); i > 0; i = r.pred_row(i))
    {
      TRectype& rec = r[i];
      TRecfield dcons(rec, RDOC_DATACONS);
      if (order && datacons == dcons)
        dcons = "";
      TRecfield ccms(rec, RDOC_CODCMS);
      TRecfield fcms(rec, RDOC_FASCMS);
      TRecfield ccos(rec, RDOC_CODCOSTO);
      if (codcms == ccms && fascms == fcms && codcos == ccos)
      {
        ccms = "";
        fcms = "";
        ccos = "";
      }
    }
    ((TDocumento*)this)->_dirty_deny = false;
  }
  return r;
}

void TDocumento::update_spese_aut(TString_array & spese_aut, bool preserve_old, TSheet_field * sh)
{
  if (_spese_updated)
    return;

  const bool interactive = sh != NULL;
    
  if (tipo().spese_aut())
  {                
    const int nrows = physical_rows();
    int i;
    
    for (i = nrows; i > 0; i--)     
    {
      TRiga_documento & r = row(i);
      bool tipo_spese = r.get("GENTIPO").empty();
    
      if (r.tipo().tipo() == RIGA_SPESEDOC && r.is_generata() && tipo_spese)
      { 
        if (preserve_old)
          return;
        destroy_row(i, TRUE); 
        if (interactive)
          sh->destroy(i - 1);
      }
    }
    
		TString4 cod_iva_cli;
    const int nspese = spese_aut.items();

    if (nspese > 0)
    {
      TString16 key; key.format("%c|%ld", get_char(DOC_TIPOCF), get_long(DOC_CODCF));
      const TString4 cod_iva_cli = cache().get(LF_CFVEN, key, "ASSFIS");

      TSpesa_prest sp;
      for (i = 0; i < nspese; i++)
      {                     
        const TString& s = spese_aut.row(i);
        
        if (sp.read(s) != NOERR)
          message_box("Codice spesa  %s  assente", (const char *) s);
        else 
        {
          const TString4 tipo(sp.tipo_riga());
          TRiga_documento& riga = new_row(tipo);
      
          riga.put(RDOC_CODART, s);
          riga.generata();
          riga.put(RDOC_DESCR, sp.descrizione());
          switch (sp.tipo())
          {                 
            case 'Q':
              {                              
                real qta = sp.qta();
                if (qta == ZERO)
                  qta = UNO;                                       
                riga.put("QTA", qta);                 
              }
              // Continua perche' e' quantita' e valore
            case 'V':
              {
                const real cambio = get_real(DOC_CAMBIO);
                const TString4 valuta = get(DOC_CODVAL);
                const exchange_type controeuro = get_bool(DOC_CONTROEURO) ? _exchange_contro : _exchange_base;
                real prezzo = sp.prezzo();
          
                sppr_calc(sp, valuta, cambio, prezzo, controeuro);
                riga.put(RDOC_PREZZO, prezzo);
                riga.put(RDOC_UMQTA, sp.um());
              }
              break;
            case 'P':
            default:
                riga.put(RDOC_QTA, sp.perc());
              break;
          }
          if (cod_iva_cli.empty())
            riga.put(RDOC_CODIVA, sp.cod_iva());
          else
            riga.put(RDOC_CODIVA, cod_iva_cli);

          riga.put(RDOC_CODCOSTO, sp.cdc());
          riga.put(RDOC_CODCMS, sp.cms());
          riga.put(RDOC_FASCMS, sp.fase());

          if (interactive)
          {              
            const int nrow = sh->insert(-1, FALSE);
            riga.autoload(*sh);        
            sh->check_row(nrow);  
          }
        }         
      }
    }                                                   
  }
  _spese_updated = true;
}
                                   
real TDocumento::calc_conai_qta(int type)
{                                     
  const char * const __conai_art_names[] = {"CONACC", "CONALL", "CONCAR", "CONPLA", "CONLEG", "CONVET"};
	const int nrows = physical_rows();
	real qta;

	for (int i = nrows; i > 0; i--)     
	{
		const TRiga_documento& r = row(i);
		TArticolo_giacenza* art = r.articolo();
		if (art != NULL)
		{
			const TString4 um = r.get(RDOC_UMQTA);
			real row_qta = art->convert_to_um(r.get_real(RDOC_QTA), NULL, um);

			const bool has_formula = ((TTipo_riga_documento &)r.tipo()).has_formula(__conai_art_names[type]);
			if (has_formula)
				row_qta *= r.get_real(__conai_art_names[type]);
			else
				row_qta *= art->get_real(__conai_art_names[type]);
  		if (row_qta > ZERO) 
				qta += row_qta;
		}
	}  
  return qta;
}               

void TDocumento::update_conai()
{                   
  if (tipo().add_conai())
  {
    const bool cli_add_conai = clifor().vendite().get_bool("ADDCONAI");
    const char* conai_cod[6] = { "CODACC", "CODALL", "CODCAR", "CODPLA", "CODLEG", "CODVET" };
    const char* conai_mat[6] = { "Acciaio", "Alluminio", "Carta", "Plastica", "Legno", "Vetro" };
	  const char * const __conai_cf_names[] = {"ESACC", "ESALL", "ESCAR", "ESPLA", "ESLEG", "ESVET"};
    TString_array conai_sp(6);   // Codici spesa conai
		const TDate datadoc = get(DOC_DATADOC);
		const TDate dataes = clifor().vendite().get(CFV_DATAECONAI);
		bool esponi_esenti = false;
	
    {
      TConfig c(CONFIG_DITTA, "ve");    
      for (int i = 0; i < 6; i++)
        conai_sp.add(c.get(conai_cod[i]));
			
			esponi_esenti = c.get_bool("ESPONIESENTI");
    }                          
    bool updated[6] = {false,false,false,false,false,false}; 
    
    const int nrows = physical_rows();
    int i;
    
    for (i = nrows; i > 0; i--)     
    {
      TRiga_documento& r = row(i);   
      const bool tipo_conai = r.get_char("GENTIPO") == 'C';
      
      // Elimina righe generate
      if (tipo_conai)
      {     
				const TString& cod = r.get(RDOC_CODART);
				const int pos = conai_sp.find(cod);
        
				if (pos >= 0)
				{  
					if (cli_add_conai)
					{
						real perc_esenz = clifor().vendite().get_real(__conai_cf_names[pos]);
						real qta = calc_conai_qta(pos);
						if (dataes.ok() && datadoc > dataes)
							perc_esenz = ZERO;
						const bool cli_esente = (esponi_esenti) && (perc_esenz == CENTO);

						if (!cli_esente)
							qta *= (CENTO - perc_esenz) / CENTO;  // More precise
						if (qta > ZERO) 
						{
							r.put(RDOC_QTA, qta);  
							if (cli_esente)
								r.zero(RDOC_PREZZO);
						}
						else
							destroy_row(i, true); 
					}
					else
						destroy_row(i, true); 
					updated[pos] = true;
				}
      }
    } 
    
    // Genera nuove righe
    if (cli_add_conai)
    {  
      const TString4 cod_iva_cli = clifor().vendite().get("ASSFIS");
      TSpesa_prest sp;
  
			for (i = 0; i < 6; i++)
			{
				if (!updated[i])
				{            
					real perc_esenz = clifor().vendite().get_real(__conai_cf_names[i]);
					real qta = calc_conai_qta(i);
					const bool cli_esente = (esponi_esenti) && (perc_esenz == CENTO);

					if (!cli_esente)
						qta = qta * (CENTO - perc_esenz) / CENTO;  // More precise
					if (qta > ZERO)
					{            
						const TString & s = conai_sp.row(i);

						if (sp.read(s) != NOERR)
							message_box("Il codice spesa CONAI %s specificato nei parametri ditta e' assente: '%s'", 
													conai_mat[i], (const char*)s);
						else 
						{
							const TString4 tipo = sp.tipo_riga(); 
							TRiga_documento& riga = new_row(tipo);
        
							riga.put("CODART", s);
							riga.generata();
							riga.put("GENTIPO", "C");
							riga.put("DESCR", sp.descrizione());
							riga.put("QTA", qta);                 

							const real cambio = get_real(DOC_CAMBIO);
							const TString16 valuta = get(DOC_CODVAL);
							const bool controeuro = get_bool(DOC_CONTROEURO);
							real prezzo = cli_esente ? ZERO : sp.prezzo();
            
							sppr_calc(sp, valuta, cambio, prezzo, controeuro ? _exchange_contro : _exchange_base);
							riga.put("PREZZO", prezzo);
							riga.put("UMQTA", sp.um());
							if (cod_iva_cli.empty())
								riga.put("CODIVA", sp.cod_iva());
							else
								riga.put("CODIVA", cod_iva_cli);
						}         
					}
				}  
			}  
    }  
  }
}
                                   
bool TDocumento::is_evaso() const
{
  bool ok = is_ordine() || is_bolla() || is_generic();
  for (int r = 1; ok && r <= physical_rows(); r++)
  {
    const TRiga_documento& riga = physical_row(r);
    if (riga.is_evadibile())
      ok = riga.is_evasa();
  }
  return ok;
}

bool TDocumento::is_nota_credito() const
{ 
  bool swap = FALSE;
  
  // Controlla prima l'esistenza del flag nota-credito sul tipo documento;
  // se non e' settato controlla la causale
  if (tipo().nota_credito())
    swap = TRUE;
  else
  {
    const TString16 codcaus(tipo().causale());
    if (codcaus.not_empty())
    {
      TLocalisamfile caus(LF_CAUSALI);
      TLocalisamfile rcaus(LF_RCAUSALI);
      TCausale c(codcaus, data().year());
      const char sez = c.sezione_clifo();
      swap = ((c.reg().iva() == iva_vendite) ^ (sez == 'D'));
    }
  }
  return swap;
}  

TCurrency_documento::TCurrency_documento(const real& num, const TDocumento & doc, bool price)
         : TCurrency(ZERO, "", ZERO, _exchange_base, price)
{           
  const TString16 val(doc.get(DOC_CODVAL));  
  const bool controeuro = doc.get_bool(DOC_CONTROEURO);
  force_value(val, doc.get_real(DOC_CAMBIO), controeuro ? _exchange_contro : _exchange_base);
  set_num(num);
}

int TDocumento::set_row_ids()
{
  const int phrw = physical_rows();
  long maxid = 0L;
  int first_needed = 0, r;
  for (r = 1; r <= phrw; r++)
  {
    const TRiga_documento& row = physical_row(r);
    const long id = row.get_long(RDOC_IDRIGA);
    if (id > maxid) 
      maxid = id;
    else
    {
      if (first_needed == 0)
        first_needed = r;
    }
  }
  if (first_needed > 0)
  {
    for (r = first_needed; r <= phrw; r++)
    {
      TRiga_documento& row = (TRiga_documento&)physical_row(r);
      const long id = row.get_long(RDOC_IDRIGA);
      if (id <= 0)
        row.put(RDOC_IDRIGA, ++maxid);
    }
  }  
  return phrw;
}

const TRiga_documento* TDocumento::get_row_id(long id) const
{
  for (int r = physical_rows(); r > 0; r--)
  {
    const TRiga_documento& row = physical_row(r);
    if (row.get_long(RDOC_IDRIGA) == id)
      return &row;
  }
  return NULL;
}

int TDocumento::tipo_riclassificato() const
{
	int tipo_riclassificato = tipo().tipo();
	if (tipo_riclassificato == TTipo_documento::_altro)
	{
		const TCodice_numerazione& num = codice_numerazione();
		if (num.fattura_emettere_ricevere())
			tipo_riclassificato = TTipo_documento::_bolla;
		else
			tipo_riclassificato = TTipo_documento::_fattura;
	}
	return tipo_riclassificato;
}