#include <applicat.h>
#include <automask.h>
#include <defmask.h>
#include <execp.h>
#include <modaut.h>
#include <progind.h>
#include <relation.h>
#include <tabutil.h>

#include <clifo.h>
#include <cfven.h>
#include <doc.h>
#include <mov.h>
#include "../ca/calib01.h"
#include "../cg/cgsaldac.h"
#include "../ef/ef0301.h"

#include "ps0017.h"
#include "ps0017100a.h"

///////////////////////////////////////////////////////////
// Main Mask
///////////////////////////////////////////////////////////

class TPE_mask : public TAutomask
{
  TRelation* _rel;
  bool _spork;
  bool _is_new;
	int _start[200];
	int _end[200];

protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  bool on_sheet_event(TOperable_field& o, TField_event e, long jolly);
	int insert_anal_fields(TMask& m, int page, int lf, int& y, short& dlg, short& dlgd, bool required);
  void insert_anal_page();
  const TString& get_anal_field(const int lf) const;

public:
  bool calc_residual(const TRiga_scadenze& scad, real& impres, real& imppag, 
                     char& accsal, int& rdist, int& reff, 
                     bool& partially_unassigned, int& tipopag, bool is_old = false) const;
  bool fill_row(const TRiga_scadenze& rs, TToken_string& row, bool& partially_unassigned, bool force, bool is_new);
  bool fill_rate();
  int insert_row(const TToken_string& row);
  bool fill_distinta(bool clear_all = false);
  void sort_sheet();
  void update_total();

  TRectype& new_row_effetto(TDistinta& dist, char tipocf, long codcf,
                            int& rigadist, int& rigaeff) const;
  long get_free_num() const;
  void save_rate();
  void print();
 
  TPE_mask();
  virtual ~TPE_mask();
};

// Calcola il residuo di una rata tenendo conto anche degli eventuali
// effetti non ancora contabilizzati
bool TPE_mask::calc_residual(const TRiga_scadenze& scad, 
                             real& impres, real& imppag, 
                             char& accsal, int& rdist, int& reff, 
                             bool& partially_unassigned, int& tipopag,
                             bool is_old) const
{                            
  tipopag = scad.get_int(SCAD_TIPOPAG);
  const bool valuta = scad.in_valuta();
  const char tipocf = scad.get_char(SCAD_TIPOCF);

  TImporto importo = scad.residuo(TRUE);
  importo.normalize(tipocf == 'C' ? 'D' : 'A');

  impres = importo.valore();
  imppag = ZERO;
  accsal = 'A'; 
  rdist = reff = 0;
  partially_unassigned = FALSE;  
  
  // Contolla se ci sono pagamenti non assegnati da gestire
  const TRiga_partite& fattura = scad.riga();
  TPartita& partita = fattura.partita();
  const TRecord_array& unassigned = partita.unassigned();
  if (unassigned.rows() > 0)  
  {
    real tot_unassigned;  // Totale pagamenti non assegnati
    for (int u = unassigned.last_row(); u > 0; u = unassigned.pred_row(u))
    {
      const real imp = unassigned[u].get(valuta ? PAGSCA_IMPORTOVAL : PAGSCA_IMPORTO);
      tot_unassigned += imp;
    } 
    
    // Scala non assegnati dalle rate precedenti ancora aperte
    const int nrata = scad.get_int(SCAD_NRATA);
    for (int r = 1; r < nrata; r++)
    {                                               
      if (!fattura.rata(r).chiusa())   
      {
        TImporto res = fattura.rata(r).residuo(TRUE);
        tot_unassigned -= res.valore();
        if (tot_unassigned <= ZERO)
          break;
      }                                             
    }
    // Scala non assegnati dalla rata corrente
    if (tot_unassigned > ZERO)
    {
      if (tot_unassigned >= impres)
        impres = ZERO;
      else
        impres -= tot_unassigned;
      partially_unassigned = TRUE;  
    }
  }
  
  // Se c'e' ancora un residuo calcola totale pagato con effetti
  if (!impres.is_zero())
  {
    TRectype& filter = _rel->curr();
    const char* field[] = { SCAD_ANNO, SCAD_NUMPART, SCAD_NRIGA, SCAD_NRATA, NULL };
    for (int i = 0; field[i]; i++)
      filter.put(field[i], scad.get(field[i]));
    TCursor cur(_rel, "", 2, &filter, &filter);  // Elenco effetti su questa rata
  
    const long codcf = scad.get_long(SCAD_SOTTOCONTO);
    TString80 expr; 
    expr << '(' << LF_EFFETTI << "->EFFCONT!=\"X\")&&";
    expr << '(' << LF_EFFETTI << "->TIPOCF==\"" << tipocf << "\")&&";
    expr << '(' << LF_EFFETTI << "->CODCF==\"" << codcf << "\")";
    cur.setfilter(expr, TRUE);
    const long items = cur.items();
    if (items > 0L)
    {
      cur.freeze();
      const TRectype& riga = cur.curr();
      const TRectype& effe = cur.curr(LF_EFFETTI);
      TString8 key = get(F_TIPODIST); key << get(F_DIST);
      for (cur = 0L; cur.pos() < items; ++cur)
      {
        TString8 effkey = effe.get(EFF_TIPODIST); effkey << effe.get(EFF_NDIST);
        if (effkey == key)
        {
          rdist = effe.get_int(EFF_NRIGADIST);
          reff = riga.get_int(REFF_NRIGATR);
          accsal = riga.get_char(REFF_ACCSAL);
          imppag += riga.get_real(valuta ? REFF_IMPORTOVAL : REFF_IMPORTO);
          tipopag = effe.get_int(EFF_TIPOPAG);
        }
        else
        {
          if (riga.get_char(REFF_ACCSAL) == 'S')
          {
            impres = ZERO;
            if (is_old)
              return true; // Devo farla vedere
            break;
          }     
          impres -= riga.get_real(valuta ? REFF_IMPORTOVAL : REFF_IMPORTO);
        }
      }
    }
  }
  
  return !impres.is_zero();
}

void TPE_mask::update_total()
{
  real tot;
  TSheet_field& s = sfield(F_SHEET);
  const int postot = s.cid2index(F_IMPORTO);
  FOR_EACH_SHEET_ROW_BACK(s, r, row)
    tot += real(row->get(postot));
  set(F_TOTAL, tot);
  if (tot==ZERO)
  	disable(DLG_SAVEREC);
 	else
 		enable(DLG_SAVEREC); 	
}

bool TPE_mask::fill_row(const TRiga_scadenze& rs, TToken_string& row, bool& partially_unassigned, bool force, bool is_old)
{
  const int tipopageff = get_int(F_TIPOPAGEFF);
  int tipopag;
  real impres, imppag;
  char accsal; int rigadist, rigaeff;
  row.cut(0);
  bool ok = calc_residual(rs, impres, imppag, accsal, rigadist, rigaeff, partially_unassigned, tipopag, is_old) || force;
  if (ok)
    ok = tipopag == tipopageff || tipopageff == 0;
  if (ok)
  {           
    row.add(imppag.string());
    row.add(accsal);
    row.add(impres.string());
    row.add(rs.get_long(SCAD_SOTTOCONTO));
    TString16 cod; cod << rs.get_char(SCAD_TIPOCF) << '|' << rs.get_long(SCAD_SOTTOCONTO);
    row.add(cache().get(LF_CLIFO, cod, CLI_RAGSOC));
    row.add(rs.get(SCAD_ANNO));
    row.add(rs.get(SCAD_NUMPART));
    row.add(rs.get(SCAD_NRIGA));
    row.add(rs.get(SCAD_NRATA));
    row.add(rs.get(SCAD_DATASCAD));
    row.add(rigadist);
    row.add(rigaeff);
    switch (tipopag)
    {              
    case 1:row.add(TR("Rimessa Diretta")); break;
    case 2:row.add(TR("Tratta")); break;
    case 3:row.add(TR("Ricevuta Bancaria")); break;
    case 4:row.add(TR("Cessione")); break;
    case 5:row.add(TR("Pagher�")); break;
    case 6:row.add(TR("Lettera di credito")); break;
    case 7:row.add(TR("Tratta accettata")); break;
    case 8:row.add(TR("Rapporti interbancari diretti")); break;
    case 9:row.add(TR("Bonifico")); break;
    default: break;
    }
  }
  return ok;
}

HIDDEN long row_compare(TToken_string& r1, TToken_string& r2, const int sort_mode)
{
  long cmp = 0;
  if (sort_mode == 1)
  {
    const TDate d1 = r1.get(F_DATA-FIRST_FIELD);
    const TDate d2 = r2.get(F_DATA-FIRST_FIELD);
    cmp = d1 - d2;
    if (cmp != 0)
      return cmp;   // else normal comparing
  }
  else if (sort_mode == 2)
  {
    const real i1 = r1.get(F_RESIDUO-FIRST_FIELD);
    const real i2 = r2.get(F_RESIDUO-FIRST_FIELD);
		const real diff = i1 - i2;
		cmp = diff.sign();
    if (cmp != 0)
      return cmp;   // else normal comparing
  }
  for (int i = 3; cmp == 0 && i <= 8; i++)
  {
    if (i == 4) continue;
    if (i == 6)
    {
      TString16 p1; r1.get(i, p1);
      TString16 p2; r2.get(i, p2);
      cmp = p1.compare(p2);
    }
    else
    {
      long n1; r1.get(i, n1);
      long n2; r2.get(i, n2);
      cmp = n1 - n2;
    }
  }

  return cmp;
}

int TPE_mask::insert_row(const TToken_string& row)
{                            
  const int sort_mode = get_int(F_SORT);
  TString_array& sheet = sfield(F_SHEET).rows_array();
  int first = 0;
  int last = sheet.items()-1;
  
  long cmp = -1;
  int cur = 0;
  while (first <= last)
  {
    cur = (first+last) / 2;
    TToken_string& curr = sheet.row(cur);
    cmp = row_compare((TToken_string&)row, curr, sort_mode);
    if (cmp == 0)
      break;
    if (cmp > 0)  
      first = cur+1;
    else
      last = cur-1;  
  }
  if (cmp)
  {
    if (cmp > 0)
      cur++;
    sheet.insert(row, cur);
  }
  return cur;
}

void TPE_mask::sort_sheet()
{
  TWait_cursor hourglass;
  const int sort_mode = get_int(F_SORT);
  
  TString_array& rows = sfield(F_SHEET).rows_array();
  for (int i = 0; i < rows.items()-1; i++)
  {
    for (int j = i+1; j < rows.items(); j++)
    {
      TToken_string& r1 = rows.row(i);
      TToken_string& r2 = rows.row(j);
      if (row_compare(r1, r2, sort_mode) > 0)
        rows.swap(i, j);
    }
  }
}

const TString& TPE_mask::get_anal_field(const int lf) const
{
  TString80 stringona;
  TString80 workstring;
	for (short id = _start[lf]; id <= _end[lf]; id++)
  {
    const TEdit_field& f = efield(id);
    workstring = f.get();
    workstring.rpad(f.size());
		stringona << workstring;
  }
  stringona.rtrim();
  return get_tmp_string() = stringona;
}

bool TPE_mask::fill_rate()
{
  const long ndist = get_long(F_DIST);
  const TDate dadata = get(F_DA_DATA);
  const TDate adata = get(F_A_DATA);     
  const TString& codval = get(F_CODVAL);
  TSheet_field& sheet = sfield(F_SHEET);
  sheet.destroy();

  TString commessa;
	TString cdc;
	TString fase;
	TRelation rel(LF_PARTITE);
  TRectype filter(LF_PARTITE); 
  filter.put(PART_TIPOCF, "C");
  filter.put(PART_GRUPPO, 0);
  filter.put(PART_CONTO, 0);
//  filter.put(PART_SOTTOCONTO, get(F_CLIFO));
  
  TString filtro;
  filtro << "(CHIUSA!=\"X\")&&(CODVAL==\"" << codval << "\")";
  TCursor partite(&rel, filtro, 1, &filter, &filter);
	TLocalisamfile mov(LF_MOV);
	TLocalisamfile doc(LF_DOC);

  commessa = get_anal_field(LF_COMMESSE);
  cdc = get_anal_field(LF_CDC);
  fase = get_anal_field(LF_FASI);

  const long items = partite.items();
  partite.freeze();
  if (items > 0)
  {
    TRectype& partita = partite.curr();
  
    TProgind pi(items, "Caricamento partite aperte", TRUE, TRUE);
  
    long last_cf = 0;
    int last_year = 0;
    TString16 last_game; 
    
    TToken_string row;
    
    for (partite = 0L; partite.pos() < items; ++partite)
    {
      pi.addstatus(1);
      if (pi.iscancelled())
        break;
      if (sheet.items() > 900)  // Anche troppe righe
        break;
      
      const long cur_cf = partita.get_long(PART_SOTTOCONTO);
      const int cur_year = partita.get_int(PART_ANNO);
      const TString& cur_game = partita.get(PART_NUMPART);
      if (cur_cf == last_cf && cur_year == last_year && cur_game == last_game)
        continue;
      last_cf   = cur_cf;
      last_year = cur_year;
      last_game = cur_game;
  
      const long numreg = partita.get_long(PART_NREG);
			if (numreg <= 0L)
				continue;
			mov.put(MOV_NUMREG, numreg);
			if (mov.read() != NOERR)
				continue;
			doc.put(DOC_PROVV, mov.get(MOV_DPROVV));
			doc.put(DOC_ANNO, mov.get(MOV_DANNO));
			doc.put(DOC_CODNUM, mov.get(MOV_DCODNUM));
			doc.put(DOC_NDOC, mov.get(MOV_DNDOC));
			if (doc.read() != NOERR)
				continue;
			if (commessa.full() && commessa != doc.get(DOC_CODCMS))
				continue;
			if (cdc.full() && cdc != doc.get(DOC_CODCOSTO))
				continue;
			if (fase.full() && fase != doc.get(DOC_FASCMS))
				continue;
			
			const TPartita game(partita);
      const int last = game.last();
      for (int riga = game.prima_fattura(); riga > 0 && riga <= last; riga = game.succ(riga))
      {
        const TRiga_partite& rp = game.riga(riga);
        if (rp.is_fattura())
        {
          for (int rata = 1; rata <= rp.rate(); rata++)
          {
            const TRiga_scadenze& rs = rp.rata(rata);
            if (!rs.chiusa())
            {
              const TDate data = rs.get(SCAD_DATASCAD);
              if (data >= dadata && (!adata.ok() || data <= adata))
              {             
                bool partially_unassigned;
                if (fill_row(rs, row, partially_unassigned, false, false))
                {
                  const int numrow = sheet.items();
                  sheet.row(numrow) = row;            
                  if (partially_unassigned)
                    sheet.disable_cell(numrow, 1);  // Non permette di mettere a saldo
                }
              }  
            } // if rata aperta
          } // for ogni rata
        } // if e' una fattura
      } // for ogni fattura
    } // for ogni partita
  } // if ci sono partite 
  return fill_distinta(false);
}

bool TPE_mask::fill_distinta(bool clear_all)
{             
  TWait_cursor hourglass;
  TSheet_field& sheet = sfield(F_SHEET);   
  if (clear_all) 
    sheet.destroy();
  const char tdist = get(F_TIPODIST)[0];
  const long ndist = get_long(F_DIST);
  TDistinta dist(tdist, ndist);
  const bool dist_cont = dist.contabilizzata();
  if (dist.items() > 0)
  {
    TToken_string row;
    for (int e = 0; e < dist.items(); e++)
    {
      const TEffetto& eff = dist[e];
      const TBill clifo(0, 0, eff.get_long(EFF_CODCF), eff.get_char(EFF_TIPOCF));
      if (e==0)
        set(F_TIPOPAG, eff.get_int(EFF_TIPOPAG));
      for (int r = 1; r <= eff.rows_r(); r++)
      {
        const TRectype& reff = eff.row_r(r);
        const int anno = reff.get_int(REFF_ANNO);
        const TString16 part = reff.get(REFF_NUMPART);
        const int nriga = reff.get_int(REFF_NRIGA);
        const int nrata = reff.get_int(REFF_NRATA);
        TPartita game(clifo, anno, part);            
        if (game.esiste(nriga, nrata))
        {
          TRiga_scadenze& rs = game.rata(nriga, nrata); 
          bool partially_unassigned;
          if (fill_row(rs, row, partially_unassigned, dist_cont, clear_all))
          {
            const int numrow = insert_row(row);
            if (partially_unassigned)
              sheet.disable_cell(numrow, 1); // Non e' possibile mettere saldo
            if (dist_cont)
              sheet.disable_cell(numrow, -1); // Non e' possibile modificare nulla
          }
        }
        else
        {
          TString msg; 
          msg << "La riga " << (e+1) << " della distinta " << ndist 
              << " si riferisce ad una rata o partita non valida:\n" 
              << clifo.tipo() << ' ' << clifo.codclifo() << ' ' << anno << ' ' 
              << part << " Riga " << nriga << " Rata " << nrata << "\n"
              << "Si desidera eliminare l'effetto?";
          if (yesno_box(msg))
          {
            TLocalisamfile righe(LF_REFFETTI);
            righe.curr() = reff;
            if (righe.remove() != NOERR)
              error_box("Errore di cancellazione riga effetto: %d", righe.status());
          }
        }
      }
    }
  } 
  
  if (get_bool(F_SORT))
    sort_sheet();
  
  sheet.force_update();
  update_total();
  _spork = FALSE;
  
  const bool full = sheet.items() > 0;
  if (full) 
  {
    disable(-3);
    disable(-4);
  }
  enable(-5, full && !dist_cont);
  show(F_UNCONTABIL, dist_cont);

  return full;
}

TRectype& TPE_mask::new_row_effetto(TDistinta& dist, char tipocf, long codcf,
                                    int& rigadist, int& rigaeff ) const
{
  TString16 codice; codice << tipocf << '|' << codcf;
  const bool ragg = !cache().get(LF_CFVEN, codice, CFV_RAGGEFF).blank();

  TEffetto* neweff = NULL;
  if (ragg)
  {
    for (int i = 0; i < dist.items(); i++)
    {
      TEffetto& eff = dist[i];
      if (eff.get_char(EFF_TIPOCF) == tipocf &&
          eff.get_long(EFF_CODCF) == codcf)
      {
        neweff = &eff;
        rigadist = i+1;
        break;
      }
    }
  }
  if (neweff == NULL)
  {
    neweff = new TEffetto;
    dist.righe().add(neweff);
    rigadist = dist.items();
    neweff->put(EFF_TIPOCF, tipocf);
    neweff->put(EFF_CODCF, codcf);
    neweff->put(EFF_CODVAL, get(F_CODVAL));
    neweff->put(EFF_CAMBIO, get(F_CAMBIO));
    neweff->put(EFF_DATACAMBIO, get(F_DATACAMBIO));
  }
  rigaeff = neweff->rows_r()+1;
  return neweff->row_r(rigaeff, TRUE);
}

void TPE_mask::save_rate()
{
  const char tipodist = get(F_TIPODIST)[0];
  long numdist = get_long(F_DIST);
  if (_is_new)
  {
    TLocalisamfile f(LF_EFFETTI); f.setkey(4);
    while (TRUE)
    { 
      f.put(EFF_TIPODIST, tipodist);
      f.put(EFF_NDIST, numdist);
      f.put(EFF_NRIGADIST, 1);
      if (f.read() != NOERR)
        break;
      const long nd = get_free_num();
      warning_box("La distinta %ld � gi� presente: verr� rinumerata in %ld", numdist, nd);
      numdist = nd;
    }
    _is_new = FALSE;
  }

  TDistinta distinta(tipodist, numdist, _lock);

  const TString& codval = get(F_CODVAL);
  const real cambio = get(F_CAMBIO);
  const bool eurocambio = get_bool(F_EURO);

  bool zeroes = FALSE; // Esistono righe effetto da cancellare

  TSheet_field& sheet = sfield(F_SHEET);
  FOR_EACH_SHEET_ROW(sheet, r, row)
  {
    real imp = row->get(sheet.cid2index(F_IMPORTO));
    real impval;
    if (codval.not_empty() && !imp.is_zero())
    {
      impval = imp;
      TCurrency c(impval, codval, cambio, eurocambio ? _exchange_contro : _exchange_base);
      c.change_to_firm_val();
      imp = c.get_num();
    }
    char accsal  = row->get_char(sheet.cid2index(F_ACCSAL));
    int rigadist = row->get_int(sheet.cid2index(F_RIGADIST));
    int rigaeff  = row->get_int(sheet.cid2index(F_RIGAEFF));
    if (rigaeff > 0 || accsal == 'S' || imp > ZERO)
    {   
      if (rigaeff <= 0)
      {
        long codcf = row->get_long(sheet.cid2index(F_CODCF));
        
        TRectype& reff = new_row_effetto(distinta, 'C', codcf, rigadist, rigaeff);
        reff.put(REFF_ANNO, row->get(sheet.cid2index(F_ANNO)));
        reff.put(REFF_NUMPART, row->get(sheet.cid2index(F_PARTITA)));
        reff.put(REFF_NRIGA, row->get(sheet.cid2index(F_RIGA)));
        reff.put(REFF_NRATA, row->get(sheet.cid2index(F_RATA)));

        TToken_string key;
        key.add("C|0|0");
        key.add(row->get(sheet.cid2index(F_CODCF)));
        key.add(row->get(sheet.cid2index(F_ANNO)));
        key.add(row->get(sheet.cid2index(F_PARTITA)));
        key.add(row->get(sheet.cid2index(F_RIGA)));
        // Trova partita
        const TRectype& part = cache().get(LF_PARTITE, key);
        reff.put(REFF_NFATT, part.get(PART_NUMDOC));
        reff.put(REFF_DATAFATT, part.get(PART_DATADOC));
        reff.put(REFF_IMPFATT, part.get(PART_IMPORTO));

        // Trova rata       
        key.add(row->get(sheet.cid2index(F_RATA)));
        const TRectype& scad = cache().get(LF_SCADENZE, key);
        
        TString8 abi = scad.get(SCAD_CODABI);
        TString8 cab = scad.get(SCAD_CODCAB);
        
        key = "C";
        key.add(row->get(sheet.cid2index(F_CODCF)));
        const TRectype& clifo = cache().get(LF_CLIFO, key);
        TString80 iban = clifo.get(CLI_IBAN);
        
        if (atol(cab) == 0L)
        {
          abi = clifo.get(CLI_CODABI);
          cab = clifo.get(CLI_CODCAB);
        }
        else
        {
          if (iban.starts_with("IT"))
          {
            TString16 abicab; abicab << abi << cab;
            if (iban.mid(5, 10) != abicab)
              iban.cut(0);
          }
        }
        
        TEffetto& eff = distinta[rigadist-1];
        eff.put(EFF_DATASCAD, scad.get(SCAD_DATASCAD));
        eff.put(EFF_CODABI, abi);
        eff.put(EFF_CODCAB, cab);
        eff.put(EFF_IBAN, iban);
      }

      TEffetto& eff = distinta[rigadist-1];
      TRectype& reff = eff.row_r(rigaeff); 
      real oldimp = reff.get(REFF_IMPORTO);
      real oldimpval = reff.get(REFF_IMPORTOVAL);
      reff.put(REFF_IMPORTO, imp);
      reff.put(REFF_IMPORTOVAL, impval);
      reff.put(REFF_ACCSAL, accsal);

      if (imp.is_zero() && accsal != 'S')
      {
        reff.put(REFF_ACCSAL, 'Z'); // Segna la riga come NULLA
        zeroes = TRUE;
      }
    }
  }

  // Ricalcola totali righe ed elimina le righe NULLE
  TLocalisamfile f(LF_EFFETTI); f.setkey(1);
  for (int rd = distinta.items()-1; rd >= 0; rd--)
  {
    TEffetto& eff = distinta[rd];
    real totimp, totimpval;
    for (int i = eff.rows_r(); i > 0; i--)
    {
      const TRectype& reff = eff.row_r(i);
      if (reff.get_char(REFF_ACCSAL) == 'Z')
      {                            
        eff.destroy_row_r(i, TRUE);
      }
      else
      {
        totimp += reff.get_real(EFF_IMPORTO);
        totimpval += reff.get_real(EFF_IMPORTOVAL);
      }  
    }
    if (eff.rows_r() == 0) 
    {                
      int err = eff.read(f, eff, _lock);    
      if (err == NOERR)
        err = eff.remove(f);
      if (err == NOERR)
        distinta.righe().destroy(rd, TRUE);        
      else
        error_box("Errore %d durante la cancellazione della riga %d", err, rd+1);
    }  
    else
    {
      eff.put(EFF_TIPOPAG, get(F_TIPOPAG));
      eff.put(EFF_IMPORTO, totimp);
      eff.put(EFF_IMPORTOVAL, totimpval);
      eff.put(EFF_CODVAL, codval);
      eff.put(EFF_CAMBIO, cambio);
      eff.put(EFF_CONTROEURO, eurocambio);
      eff.put(EFF_DATACAMBIO, get(F_DATACAMBIO));
      eff.put(EFF_SPESE, get(F_SPESE));
    }  
  }

  TToken_string dati_dist;
  dati_dist.add(tipodist);
  dati_dist.add(numdist);
  dati_dist.add(get(F_DATADIST));
  dati_dist.add(get(F_ABI));
  dati_dist.add(get(F_CAB));
  distinta.write(FALSE, &dati_dist);
}     

void TPE_mask::print()
{                             
  TString16 cmd;         
  cmd << "ef0 -4 " << get(F_TIPODIST) << ' ' << get(F_DIST);
  TExternal_app app(cmd);
  app.run();
}

bool TPE_mask::on_sheet_event(TOperable_field& o, TField_event e, long jolly)
{
  TMask& m = o.mask();
  switch (o.dlg())
  {
  case F_IMPORTO:
    if (e == fe_modify)
    {
      TMask_field& as = m.field(F_ACCSAL);
      if (as.enabled())
      {
        const real imp = m.get(F_IMPORTO);
        const real res = m.get(F_RESIDUO);
        as.set(imp >= res ? "S" : "A");
      }
    }
    break;
  case F_PAGA:
    if (e == fe_button)
    {
      TMask& m = o.mask();
      if (m.get_real(F_IMPORTO).is_zero())
      {
        m.set(F_IMPORTO, m.get(F_RESIDUO));
        TMask_field& as = m.field(F_ACCSAL);
        if (as.enabled()) 
          as.set("S");
      }
      else
      {
        m.reset(F_IMPORTO);
        m.reset(F_ACCSAL);
      }       
      _spork = TRUE;
    }
    break;
  case F_SHEET:
    switch(e)
    {
    case se_query_add:
      {
        TOperable_field& ronaldo = (TOperable_field&)field(F_AGGIORNA);
        if (ronaldo.active())
          on_field_event(ronaldo, fe_button, 0);
      }
      return FALSE;
    case se_notify_modify:
      _spork = TRUE;
      update_total();
      break;
    default:
      break;
    }
    
  default:
    break;
  }
  return TRUE;
} 

long TPE_mask::get_free_num() const
{
  TLocalisamfile eff(LF_EFFETTI); eff.setkey(4);
  TRectype& curr = eff.curr();        
  const TString& tipodist = get(F_TIPODIST);
  curr.put(EFF_TIPODIST, tipodist);
  curr.put(EFF_NDIST, 99999L);
  const int err = eff.read(_isgreat);
  long n = 1L;
  if (err != _isemptyfile)
  {                 
    if (err == NOERR)
      eff.prev();
    if (curr.get(EFF_TIPODIST) == tipodist)
      n += curr.get_long(EFF_NDIST);
  }
  return n;
}

bool TPE_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case DLG_NEWREC:
    {
      const long n = get_free_num();
      set(F_DIST, n);
      disable(F_DIST);
      disable(DLG_NEWREC);
      set_focus_field(F_ABI);
      _is_new = TRUE;
      _spork = FALSE;
    }
    break;
  case F_DIST:
    if (e == fe_modify)
    {
      TEdit_field& ef = (TEdit_field&)o;
      bool ok = ef.check();
      if (ok)
      {
        const TCursor& cur = *ef.browse()->cursor();
        ok = cur.ok();
        if (ok)
        {
          const TRectype& rec = cur.curr();
          if (rec.get_bool(EFF_EFFCONT))
          {
            warning_box("La distinta %s � gi� stata contabilizzata,\n"
                        "per cui non � possibile modificarla", (const char*)ef.get());
            show(F_UNCONTABIL);
          }
          ok = fill_distinta(true);
        }
      }
      _is_new = !ok;       // Memorizza se � una distinta nuova
      enable(-3, _is_new); // Abilita tipo, cliente, valuta, ecc., solo se nuova
      enable(F_TIPOPAG, _is_new);
    }
    break;
  case F_CODVAL:
    if (e == fe_modify && !o.empty())
    {
      TTable cam("CAM");
      cam.put("CODTAB", o.get());
      int err = cam.read(_isgteq);
      if (err != NOERR || cam.get("CODTAB").compare(o.get(), 3) != 0)
      {
        const TRectype& val = cache().get("%VAL", o.get());
        TString16 str = val.get("S4");
        if (str.blank()) str = val.get("R10");
        set(F_CAMBIO, str);
      }  
    }
    break;  
  case F_AGGIORNA:
    if (e == fe_button)
    {
      TEdit_field& numdist = efield(F_DIST);
      if (!numdist.empty())
      {
        bool go = TRUE;
        if (!numdist.enabled() && sfield(F_SHEET).items() > 0)
        {     
          go = check_fields();
          if (go && _spork && yesno_box("Si desidera registrare la distinta?"))
            save_rate();
        }
        if (go)
          go = fill_rate();
      }
      else
        return error_box("E' necessario specificare un numero di distinta");
    }
    break; 
  case F_CONTABILI:
    if (e == fe_button)
    {
      const char tipodist = get(F_TIPODIST)[0];
      const long numdist = get_long(F_DIST);
      TString16 cmd;    
      cmd << "ef0 -7 " << tipodist << ' ' << numdist;
      TExternal_app app(cmd);
      if (app.run() == 0)
      { 
        long numreg = 0;   // Numero di registrazione contabile
        TLocalisamfile eff(LF_EFFETTI);
        eff.setkey(4);
        eff.put(EFF_TIPODIST, tipodist);
        eff.put(EFF_NDIST,numdist);
        eff.put(EFF_NRIGADIST, 1); // Cerco prima riga distinta
        if (eff.read() == NOERR)   
          numreg = eff.get_long(EFF_NUMREG);
        if (numreg > 0)
          message_box("La distinta %ld e' stata contabilizzata col movimento %ld",
                      numdist, numreg);
        else
          warning_box("La distinta %ld non e' stata contabilizzata", numdist);
      }
      stop_run(K_ESC);
    }
    break;
  case F_UNCONTABIL:
    if (e == fe_button)
    {
      const char tipodist = get(F_TIPODIST)[0];
      const long numdist = get_long(F_DIST);
      if (tipodist > ' ' && numdist > 0)
      {
        TRelation rel(LF_EFFETTI);
        TRectype& rec = rel.curr();
        rec.put(EFF_TIPODIST, tipodist);
        rec.put(EFF_NDIST,numdist);
        TCursor cur(&rel, "", 4, &rec, &rec);
        const TRecnotype items = cur.items();
        cur.freeze();
        
        TAssoc_array movs;
        for (cur = 0; cur.pos() < items; ++cur)
        {
          movs.add(rec.get(EFF_NUMREG));
          rec.zero(EFF_EFFCONT);
          rec.zero(EFF_NUMREG);
          rel.rewrite();
        }
        
        if (movs.items() > 0)
        { 
          TString msg;
          msg = "E' necessario cancellare i seguenti movimenti contabili:\n";
          FOR_EACH_ASSOC_OBJECT(movs, hash, key, obj)
            msg << key << ' ';
          warning_box(msg);
        }
        stop_run(K_ESC);

      }
    }
    break;
  case DLG_DELREC:
    if (e == fe_button && yesno_box("Confermare l'eliminazione della distinta %ld", 
                                    get_long(F_DIST)))
    {                
      TSheet_field& sheet = sfield(F_SHEET);
      FOR_EACH_SHEET_ROW(sheet, r, row)
      {
        row->add(" ", 0);
        row->add("A", 1);
      }
      save_rate();
    }  
    break;
  case DLG_SAVEREC:
    if (e == fe_button)
    {   
      if (check_fields())
      {
        save_rate();
        fill_distinta(true);
        disable(F_TIPOPAG);
      }
    }
    break;
  case DLG_PRINT:
    if (e == fe_button)
    {
      print();
      return FALSE;
    }
    break;
  case F_SORT:
    if (e == fe_modify)
    {
      TSheet_field& s = sfield(F_SHEET);
      if (s.items() > 0)
      {
        sort_sheet();
        s.force_update();
      }
    }
    break;
  case F_SHEET:
    return on_sheet_event(o, e, jolly);
  default:
    if (jolly > 0)
      return on_sheet_event(o, e, jolly);
    break;
  }
  return TRUE;
} 

int TPE_mask::insert_anal_fields(TMask& m, int page, int lf, int& y,
		  													 short& dlg, short& dlgd, bool required)
{
	const int h = ca_create_fields(m, page, lf, 2, y, dlg, dlgd);

	y += h+1;
	dlg += h; dlgd += h;
	return h;
}

void TPE_mask::insert_anal_page()
{
	add_groupbox(DLG_NULL, 1, TR("Contabilita' Analitica"), 1, 4, 78, 14);
	
	const TMultilevel_code_info& fasinfo = ca_multilevel_code_info(LF_FASI);
	const bool use_fsc = fasinfo.levels() > 0;
	TConfig& ini = ca_config();
	const bool fsc_req = use_fsc && ini.get_bool("FscRequired");

	int y = 7;
	short dlg = F_CDC1; // id del primo campo da generare
	short dlgd = F_DESCDC1;

	for (int i = 0; i < 2; i++)
	{
		const TString& level = ini.get("Level", NULL, i+1);	// Legge il livello 1 o 2
		if (level == "CDC")																	// Crea centro di costo 
		{
		  _start[LF_CDC] = dlg;
			_end[LF_CDC] = dlg + ca_multilevel_code_info(LF_CDC).levels()-1;
			if (use_fsc && fasinfo.parent() == LF_CDC)
			{
			  _start[LF_FASI] = _end[LF_CDC] + 1;
				insert_anal_fields(*this, 1, LF_FASI, y, dlg, dlgd, fsc_req);
				_end[LF_FASI] = dlg - 1;
			}
			else
			{
				const bool cdc_req = ini.get_bool("CdcRequired");

				insert_anal_fields(*this, 1, LF_CDC, y, dlg, dlgd, cdc_req);
			}
		} else
		if (level == "CMS")																	 // Crea commessa
		{
		  _start[LF_COMMESSE] = dlg;
			_end[LF_COMMESSE] = dlg + ca_multilevel_code_info(LF_COMMESSE).levels()-1;
			if (use_fsc && fasinfo.parent() == LF_COMMESSE)
			{
			  _start[LF_FASI] = _end[LF_COMMESSE] + 1;
				insert_anal_fields(*this, 1, LF_FASI, y, dlg, dlgd, fsc_req);
				_end[LF_FASI] = dlg - 1;
			}
			else
			{
				const bool cms_req = ini.get_bool("CmsRequired");

				insert_anal_fields(*this, 1, LF_COMMESSE, y, dlg, dlgd, cms_req);
			}
		}
	}
 	if (use_fsc && fasinfo.parent() <= 0)
	{
	  _start[LF_FASI] = dlg;
		_end[LF_FASI] = dlg + ca_multilevel_code_info(LF_FASI).levels()-1;
		insert_anal_fields(*this, 1, LF_FASI, y, dlg, dlgd, fsc_req);
	}
}

TPE_mask::TPE_mask() : TAutomask("ps0017100a")
{ 
  _rel = new TRelation(LF_REFFETTI);
  _rel->add(LF_EFFETTI, "NPROGTR==NPROGTR");
  insert_anal_page();
}

TPE_mask::~TPE_mask() 
{ delete _rel; }


///////////////////////////////////////////////////////////
// Main Program
///////////////////////////////////////////////////////////

class TIncasso_fatture : public TSkeleton_application
{
  TPE_mask* _mask;

virtual const char * extra_modules() const {return "ef";}

public:
	virtual bool create();
  virtual void main_loop();
  virtual void print();
  virtual bool firm_change_enabled() const { return false; }
};

void TIncasso_fatture::main_loop()
{
  open_files(LF_TABCOM, LF_TAB, LF_CLIFO, LF_CFVEN,
             LF_PARTITE, LF_SCADENZE, LF_PAGSCA,
             LF_EFFETTI, LF_REFFETTI, LF_CESS, 0);
  enable_menu_item(M_FILE_PRINT);           
  _mask = new TPE_mask;
  do
  {
    _mask->reset();
    _mask->enable(-3);
    _mask->enable(F_TIPOPAG);
    _mask->enable(-4);
    _mask->disable(-5);
    _mask->disable(DLG_SAVEREC);
    _mask->hide(F_UNCONTABIL);
    _mask->sfield(F_SHEET).destroy();
  }
  while (_mask->run() != K_QUIT);
  
  delete _mask;
  _mask = NULL;
} 

void TIncasso_fatture::print()
{
  if (_mask)
    _mask->print();
}


bool TIncasso_fatture::create()
{
	return TSkeleton_application::create();
}


int ps0017100(int argc, char* argv[])
{
  int n = 0;
  TIncasso_fatture pe;
  pe.run(argc, argv, TR("Disposizioni incassi/pagamenti"));
  return 0;
}