#include <applicat.h>
#include <automask.h>
#include <execp.h>
#include <progind.h>

#include "ps0713600a.h"

#include <mov.h>
#include <rmov.h>
#include <rmoviva.h>

#include "ps0713lib.h"

#include "../ca/calib01.h"
#include "../ca/calib02.h"
#include "../ca/movana.h"
#include "../ca/rmovana.h"


                                 ////////////////////////////////////
                                 ////    TIMPORTA_FAT_FOR_MSK    ////
                                 ////////////////////////////////////

//Classe TImporta_fat_for_msk
class TImporta_fat_for_msk : public TAutomask
{
protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);

public:
  TImporta_fat_for_msk(const char* name);
};
  
TImporta_fat_for_msk::TImporta_fat_for_msk(const char* name)
                     :TAutomask(name) {}

//ON_FIELD_EVENT: metodo che gestisce i comportamenti dei vari campi della maschera
//(per adesso segnaposto)
bool TImporta_fat_for_msk::on_field_event(TOperable_field& f, TField_event e, long jolly)
{ 
	return true;
}

                                 ////////////////////////////////////
                                 ////    TIMPORTA_FAT_FOR_APP    ////
                                 ////////////////////////////////////

//classe TImporta_fat_for_app
class TImporta_fat_for_app : public TSkeleton_application
{
  TImporta_fat_for_msk* _msk;

protected:
  virtual bool check_autorization() const { return false; }
  virtual const char * extra_modules() const { return "ve"; }
  virtual void main_loop();

public:  
  //metodi per l'acquisizione dei dati da file e i calcoli da effettura eprima dell'importazione
  void scan_file(TVB_recset& file, TAssoc_array& righean, TAssoc_array& righecg) const;
  bool calcola_quadratura(TAssoc_array& righean, TCommessa_string& kmax, real& quad);
  bool importa(const TFilename& name, TAssoc_array& righean, TAssoc_array& righecg);
  //metodi per l'importazione vera e propria
  long genera_movcg();
  void genera_analitica(TAssoc_array& righean, TCommessa_string& kmax, const real& quad, const long numregcg);
  void importa_fat(TAssoc_array& righecg, TCommessa_string& kmax, const real& quad, TConfig* configfile, const long numregcg);
  void importa_fat_ric(TAssoc_array& righecg, TCommessa_string& kmax, const real& quad, TConfig* configfile, const long numregcg);
  void gestione_ini(TAssoc_array& righecg, TCommessa_string& kmax, const real& quad, const long numregcg);
  bool transfer();
};

void TImporta_fat_for_app::scan_file(TVB_recset& file, TAssoc_array& righean, TAssoc_array& righecg) const
{
  TProgind pi(file.items(), "Scansione file in corso ...", true, true);	

  //scorro il file, saltando le eventuali righe vuote, e riempio i due array,
  //quello delle righe di analitica e quello delle righe di contabilit� generale;
  //l'analitica va raggruppata per CODCMS - GRUPPO - CONTO - SOTTOCONTO,
  //metre la contabilit� generale solo per CODCMS (che corrisponde all'IDLAVORO)
	for (bool ok = file.move_first(); ok ;ok = file.move_next())
	{
		if (!pi.addstatus(1)) 
			break;

    //evito di analizzare eventuali righe vuote
    if(file.empty())		
			continue;

    //idlavoro
    const TString16 idlavoro = file.get(0).as_string();

    //imponibile
    TString16 tmp = file.get(1).as_string();
    tmp.replace(',','.');
		tmp.strip("\"");
    const real imp = tmp;

    //gruppo
    const int gruppo = file.get(2).as_int();

    //gruppo
    const int conto = file.get(3).as_int();

    //gruppo
    const int sotco = file.get(4).as_int();

    //chiave del TAssoc_array dell'analatica
    TCommessa_string keyan(idlavoro, gruppo, conto, sotco);

    //riempio l'array dell'analitica
    if(righean.is_key(keyan))
    {
      real& imponibile = *(real*)righean.objptr(keyan);
      imponibile += imp;
    }
    else
      righean.add(keyan, imp);

    //riempio l'array della contabilit� generale
    if(righecg.is_key((TString)idlavoro))
    {
      real& imponibile = *(real*)righecg.objptr((TString)idlavoro);
      imponibile += imp;
    }
    else
      righecg.add((TString)idlavoro, imp);
  }
}

//CALCOLA_QUADRATURA: metodo che calcola l'ammontare dell'evetuale riga di quadratura del documento
bool TImporta_fat_for_app::calcola_quadratura(TAssoc_array& righean, TCommessa_string& kmax, real& quad)
{
  //calcolo qaul'� la massima tolleranza consentita
  //(da analisi la tolleranza � un centesimo per riga)
  const real quadmax = righean.items() * 0.01;
  
  real quadra = ZERO;
  real max    = ZERO;

  bool ok = true;

  real totimp = ZERO;

  //scorro tutte le righe, eventualmente ragguppate, tenendo traccia di qual'� quella che ha imponibile maggiore
  //e arrotondando  i vari imponibili a due decimali
  FOR_EACH_ASSOC_OBJECT(righean, obj, key, itm)
  {
    real& imp = *(real*)itm;

    if(imp > max)
    {
      max = imp;
      kmax = key;
    }

    const real tmp = imp;

    imp.round(2);

    quadra += tmp - imp;
    totimp += tmp;
  }

  if(quadra <= quadmax)
  {
    quad = quadra;
    quad.round(2);
  }
  else
  {
    error_box(TR("L'arrotondamento effettuato sul documento supera la tolleranza consentita"));
    quad = ZERO;
    ok = false;
  }

  if(totimp != _msk->get_real(F_IMPON))
  {
    error_box(TR("Il totale Imponibile delle righe non corrisponde al valore riportato sulla maschera"));
    ok = false;
  }
  return ok;
}

//IMPORTA: metodo che popola l'assoc_array delle righe e calcola la quadratura
bool TImporta_fat_for_app::importa(const TFilename& name, TAssoc_array& righean, TAssoc_array& righecg)
{
  TVB_recset file(name, ',');  

  scan_file(file, righean, righecg);

  return true;
}

//GENERA_MOVCG: metodo che genera la testata del movimento di prima nota
long TImporta_fat_for_app::genera_movcg()
{
  TISAM_recordset movrec("USE MOV");
  movrec.move_last();
  long numreg = movrec.get(MOV_NUMREG).as_int(); numreg++;

  TLocalisamfile mov(LF_MOV);
  mov.put(MOV_NUMREG,   numreg);
  mov.put(MOV_DATACOMP, _msk->get_date(F_DATAREG));
  mov.put(MOV_DATAREG,  _msk->get_date(F_DATAREG));

  mov.write();

  return numreg;
}

//GENERA_ANALITICA: metodo che genera il movimento di contabilit� analitica
void TImporta_fat_for_app::genera_analitica(TAssoc_array& righean, TCommessa_string& kmax, const real& quad, const long numregcg)
{
  TProgind pi(righean.items(), "Importazione analitica in corso...", true, true);

  TToken_string key;
  key.add(_msk->get(F_CODCAU));
  key.add(1);
  key.add(1);
  const TRectype& causale = cache().get(LF_RCAUSALI, key);
  const int anno = _msk->get_date(F_DATADOC).year();
  key.add(2, 1);
  const TRectype& causale1 = cache().get(LF_RCAUSALI, key);

  //instanzio il movimento di analitica
  TAnal_mov anmo(0);
  anmo.put(MOVANA_NUMREGCG, numregcg);
  anmo.put(MOVANA_DATACOMP, _msk->get_date(F_DATAREG));
  anmo.put(MOVANA_DATAREG,  _msk->get_date(F_DATAREG));
  anmo.put(MOVANA_CODCAUS,  _msk->get(F_CODCAU));
  anmo.put(MOVANA_ANNOES,   anno);

  //aggiungo i campi relativi ai documenti solo quando li ho a disposizione
  if(_msk->get_int(F_TIPOFAT) == 1)
  {
    const real iva     = cache().get("%IVA", _msk->get(F_CODIVA), "R0");
    real totimponibile = ZERO;
    real totimposta    = ZERO;

    //calcolo dei totali
    FOR_EACH_ASSOC_OBJECT(righean, obj, keyar, itm)
    {
      real& imp = *(real*)itm;  imp.round(2);
      totimponibile += imp;

      real imposta = imp * iva / CENTO; imposta.round(2);
      totimposta += imposta;
    }
    if (quad > ZERO)
    {
      totimponibile += quad;
      real imposta = quad * iva / CENTO; imposta.round(2);
      totimposta += imposta;
    }
    const real totdoc  = totimponibile + totimposta;
    
    anmo.put(MOVANA_DATADOC, _msk->get_date(F_DATADOC));
    anmo.put(MOVANA_NUMDOC,  _msk->get_long(F_NDOC));
    anmo.put(MOVANA_TOTDOC,  totdoc.string());
  }

  //per ogni oggetto dell'assoc_array, creo una riga di analitica
  FOR_EACH_ASSOC_OBJECT(righean, obj, keyar, itm)
  {
    if (!pi.addstatus(1)) 
		  break;

    TCommessa_string& row = (TCommessa_string)keyar;
    const TString& idlav  = row.idlavoro();
    const int      gruppo = row.gruppo();   TString8  grup; grup.format("%03d",  gruppo);
    const int      conto  = row.conto();    TString8  cont; cont.format("%03d",  conto);
    const int      sotco  = row.sotco();    TString16 sotc; sotc.format("%06ld", sotco);
    TString80 codconto; codconto << grup << cont << sotc;

    real& imp = *(real*)itm;  imp.round(2);
		
		if (!imp.is_zero())
		{
      TRectype& ranmo = anmo.new_row();
      ranmo.put(RMOVANA_ANNOES,   anno);
      ranmo.put(RMOVANA_SEZIONE,  causale1.get("SEZIONE"));
      ranmo.put(RMOVANA_CODCONTO, codconto);
      ranmo.put(RMOVANA_CODCMS,   idlav);
      ranmo.put(RMOVANA_IMPORTO,  imp);
    }
  }

  if(quad > ZERO)
  {
    TImporto importo(causale.get("SEZIONE")[0], quad);
    const int  gruppo = kmax.gruppo();   TString8  grup; grup.format("%03d",  gruppo);
    const int  conto  = kmax.conto();    TString8  cont; cont.format("%03d",  conto);
    const int  sotco  = kmax.sotco();    TString16 sotc; sotc.format("%06ld", sotco);
    TString80 codconto; codconto << grup << cont << sotc;

    TRectype& ranmo = anmo.new_row();
    ranmo.put(RMOVANA_ANNOES,   anno);
    ranmo.put(RMOVANA_SEZIONE,  causale.get("SEZIONE"));
    ranmo.put(RMOVANA_CODCONTO, codconto);
    ranmo.put(RMOVANA_CODCMS,   kmax.idlavoro());
    ranmo.put(RMOVANA_IMPORTO,  importo.valore().string());
  }

  TLocalisamfile fmov(LF_MOVANA);
  anmo.write(fmov);
}

//IMPORTA_FAT: metodo che genera l'ini per l'importazione delle fatture a fornitore
void TImporta_fat_for_app::importa_fat(TAssoc_array& righecg, TCommessa_string& kmax, const real& quad, TConfig* configfile, const long numregcg)
{
  const TDate    datareg = _msk->get_date(F_DATAREG);
  const TDate    datadoc = _msk->get_date(F_DATADOC);
  const long     ndoc    = _msk->get_long(F_NDOC);
  const TString4 codiva  = _msk->get(F_CODIVA);
  const long     codcf   = _msk->get_long(F_CODFOR);
  const TString4 codcau  = _msk->get(F_CODCAU);
  const int      anno    = datadoc.year();

  TToken_string key;
  key.add(codcau);
  key.add(1);
  const TRectype& causale = cache().get(LF_RCAUSALI, key);
  
  const real iva     = cache().get("%IVA", codiva, "R0");
  real totimponibile = ZERO;
  real totimposta    = ZERO;

  //calcolo dei totali
  FOR_EACH_ASSOC_OBJECT(righecg, obj, keyar, itm)
  {
    real& imp = *(real*)itm;  imp.round(2);
    totimponibile += imp;

    real imposta = imp * iva / CENTO; imposta.round(2);
    totimposta += imposta;
  }
  if (quad > ZERO)
  {
    totimponibile += quad;
    real imposta = quad * iva / CENTO; imposta.round(2);
    totimposta += imposta;
  }
  
  const real totdoc  = totimponibile + totimposta;

  TProgind pi(righecg.items(), "Importazione in corso...", true, true);
  
  configfile->set_paragraph("23"); //setto il paragrafo [23] del file ini (testata)
  configfile->set(MOV_NUMREG,  numregcg);  
  configfile->set(MOV_DATADOC, datadoc);
  configfile->set(MOV_NUMDOC,  ndoc);
  configfile->set(MOV_CODCAUS, codcau);
  configfile->set(MOV_TIPO,    'F');
  configfile->set(MOV_CODCF,   codcf);
  configfile->set(MOV_TOTDOC,  totdoc.string());

  int nrigaiv = 0;

  //per ogni riga dell'assoc_array, creo un paragrafo riga
  FOR_EACH_ASSOC_OBJECT(righecg, obj, keyar1, itm1)
  {
    if (!pi.addstatus(1)) 
		  break;

    TString80 idlav(keyar1);
    
    real& imp = *(real*)itm1;  imp.round(2);
		
		if (!imp.is_zero())
		{
      TString8 paragraph;
      paragraph.format("%d,%d", LF_RMOVIVA, ++nrigaiv);
      configfile->set_paragraph(paragraph);
      configfile->set(RMI_NUMRIG, nrigaiv);
      
      TImporto importo('D', imp);

      real rimposta = imp * iva / CENTO;  rimposta.round(2);
      
      TCommessa comm(idlav);
      TCommessa_string cmsstr = comm.cmsstr();

      configfile->set(RMI_ANNOES,     anno);
      configfile->set(RMI_IMPONIBILE, importo.valore().string());
      configfile->set(RMI_CODIVA,     codiva);
      configfile->set(RMI_IMPOSTA,    rimposta.string());
      configfile->set(RMI_GRUPPO,     cmsstr.gruppo());
      configfile->set(RMI_CONTO,      cmsstr.conto());
      configfile->set(RMI_SOTTOCONTO, cmsstr.sotco());
      configfile->set(RMI_TIPOATT,    1);
    }
  }

  //se c'� bisogno, faccio una riga in pi�
  if(quad > ZERO)
  {
    TString8 paragraph;
    paragraph.format("%d,%d", LF_RMOVIVA, ++nrigaiv);
    configfile->set_paragraph(paragraph);
    configfile->set(RMI_NUMRIG, nrigaiv);

    TImporto importo('D', quad);    

    real rimposta = importo.valore() * iva / CENTO;  rimposta.round(2);

    configfile->set(RMI_ANNOES,     anno);
    configfile->set(RMI_IMPONIBILE, importo.valore().string());
    configfile->set(RMI_CODIVA,     codiva);
    configfile->set(RMI_IMPOSTA,    rimposta.string());
    configfile->set(RMI_GRUPPO,     kmax.gruppo());
    configfile->set(RMI_CONTO,      kmax.conto());
    configfile->set(RMI_SOTTOCONTO, kmax.sotco());
    configfile->set(RMI_TIPOATT,    1);
  }

  TString8 paragraph;
  paragraph.format("%d,%d", LF_RMOVIVA, ++nrigaiv);
  configfile->set_paragraph(paragraph);
}

//IMPORTA_FAT_RIC: metodo che genera l'ini per l'importazione delle fatture da ricevere
//(attenzione: genero righe IVA)
void TImporta_fat_for_app::importa_fat_ric(TAssoc_array& righecg, TCommessa_string& kmax, const real& quad, TConfig* configfile, const long numregcg)
{
  const TDate    datareg = _msk->get_date(F_DATAREG);
  const TDate    datadoc = _msk->get_date(F_DATADOC);
  const long     ndoc    = _msk->get_long(F_NDOC);
  const TString4 codiva  = _msk->get(F_CODIVA);
  const long     codcf   = _msk->get_long(F_CODFOR);
  const TString4 codcau  = _msk->get(F_CODCAU);
  const int      anno    = datadoc.year();

  TToken_string key;
  key.add(codcau, 0);
  key.add(1, 1);
  const TRectype& causale = cache().get(LF_RCAUSALI, key);
  
  const real iva = cache().get("%IVA", codiva, "R0");
  real totimp    =  _msk->get_real(F_IMPON);  totimp.round(2);
  real totdoc    = totimp;                    totdoc.round(2);

  TProgind pi(righecg.items(), "Importazione in corso...", true, true);
  
  configfile->set_paragraph("23"); //setto il paragrafo [23] del file ini (testata)
  configfile->set(MOV_NUMREG,  numregcg);
  configfile->set(MOV_DATAREG, datareg);  
  configfile->set(MOV_CODCAUS, codcau);

  int nrigaco = 1;  

  //prima riga: serve a bilanaciare le altre
  TString8 paragraph;
  paragraph.format("%d,%d", LF_RMOV, 1);
  configfile->set_paragraph(paragraph);
  configfile->set(RMV_NUMRIG, 1);

  TImporto importo(causale.get("SEZIONE")[0], totdoc);  

  configfile->set(RMV_ANNOES,     anno);
  configfile->set(RMV_SEZIONE,    causale.get("SEZIONE"));
  configfile->set(RMV_IMPORTO,    importo.valore().string());  
  configfile->set(RMV_TIPOC,      causale.get("TIPOCF"));
  configfile->set(RMV_GRUPPO,     causale.get("GRUPPO"));
  configfile->set(RMV_CONTO,      causale.get("CONTO"));
  configfile->set(RMV_SOTTOCONTO, _msk->get_long(F_CODFOR));
  configfile->set(RMV_TIPOC,      "F");
  configfile->set(RMV_ROWTYPE,    "T");

  key.add(2,1);
  const TRectype& causale2 = cache().get(LF_RCAUSALI, key);

  //per ogni oggetto dell'assoc_array, preparo un paragrafo riga
  FOR_EACH_ASSOC_OBJECT(righecg, obj, keyar, itm)
  {
    if (!pi.addstatus(1)) 
		  break;

    TString80 idlav(keyar);
    real& imp = *(real*)itm;  imp.round(2);
		
		if (!imp.is_zero())
		{
      TString8 paragraph;
      paragraph.format("%d,%d", LF_RMOV, ++nrigaco);
      configfile->set_paragraph(paragraph);
      configfile->set(RMV_NUMRIG, nrigaco);

      TImporto importo(causale2.get("SEZIONE")[0], imp);      

      const real rimposta = imp * iva / CENTO;

      const TCommessa cms(idlav);
      TCommessa_string cmsstr = cms.cmsstr();

      configfile->set(RMV_ANNOES,     anno);
      configfile->set(RMV_SEZIONE,    causale2.get("SEZIONE"));
      configfile->set(RMV_IMPORTO,    importo.valore().string());
      configfile->set(RMV_GRUPPO,     cmsstr.gruppo());
      configfile->set(RMV_CONTO,      cmsstr.conto());
      configfile->set(RMV_SOTTOCONTO, cmsstr.sotco());
    }
  }

  //se serve, faccio una riga in pi�
  if(quad > ZERO)
  {
    TString8 paragraph;
    paragraph.format("%d,%d", LF_RMOV, ++nrigaco);
    configfile->set_paragraph(paragraph);
    configfile->set(RMV_NUMRIG, nrigaco);

    TImporto importo(causale2.get("SEZIONE")[0], quad);    

    configfile->set(RMV_ANNOES,     anno);
    configfile->set(RMV_SEZIONE,    causale2.get("SEZIONE"));
    configfile->set(RMV_IMPORTO,    importo.valore().string());
    configfile->set(RMV_GRUPPO,     kmax.gruppo());
    configfile->set(RMV_CONTO,      kmax.conto());
    configfile->set(RMV_SOTTOCONTO, kmax.sotco());
  }

  paragraph.format("%d,%d", LF_RMOVIVA, ++nrigaco);
  configfile->set_paragraph(paragraph);
}

//GESTIONE_INI: metodo che si occupa di generare il file ini e di importarlo effettivamente
void TImporta_fat_for_app::gestione_ini(TAssoc_array& righe, TCommessa_string& kmax, const real& quad, const long numregcg)
{
	TLocalisamfile mov(LF_MOV);
  mov.put(MOV_NUMREG, numregcg);
  mov.remove();
  
  TConfig* configfile = NULL;
  TFilename tmpdir;  tmpdir.tempdir();

  TFilename filename = tmpdir;  
	filename.add("atsfor01.ini");

	configfile = new TConfig(filename, "Transaction");  //setto il paragrafo [Transaction] del file ini 

	configfile->set_paragraph("Transaction");
	configfile->set("Action","INSERT");
	configfile->set("Mode","AUTO");

  const int tipofat = _msk->get_int(F_TIPOFAT);

  switch(tipofat)
  {
  case 1:  importa_fat(righe, kmax, quad, configfile, numregcg);      break;
  case 2:  importa_fat_ric(righe, kmax, quad, configfile, numregcg);  break;
  default: break;
  }

  TString app;
	app << "cg2 -0 -i" << tmpdir << SLASH << "ats*.ini"; 
	TExternal_app primanota(app);
	primanota.run();

  filename.fremove();
}

bool TImporta_fat_for_app::transfer()
{
  //genero il nome del file da caricare
  TString path = _msk->get(F_PATH);

  bool trovato   = false;
  bool esiste    = false;
  bool importato = false;

  TAssoc_array righean;
  TAssoc_array righecg;  
  TCommessa_string kmax;

  for(int i = F_NAME1; i <= F_NAME2; i++)
  {
    if(_msk->get(i).full())
    {
      esiste = true;

      TFilename name = path;
      name.add(_msk->get(i));

      if(name.exist())
      {
        trovato = true;
        importato = importa(name, righean, righecg);
      }
    }
  }

  if(importato)
  {
    real quad;
    TCommessa_string kmax;
    calcola_quadratura(righean, kmax, quad);
    long numregcg = genera_movcg();
    genera_analitica(righean, kmax, quad, numregcg);
    gestione_ini(righecg, kmax, quad, numregcg);
  }
  else
  {
    if(!esiste)
      warning_box(TR("E' necessario specidicare almeno uno dei due file"));
    else 
      if(!trovato)
        error_box(TR("Nessuno dei due file indicati esiste"));
      else
        message_box(TR("Elaborazione terminata"));
  }
  return true;
}


void TImporta_fat_for_app::main_loop()
{
  _msk = new TImporta_fat_for_msk("ps0713600a");  
	
  while (_msk->run() == K_ENTER)
  {		
		if (transfer())
	    message_box(TR("Importazione spese completata"));
		else
			warning_box(TR("L'importazione spese non e' stata completata in modo corretto\nSi prega di correggere gli errori segnalati"));
  }   
}


TImporta_fat_for_app& app() { return (TImporta_fat_for_app&) main_app(); }


int ps0713600 (int argc, char* argv[])
{
  TImporta_fat_for_app main_app;
  main_app.run(argc, argv, TR("Importazione Spese"));
  return true;
}