#include "halib.h"
#include "hacnvlib.h"
#include "hacnv200a.h"

#include <applicat.h>
#include <automask.h>
#include <defmask.h>
#include <execp.h>
#include <progind.h>
#include <reprint.h>
#include <reputils.h>
#include <tabutil.h>
#include <utility.h>

#include <causali.h>
#include <pconti.h>
#include <mov.h>
#include <rmov.h>
#include <rmoviva.h>
#include <rcausali.h>

const char* const APPNAME = TR("Conversione movimenti"); 

///////////////////////////////////////////////////////////
// Movimenti
///////////////////////////////////////////////////////////

class THardy_movimenti : public THardy_transfer
{
  int _anno;              // parametri per la query
  TDate _dadata, _adata;  // parametri per la query
  TConfig* _conf;         // ini in compilazione
  long _kmovcont;         // movimento contabile in esame
  TArray* _righeiva;      // array dele righe iva hardy
  TArray* _righecont;     // array delle righe contabili hardy
  TAssoc_array* _ivaind;   // array dei codici iva con % di indetraibilità
  TAssoc_array* _ivaoma;   // array dei codici iva per gli omaggi

protected:
  bool scrivi_righe();
  bool scrivi_righecont();
  bool test_movcont();
  bool conto_is_costoricavo(const int gr);
  bool test_moviva();
  void conto2campo(const TString& hd_tipoc, const TString& hd_key, TString4& tipoc, int& gr, int& co, long& so);
  void rec2ini(const TRectype& rec);
  void recset2rec(const TODBC_recordset& recset, TRectype& rec, const TString_array& lista_campi);
  real get_imponibile(const TRectype& rec);

public:
  virtual bool trasferisci();
  THardy_movimenti(const int anno, const TDate dadata, const TDate adata);
};

// carica il record campo con il record hardy in base alla configurazione 
void THardy_movimenti::recset2rec(const TODBC_recordset& recset, TRectype& rec, const TString_array& lista_campi)
{
	TString campo_dest, campo_orig, valore, str;
	FOR_EACH_ARRAY_ROW(lista_campi,i,row)
	{
		row->get(0, campo_dest); 
		row->get(1, campo_orig);
		if (campo_orig.full())
		{
			if (campo_orig[0] == '_')
			{
        if (campo_orig.starts_with("_SCONTO")) // è uno sconto (ca..o!)
        {
          valore.cut(0);
          real sconto;
          TString8 field;
        	for (int i = 1; i < 6; i++)
          {
            field.format("Sconto%1d",i);
            sconto = get_real(field);
            sconto.round(2);
            if (sconto != ZERO)
            {
              valore << sconto.string();
              valore << "+";
            }
          }
          if (valore.len()>0)
            valore = valore.left(valore.len()-1);
        } else
        if (campo_orig.starts_with("_REAL")) // è un real
        {
          const TString80 campo = campo_orig.after(','); 
          real r = recset.get(campo).as_real();
          valore = r.string();
        } else
        if (campo_orig.starts_with("_ROUND")) // arrotondo a due decimali
        {
          const TString80 campo = campo_orig.after(','); 
          real contenuto = recset.get(campo).as_real();
          contenuto.round(2);
          valore = contenuto.string();
        } else
        if (campo_orig.starts_with("_FISSO")) // valore fisso indicato in configurazione 
        {
  			  valore = campo_orig.after(','); 
          valore.trim();
        } else
				if (campo_orig.starts_with("_STREXPR")) // formato _STREXPR, espressione
				{
          TExpression expr(campo_orig.after(','), _strexpr);
          for (int v = 0; v < expr.numvar(); v++)
          {
            const char* varname = expr.varname(v);
            expr.setvar(v, recset.get(varname).as_string());
          }
  			  valore = expr.as_string();
          valore.trim();
				}	else 
				if (campo_orig.starts_with("_TAB")) // formato _TAB,<tabella da leggere>,<valore CODTAB>, <campo da leggere>
				{
  				TToken_string elabora(campo_orig, ',');
					const TString4 tab = elabora.get(1); // tabella da leggere
          const TString16 codtab = recset.get(elabora.get()).as_string();
					const TString16 campotab = elabora.get();
					valore = cache().get(tab, codtab, campotab);
        } else
				if (campo_orig.starts_with("_TRADUCI"))
        {
          const TString80 campo = campo_orig.after(','); 
          const TString80 contenuto = recset.get(campo).as_string();
         	TConfig& ini = config();
          valore = ini.get(contenuto,campo);
        }
        else
          valore.cut(0);
      }
      else
        valore = recset.get(campo_orig).as_string();
			rec.put(campo_dest, valore);
		}
	}
}

// calcola conto campo a partire da conto hardy
void THardy_movimenti::conto2campo(const TString& hd_tipoc, const TString& hd_key, TString4& tipoc, int& gr, int& co, long& so)
{
  TConfig& ini = config();
  char tipocc = hd_tipoc[0];
  switch (tipocc)
  {      
  case 'S':
    {
      tipoc = " ";
      hd_key2conto(hd_key, gr, co, so);
    }
    break;
  case 'C':
    {       
      tipoc = "C";
      so = hd_key2cli(hd_key);
      TToken_string key(tipoc);
      key.add(so);
      const TRectype rec_cf = cache().get(LF_CLIFO, key);
      gr = rec_cf.get_int(CLI_GRUPPO);
      co = rec_cf.get_int(CLI_CONTO);
      if (gr == 0)
      {
        gr = ini.get_int("CLI_GRUPPO", "Parametri");
        co = ini.get_int("CLI_CONTO", "Parametri");
      }
    }
    break;
  case 'F':
    {
      tipoc = "F";
      so = hd_key2for(hd_key);
      TToken_string key(tipoc);
      key.add(so);
      const TRectype rec_cf = cache().get(LF_CLIFO, key);
      gr = rec_cf.get_int(CLI_GRUPPO);
      co = rec_cf.get_int(CLI_CONTO);
      if (gr == 0)
      {
        gr = ini.get_int("FOR_GRUPPO", "Parametri");
        co = ini.get_int("FOR_CONTO", "Parametri");
      }
    }
    break;
  default:
    break;
  }
}

// verifica in configurazione se  il conto è costo o ricavo
bool THardy_movimenti::conto_is_costoricavo(const int gr)
{
  TConfig& ini = config();
  const int costi = ini.get_int("COSTI_GRUPPO", "Parametri");
  const int ricavi = ini.get_int("RICAVI_GRUPPO", "Parametri");
  return ((gr == costi) || (gr == ricavi));
}

// verifica se il movimento è iva e nel caso riempie array delle righe iva
bool THardy_movimenti::test_moviva()
{
  // verifico se è un movimento iva: esiste un record in MovIvaT
  TString query;
  query << query_header();
  query << "SELECT * "
	         "FROM dbo.MovIvaT "
           "WHERE KMovconT=";
  query << _kmovcont;
  TODBC_recordset recset(query);
  real totdoc = ZERO;
  long kregivat = -1;
  if (recset.items() > 0)
  {
    bool ok=recset.move_first();
    if (ok)
    {
      kregivat = recset.get("KRegivaT").as_int(); 
      // aggiorna_testata movimento già scritta su ini con i nuovi dati di testata
      const TString& key = recset.get("IdConto").as_string(); 
      TString4 hdtipoc = recset.get("IdContoTp").as_string();
      TString4 tipoc = " ";
      int gr, co;
      long so;
      gr = 0;
      co = 0;
      so = 0;
      conto2campo(hdtipoc, key, tipoc, gr, co, so);
      _conf->set(MOV_TIPO, tipoc);
      _conf->set(MOV_CODCF, so);
		  totdoc = recset.get("TotDocumento").as_real();
      if (totdoc == ZERO)
        _conf->set("SOLAIVA", "X");
      _conf->set(MOV_TOTDOC, totdoc.string(0,2));
    }
  }
  // leggo le righe iva e costrisco array corrispondente
  TString_array lista_campi_righeiva;
  TConfig& ini = config();
	ini.list_variables(lista_campi_righeiva, true, "RMOVIVA", true);
  TString query_righe;
  query_righe << query_header();
  query_righe << "SELECT * "
           "FROM dbo.MovIva "
           "WHERE KRegivaT=";
  query_righe << kregivat;
  TODBC_recordset recset_righe(query_righe);
  _righeiva->destroy();
  TLocalisamfile rmoviva(LF_RMOVIVA);
  TRectype& rec_rmoviva = rmoviva.curr();
  real totdoc_calc = ZERO;
  for (bool ok=recset_righe.move_first();ok;ok=recset_righe.move_next())
  {
    recset2rec(recset_righe, rec_rmoviva, lista_campi_righeiva);
    const TString& key = recset_righe.get("IdConto").as_string(); 
    TString4 hdtipoc = recset_righe.get("IdContoTp").as_string();
    TString4 tipoc;
    tipoc = " ";
    int gr, co;
    long so;
    gr = 0;
    co = 0;
    so = 0;
    conto2campo(hdtipoc, key, tipoc, gr, co, so);
    rec_rmoviva.put(RMI_TIPOC, tipoc);
    rec_rmoviva.put(RMI_GRUPPO, gr);
    rec_rmoviva.put(RMI_CONTO, co);
    rec_rmoviva.put(RMI_SOTTOCONTO, so);  
    const TString& codiva = rec_rmoviva.get(RMI_CODIVA);
    const TString* codind = (TString*)_ivaind->objptr(codiva);			
    if (codind != NULL)
      rec_rmoviva.put(RMI_TIPODET, *codind);
    _righeiva->add(new TRectype(rec_rmoviva));
    if (!_ivaoma->is_key(codiva))
      totdoc_calc+=recset_righe.get("Imponibile").as_real();
    totdoc_calc+=recset_righe.get("Imposta").as_real();
  } 
  if (totdoc == ZERO && totdoc_calc != ZERO)
  {
    _conf->set("SOLAIVA", " ");
    _conf->set(MOV_TOTDOC, totdoc_calc.string(0,2));
  }
  return (kregivat > 0);
}

// riempie array delle righe contabili
bool THardy_movimenti::test_movcont()
{
  TString_array lista_campi_righe;
  TConfig& ini = config();
	ini.list_variables(lista_campi_righe, true, "RMOV", true);
  TString query_righe;
  query_righe << query_header();
  query_righe << "SELECT * "
	         "FROM dbo.MovContabili "
           "WHERE KMovconT=";
  query_righe << _kmovcont;
  TODBC_recordset recset_righe(query_righe);
  _righecont->destroy();
  TLocalisamfile rmov(LF_RMOV);
  TRectype& rec_rmov = rmov.curr();
  for (bool ok=recset_righe.move_first();ok;ok=recset_righe.move_next())
  {
    recset2rec(recset_righe, rec_rmov, lista_campi_righe);
    const TString& key = recset_righe.get("IdConto").as_string(); 
    TString4 hdtipoc = recset_righe.get("IdContoTp").as_string();
    TString4 tipoc;
    tipoc = " ";
    int gr, co;
    long so;
    gr = 0;
    co = 0;
    so = 0;
    conto2campo(hdtipoc, key, tipoc, gr, co, so);
    TString4 sezione = "D";
    real imp_dare = recset_righe.get("Dare").as_real();
    real imp_avere = recset_righe.get("Avere").as_real();
    if (imp_dare.is_zero())
      sezione = "A";
    rec_rmov.put(RMV_SEZIONE, sezione);
    rec_rmov.put(RMV_IMPORTO, (imp_avere.is_zero() ? imp_dare : imp_avere));
    rec_rmov.put(RMV_TIPOC, tipoc);
    rec_rmov.put(RMV_GRUPPO, gr);
    rec_rmov.put(RMV_CONTO, co);
    rec_rmov.put(RMV_SOTTOCONTO, so);
    _righecont->add(new TRectype(rec_rmov));
  } 
  return true;
}

// scrive il record passato sull'ini corrente
void THardy_movimenti::rec2ini(const TRectype& rec)
{
  for (int i=0; i<rec.items(); i++)
  {
    const char* fieldname = rec.fieldname(i);
    const TString& value = rec.get(fieldname);
    if (!value.empty())
      _conf->set(fieldname, value);
  }
}

// scrive su ini le righe contabili
bool THardy_movimenti::scrivi_righecont()
{
  TString paragraph;
  int nrigac = 1;
  for (int i=0;i<_righecont->items();i++)
  {
    TRectype& rec_rmov = *(TRectype*)_righecont->objptr(i);
    paragraph.format("%d,%d",LF_RMOV, nrigac++);
	  _conf->set_paragraph(paragraph); // riga contabile
    rec2ini(rec_rmov);
  }
  return true;
}

// calcola imponibile della riga iva passata:
// 1. se è una riga con iva indetraibile verifico il conto 4 sulla causale
//    se il conto c'è, l'imponibile è imponibile della riga
//    se il conto non c'è calcolo iva indetraibile utilizzando la % e la sommo all'imponibile
// 2. se è una riga con iva normale, l'imponibile è imponibliie della riga
real THardy_movimenti::get_imponibile(const TRectype& rec)
{
  real imponibile = rec.get_real(RMI_IMPONIBILE);
  const char* codiva = rec.get(RMI_CODIVA);
  if (_ivaind->is_key(codiva))
  {
    TString16 causale = get_str("IdCausale");
    causale << "|4";
    const TString& gruppo = cache().get(LF_RCAUSALI, causale, RCA_GRUPPO);
    if (gruppo.blank())
    {
      real imposta = rec.get_real(RMI_IMPOSTA);
      TString& codind = (TString&)_ivaind->find(codiva);
      real perc(cache().get("%DET", codind, "R0"));
      imposta = (imposta*perc)/CENTO + 0,01;
      imposta.round(2);
      imponibile+=imposta;
    }
  }
  return imponibile;
}

// gestisce tutto il procedimento di scrittura su ini delle righe iva e contabili
bool THardy_movimenti::scrivi_righe()
{
	const int ndec = TCurrency::get_firm_dec(false);
 	TString paragraph;
  int nrigai = 1; // contatore righe iva

  TConfig& ini = config();
  TString8 iva_esente = ini.get("IVA_ESENTE", "Parametri");
  TToken_string sconto_omaggi = ini.get("CONTO_OMAGGI", "Parametri");
  TToken_string conti_mov = ini.get("CONTI_MOV", "Parametri");
  const int gruppo_omaggi = sconto_omaggi.get_int(0);
  const int conto_omaggi = sconto_omaggi.get_int(1);
  const long sottoconto_omaggi = sconto_omaggi.get_long(2);
  real saldo;

  // la sezione della riga 1 mi serve per verificare il segno e la sezione delle righe contabili
  TString16 causale = get_str("IdCausale");
  causale << "|1";
  const char sez_cau = (cache().get(LF_RCAUSALI, causale, RCA_SEZIONE)[0] == 'D' ? 'A' : 'D');
	bool has_iva_omaggio = false;
        
  // se è un movimento iva metto in atto il meccanismo di ricerca  per assegnare le aliquote ai conti
  if (_righeiva->items() > 0)
  {
    for (int i = 0; !has_iva_omaggio && i<_righeiva->items(); i++)
      has_iva_omaggio = _ivaoma->is_key(((TRectype*)_righeiva->objptr(i))->get(RMI_CODIVA));
    // primo passo: scartare le righe contabili con gruppi non presenti nella lista GRUPPI_MOV
    for (int i=_righecont->items() - 1;i>=0;i--)
    {
      TRectype& rec_rmov = *(TRectype*)_righecont->objptr(i);
      const int gruppo = rec_rmov.get_int(RMV_GRUPPO);
      const int conto = rec_rmov.get_int(RMV_CONTO);
      const long sottoconto = rec_rmov.get_long(RMV_SOTTOCONTO);
			TToken_string key;

			key.add(gruppo);
			key.add(conto);

			const int tipoconto = atoi(cache().get(LF_PCON, key, PCN_INDBIL));
      const bool riga_omaggio = (gruppo == gruppo_omaggi) && (conto == conto_omaggi) && (sottoconto == sottoconto_omaggi);
      const TString & descr = rec_rmov.get(RMV_DESCR);
      
      // se la descrizione comincia con queste stringhe, significa che è un pagamento immediato
      // e va passata la riga contabile cosi come è
      if (descr.starts_with("S.DO DOC.") || descr.starts_with("ABB. DOC.") || descr.starts_with("ACC. DOC."))
        rec_rmov.put(RMV_ROWTYPE, "C");
      else
        if (riga_omaggio)
        {
					if (!has_iva_omaggio)
          {
            paragraph.format("%d,%d",LF_RMOVIVA, nrigai++);
            _conf->set_paragraph(paragraph); // riga iva
            rec2ini(*(TRectype*)_righeiva->objptr(0));
            // sostituisco codice iva e importo (-) e gruppo conto sottoconto
            const char sezione = rec_rmov.get_char(RMV_SEZIONE);
            real importo = rec_rmov.get_real(RMV_IMPORTO);
            if (sezione != sez_cau)
              importo = -importo;
            saldo += importo;
            _conf->set(RMI_CODIVA, iva_esente); // codice iva esente per quadrare il movimento
            _conf->set(RMI_TIPODET, "");
            _conf->set(RMI_IMPONIBILE, importo.string(0,2));  // imponibile negativo
            _conf->set(RMI_IMPOSTA, ""); // imposta zero           
            _conf->set(RMI_TIPOC, "");
            _conf->set(RMI_GRUPPO, sconto_omaggi.get(0));
            _conf->set(RMI_CONTO, sconto_omaggi.get(1));
            _conf->set(RMI_SOTTOCONTO, sconto_omaggi.get(2));
          }
          _righecont->destroy(i);
        }
        else
        {
          bool found = (tipoconto == 3) || (tipoconto == 4);

			    if (!found)
			    {
				    TToken_string cod("", ',');
				    cod.add(gruppo);
				    cod.add(conto);
				    cod.add(sottoconto);

				    // Provo il sottoconto ma se non riesco provo con conto e poi anche gruppo (formato -> 3,1,2 o 3,1,0 o 3,0,0)
				    for (int c = 2; !found && c >= 0; c--)
				    {
					    found = conti_mov.get_pos(cod) >= 0;
					    cod.add(0, c);
				    }
				    if (!found)
					    _righecont->destroy(i);
			    }
          if (found)
          {
            const char sezione = rec_rmov.get_char(RMV_SEZIONE);
            if (sezione != sez_cau)
            {
              real importo = rec_rmov.get_real(RMV_IMPORTO);
              importo = -importo;
              rec_rmov.put(RMV_SEZIONE, sez_cau);
              rec_rmov.put(RMV_IMPORTO, importo);
            }
          }
        }
    }
    _righecont->pack();
    const bool singola_rigacont = (_righecont->items()==1);
    // secondo passo: per ogni riga iva cerco importo uguale in righe contabili, 
    // se lo trovo assegno quel codice iva al conto contabile trovato e cancello la riga iva e la riga contabile
    for (int i=0;i<_righeiva->items();i++)
    {
      TRectype& rec_rmoviva = *(TRectype*)_righeiva->objptr(i);
      const char* codiva = rec_rmoviva.get(RMI_CODIVA);
      const bool riga_omaggio = _ivaoma->is_key(codiva);
      // se le righe contabili sono 1, su tutte le righe iva metto quel conto, da brava massaia ...
      if ((!riga_omaggio) && _righecont->items()==1)
      {
        TRectype& rec_rmov = *(TRectype*)_righecont->objptr(0);
        rec_rmoviva.put(RMI_TIPOC, rec_rmov.get(RMV_TIPOC));
        rec_rmoviva.put(RMI_GRUPPO, rec_rmov.get(RMV_GRUPPO));
        rec_rmoviva.put(RMI_CONTO, rec_rmov.get(RMV_CONTO));
        rec_rmoviva.put(RMI_SOTTOCONTO, rec_rmov.get(RMV_SOTTOCONTO));
        paragraph.format("%d,%d",LF_RMOVIVA, nrigai++);
        _conf->set_paragraph(paragraph); // riga iva
        rec2ini(rec_rmoviva);
			  rec_rmoviva.zero();
      }
      else
      {
			  TCodiceIVA c(codiva);
        real imponibile = get_imponibile(rec_rmoviva);

        for (int j=0;j<_righecont->items();j++)
        {
          TRectype& rec_rmov = *(TRectype*)_righecont->objptr(j);
          const bool riga_cont = (rec_rmov.get(RMV_ROWTYPE) == "C");
          
          real importo = rec_rmov.get_real(RMV_IMPORTO);
          if ((!riga_omaggio) && (!riga_cont) && (importo <= imponibile))
          {
					  const real impon = rec_rmoviva.get_real(RMI_IMPONIBILE);
					  const real iva = rec_rmoviva.get_real(RMI_IMPOSTA);
					  c.imposta(importo);
            rec_rmoviva.put(RMI_TIPOC, rec_rmov.get(RMV_TIPOC));
            rec_rmoviva.put(RMI_GRUPPO, rec_rmov.get(RMV_GRUPPO));
            rec_rmoviva.put(RMI_CONTO, rec_rmov.get(RMV_CONTO));
            rec_rmoviva.put(RMI_SOTTOCONTO, rec_rmov.get(RMV_SOTTOCONTO));
            real wimp = impon ;
            if (importo < imponibile)
            {
              wimp *= importo / imponibile;
              wimp.round(2);
            }
					  const real wiva = c.imposta(wimp);
					  if (importo < imponibile)
					  {
						  rec_rmoviva.put(RMI_IMPONIBILE, wimp);
						  rec_rmoviva.put(RMI_IMPOSTA, wiva);
					  }
            paragraph.format("%d,%d",LF_RMOVIVA, nrigai++);
	          _conf->set_paragraph(paragraph); // riga iva
            rec2ini(rec_rmoviva);
            _righecont->destroy(j, true);
            j = _righecont->items();
					  if (importo == imponibile)
						  rec_rmoviva.zero();
					  else
					  {
						  rec_rmoviva.put(RMI_IMPONIBILE, impon - wimp);
						  rec_rmoviva.put(RMI_IMPOSTA, iva - wiva);
					  }
          }        
        }
      }
    }
    _righecont->pack();
    // terzo passo: per ogni riga iva rimasta distribuisco importo su tutti i conti rimasti in righe cont.
    for (int i=0;i<_righeiva->items();i++)
    {
      TRectype& rec_rmoviva = *(TRectype*)_righeiva->objptr(i);
      if (!rec_rmoviva.empty())
      {
        const TString& codiva = rec_rmoviva.get(RMI_CODIVA);
        const bool riga_omaggio = _ivaoma->is_key(codiva);
        real imponibile = rec_rmoviva.get_real(RMI_IMPONIBILE);
        real imposta = rec_rmoviva.get_real(RMI_IMPOSTA);
        TGeneric_distrib dimponibile(imponibile, ndec);
        TGeneric_distrib dimposta(imposta, ndec);
        for (int j=0;j<_righecont->items();j++)
        {
          TRectype& rec_rmov = *(TRectype*)_righecont->objptr(j);
          const bool riga_cont = (rec_rmov.get(RMV_ROWTYPE) == "C");
          if (!riga_cont)
          {
            real importo = rec_rmov.get_real(RMV_IMPORTO);
            dimponibile.add(importo);
            dimposta.add(importo);
          }
        }
        for (int j=0;j<_righecont->items();j++)
        {
          TRectype& rec_rmov = *(TRectype*)_righecont->objptr(j);
          const bool riga_cont = (rec_rmov.get(RMV_ROWTYPE) == "C");
          if (!riga_cont)
          {
            real importo = dimponibile.get();
            real imposta = dimposta.get();
            rec_rmoviva.put(RMI_TIPOC, rec_rmov.get(RMV_TIPOC));
            rec_rmoviva.put(RMI_GRUPPO, rec_rmov.get(RMV_GRUPPO));
            rec_rmoviva.put(RMI_CONTO, rec_rmov.get(RMV_CONTO));
            rec_rmoviva.put(RMI_SOTTOCONTO, rec_rmov.get(RMV_SOTTOCONTO));
            rec_rmoviva.put(RMI_IMPONIBILE, importo);
            rec_rmoviva.put(RMI_IMPOSTA, imposta);
            paragraph.format("%d,%d",LF_RMOVIVA, nrigai++);
            _conf->set_paragraph(paragraph); // riga iva
            rec2ini(rec_rmoviva);
          }
        }
        // se iva utilizzata per gli omaggi, devo fare un'altra riga iva identica ma con importo avere con iva esente e gr/co/so letto da configurazione CONTO_OMAGGI
        if (riga_omaggio)
        {
          paragraph.format("%d,%d",LF_RMOVIVA, nrigai++);
          _conf->set_paragraph(paragraph); // riga iva
          rec2ini(rec_rmoviva);
          // sostituisco codice iva e importo (-) e gruppo conto sottoconto
          imponibile = -imponibile;
          _conf->set(RMI_CODIVA, iva_esente); // codice iva esente per quadrare il movimento
          _conf->set(RMI_TIPODET, "");
          _conf->set(RMI_IMPONIBILE, imponibile.string(0,2));  // imponibile negativo
          _conf->set(RMI_IMPOSTA, ""); // imposta zero           
          _conf->set(RMI_TIPOC, "");
          _conf->set(RMI_GRUPPO, sconto_omaggi.get(0));
          _conf->set(RMI_CONTO, sconto_omaggi.get(1));
          _conf->set(RMI_SOTTOCONTO, sconto_omaggi.get(2));
        }
      }
    }
    for (int j=_righecont->items()-1;j>=0;j--)
    {
      TRectype& rec_rmov = *(TRectype*)_righecont->objptr(j);
      const bool riga_cont = (rec_rmov.get(RMV_ROWTYPE) == "C");
      if (riga_cont)
        rec_rmov.zero(RMV_ROWTYPE);
      else
        _righecont->destroy(j, true);
    }   
		if (saldo != ZERO)
		{
			TString paragraph;
			
			paragraph.format("%d",LF_MOV);

			real totdoc(_conf->get(MOV_TOTDOC, paragraph));
			totdoc += saldo;
			_conf->set(MOV_TOTDOC, totdoc.string(0,2), paragraph);
		}
    //_righecont->destroy();
  }
  // scrivo su ini le righe contabili rimaste (tutte se il mov non è iva)
  scrivi_righecont();
  return true;
}

// procedura principale di conversione
bool THardy_movimenti::trasferisci()
{
  TConfig& ini = config();

  // creazione array delle aliquote iva con % indetraibilità e degli omaggi
  // leggere la tabella hardy AliquoteIVA
  _ivaind->destroy();
  _ivaoma->destroy();
  TString query_iva;
  query_iva << query_header();
  query_iva << "SELECT * "
		           "FROM dbo.AliquoteIVA ";    
  TODBC_recordset recset_iva(query_iva);
  for (bool ok=recset_iva.move_first();ok;ok=recset_iva.move_next())
  {
    const char* codiva = recset_iva.get("IdIva").as_string();
    real ind = recset_iva.get("Indetraibilita").as_real();
    const int flomaggio = recset_iva.get("FlOmaggio").as_int();
    if (ind != ZERO)
    {
      TString4 oggetto = ini.get(codiva, "Indetraibilita");
      _ivaind->add(codiva, oggetto);
    }
    if (flomaggio > 0)
    {
		  real* oggetto = new real();                                
      _ivaoma->add(codiva, (TObject*)oggetto);
    }
  }

  // query su testate movimenti 
  TString16 dastr, astr;
  dastr.format("%4d-%2d-%2d", _dadata.year(), _dadata.month(), _dadata.day());
  astr.format("%4d-%2d-%2d", _adata.year(), _adata.month(), _adata.day());

  TString query = 
    "SELECT * "
		"FROM dbo.MovContabiliT "
    "WHERE Esercizio=";
  query << _anno;
  query << " AND DataMovimento>= '";
  query << dastr;
  query << "' AND DataMovimento<= '";
  query << astr;
  query << "' ORDER BY DataMovimento ";

  TRecordset& recset = create_recordset(query);
	
  TString_array lista_campi;
	ini.list_variables(lista_campi, true, "MOV", true);

  TFilename outdir;
  outdir = ini.get("PATH", "Main");
	TFilename listfiles = outdir;	listfiles.add("ha*.ini");
	TString_array transactions;
  list_files(listfiles, transactions);
  FOR_EACH_ARRAY_ROW(transactions, row, name)
    remove(*name);

  _conf = NULL;
	long ntran = 1L;
 	TString paragraph;

  THardy_iterator hi(this);
  while (++hi)
  {
    _kmovcont = recset.get("KMovconT").as_int(); // numero movimento testata
	  if (_conf != NULL)
			  delete _conf;
	  _conf = NULL;
	  TFilename temp(outdir);
	  temp.add(format("ha%06ld", ntran++));
	  temp.ext("ini");
	  if (temp.exist())
      temp.fremove();
	  _conf = new TConfig(temp);
	  _conf->set_paragraph("Transaction");
	  _conf->set("Action","INSERT");
	  _conf->set("Mode", "AUTO");
    paragraph.format("%d",LF_MOV);
	  _conf->set_paragraph(paragraph); // testata movimento
    aggiorna_ini(*_conf, lista_campi);
    TString codcaus = _conf->get(MOV_CODCAUS);
    if (cache().get(LF_CAUSALI, codcaus, CAU_MOVAP) == "C")
    {
      const TDate d(_conf->get(MOV_DATAREG));
      const TDate datacomp(31, 12, d.year() - 1);

      _conf->set(MOV_DATACOMP, datacomp.string());
    }
    // verifica se è un mov. iva e nel caso aggiorna testata e array righe iva
    bool iva = test_moviva(); 
    // legge righe contabili e aggiorna array righe cont.
    test_movcont();
    // scrive RMOV e /o RMOVIVA a partire da array righe letti da db hardy
    bool ok = scrivi_righe();

    if (!ok)    
    {
      ntran--;
  	  if (temp.exist())
        temp.fremove();
      TString msg;
      msg << (iva ? TR("Il movimento iva "): TR("Il movimento contabile ")) << _kmovcont 
          << TR(" ha generato un errore, non è stato convertito ");
      log(msg, 2); // Non uso log_error per non dare messaggi fuorvianti
    }
#ifdef DBG
    else
    {
      TString msg;
      msg << (iva ? TR("Movimento iva "): TR("Movimento contabile ")) << _kmovcont 
          << TR(" generato nel file ") << temp;
      log(msg);
    }
#endif
  }
	if (_conf != NULL)
		delete _conf;
  show_log();
	if (yesno_box(FR("Si desidera confermare l'importazione di %ld movimenti"), ntran-1))
	{
		TString app;
		app << "cg2 -0 -i" << outdir << "/ha*.ini"; 
		TExternal_app primanota(app);
		primanota.run(true);
	}
  return true;
}

THardy_movimenti::THardy_movimenti(const int anno, const TDate dadata, const TDate adata) : _anno(anno), _dadata(dadata), _adata(adata)
{
  _righeiva = new TArray;
  _righecont = new TArray;
  _ivaind = new TAssoc_array;
  _ivaoma = new TAssoc_array;
}

///////////////////////////////////////////////////////////
// TConvMovimentiHardy_mask
///////////////////////////////////////////////////////////

class TConvMovimentiHardy_mask : public TAutomask
{
protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  void serialize(bool bSave);

public:
  void trasferisci();

  TConvMovimentiHardy_mask();
  virtual ~TConvMovimentiHardy_mask();
};

// Funzione di trasferimento dati da/verso file .ini con lo stesso nome della maschera
// Andrebbe messo in libreria
void TConvMovimentiHardy_mask::serialize(bool bSave)
{
  TFilename n = source_file(); n.ext("ini");  // Construisce il nome del .ini in base al .msk
  TConfig cfg(n, "Main");                     // Crea il file di configurazione
  TString4 id; 
  for (int i = fields()-1; i >= 0; i--)       // Scandisce tutti i campi della maschera ...   
  {
    TMask_field& f = fld(i);
    if (f.active() && f.is_loadable())        // ... selezionando solo quelli editabili
    {
      id.format("%d", f.dlg());
      if (bSave)                              // A seconda del flag di scrittura ... 
        cfg.set(id, f.get());                 // ... o scrive sul .ini 
      else 
        f.set(cfg.get(id));                   // ... o legge dal .ini
    }
  }
}

void TConvMovimentiHardy_mask::trasferisci()
{
  TString query_header;
  query_header << "ODBC(" << get(F_DSN) << ',' << get(F_USR) << ',' << get(F_PWD) << ")\n";

  const int anno = get_int(F_ANNO);
  const TDate dadata = get_date(F_DADATA);
  const TDate adata = get_date(F_ADATA);
  if (anno!=0)
  {
    THardy_log log;
    THardy_movimenti pc(anno, dadata, adata);
    pc.init(TR("Movimenti contabili"), query_header, log);
    pc.trasferisci();
  }
}

bool TConvMovimentiHardy_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case DLG_OK:
    if (e == fe_button)
      serialize(true);
    break;
  default:
    break;
  }
  return true;
}

TConvMovimentiHardy_mask::TConvMovimentiHardy_mask() : TAutomask("hacnv200a")
{
  serialize(false);
}

TConvMovimentiHardy_mask::~TConvMovimentiHardy_mask()
{ 
}

///////////////////////////////////////////////////////////
// TConvMovimentiHardy
///////////////////////////////////////////////////////////

class TConvMovimentiHardy : public TSkeleton_application
{
protected:
  virtual void main_loop();
};

void TConvMovimentiHardy::main_loop()
{
  TConvMovimentiHardy_mask mask;
  while (mask.run() == K_ENTER)
    mask.trasferisci();
}

int hacnv200(int argc, char* argv[])
{
  TConvMovimentiHardy ih;
  ih.run(argc, argv, APPNAME);
  return 0;
}