#include <applicat.h>
#include <automask.h>
#include <colors.h>
#include <defmask.h>
#include <modaut.h>
#include <progind.h>
#include <recarray.h>
#include <relation.h>
#include <reputils.h>
#include <tabmod.h>

#include <alleg.h>
#include <attiv.h>
#include <clifo.h>
#include <mov.h>
#include <occas.h>
#include <partite.h>
#include <pconti.h>
#include <rmoviva.h>

#include "fe0100a.h"
#include "felib.h"
#include "../cg/cglib01.h"

const char* const INVALID_NUMDOC = "???????";
const long MANUAL_ROW = 900000L;


///////////////////////////////////////////////////////////
// Utility
///////////////////////////////////////////////////////////

enum TExclusion_mode { em_incluso, em_importo_limite, em_no_allegato, 
                       em_fiscalita_agevolata, em_estero, em_intra,
                       em_art8, em_data_limite, em_passaggi_interni,
                       em_inviato, em_altro };

static const char* mode2string(TExclusion_mode motivo)
{
  const char* msg = "";
  switch (motivo)
  {
  case em_importo_limite     : msg = TR("importo inferiore al limite della comunicazione"); break;
  case em_no_allegato        : msg = TR("Soggetto da non inserire in allegato"); break;
  case em_fiscalita_agevolata: msg = TR("Soggetto residente in stato a fiscalit� agevolata"); break;
  case em_estero             : msg = TR("Soggetto residente all'estero"); break;
  case em_intra              : msg = TR("Movimento intra"); break;
  case em_data_limite        : msg = TR("Data fuori dal limite della comunicazione"); break;
  case em_art8               : msg = TR("Soggetto all'articolo 8 (del dpr 26-10-1972)"); break;
  case em_passaggi_interni   : msg = TR("Passaggi interni"); break;
  case em_inviato            : msg = TR("Inviato l'anno precedente"); break;
  default                    : msg = TR("Altri motivi"); break;
  }
  return msg;
}

///////////////////////////////////////////////////////////
// TDati_rilevanti_array
///////////////////////////////////////////////////////////

class TBase_contract_cache : public TCache
{
  TContratto _c;

protected:
  virtual TObject* key2obj(const char* c);

public:
  const TString& base_contract(const TRectype& a);
};

TObject* TBase_contract_cache::key2obj(const char* c)
{
  const TFixed_string codice (c);
  _c.init(codice);
  return new TString(_c.codice_base());
}

const TString& TBase_contract_cache::base_contract(const TRectype& a)
{
  const char* cod = a.get(ALL_CONTRATTO);
  if (*cod > ' ')
  {
    _c.init(a);
    return *(TString*)objptr(_c.chiave());
  }
  return EMPTY_STRING;
}

class TDati_rilevanti_array : public TObject
{
  TArray _data;
  TBase_contract_cache _base;

protected:
  TExclusion_mode segnala_riga(const TRectype& alleg, TExclusion_mode motivo, TLog_report& log) const;
  const TString& get_base_contract(const TRectype& alleg);

public:
  int items() const { return _data.items(); }
  const TRectype& operator[](int i) { return (const TRectype&)_data[i]; }
  TExclusion_mode add(const TRectype& alleg, bool send_all, TLog_report& log);
  void add(TArray& fatture, TArray& note, bool send_all, TLog_report& log);
};

TExclusion_mode TDati_rilevanti_array::segnala_riga(const TRectype& alleg, TExclusion_mode motivo, TLog_report& log) const
{
  if (motivo > em_importo_limite)
  {
    const TAnagrafica a(alleg);
    TString msg; 
    msg.format(FR("%s %s - Riga %7ld: "), alleg.get_char(ALL_TIPOCF) == 'F' ? TR("For.") : TR("Cli."), 
               (const char*)a.ragione_sociale(), alleg.get_long(ALL_PROGR));
    msg << mode2string(motivo);
    log.log(1, msg);
  }
  return motivo;
}

TExclusion_mode TDati_rilevanti_array::add(const TRectype& alleg, bool send_all, TLog_report& log)
{
  TExclusion_mode ignora = TExclusion_mode(alleg.get_int(ALL_IGNORA));
  if (ignora > em_importo_limite)
    return ignora;

  const real importo = alleg.get_real(ALL_IMPORTO);
  const real imposta = alleg.get_real(ALL_IMPOSTA);
  if (importo.is_zero() && imposta.is_zero())
    return segnala_riga(alleg, em_importo_limite, log);

  const TAnagrafica a(alleg);
  if (a.codice_fiscale().blank() && a.partita_IVA().blank() && a.italiano())
    return segnala_riga(alleg, em_no_allegato, log);
    
  if (fe_is_nota_variazione(alleg))
  {
    TRectype& a = (TRectype&)alleg; // Triste necessit�

    if (!send_all && ignora <= em_importo_limite)
    {
      const int anno = alleg.get_int(ALL_ANNO);
      const real importo = abs(alleg.get_real(ALL_IMPORTO));
      if (importo < fe_importo_limite(anno))
      {
        ignora = segnala_riga(alleg, em_importo_limite, log);
        a.put(ALL_IGNORA, ignora);
      }
    }
    
    if (ignora == em_incluso)
    {
      TDate datarett = a.get(ALL_DATARETT);
      if (datarett.ok())
      {
        if (datarett.year() < 2010)
          ignora = segnala_riga(alleg, em_data_limite, log);
      }
      else
      {
        const int anno = alleg.get_int(ALL_ANNO) - 1;
        a.put(ALL_DATARETT, TDate(31, 12, anno));
        if (anno < 2010)
          ignora = segnala_riga(alleg, em_data_limite, log);
      }

      const TString& numrett = a.get(ALL_NUMRETT);
      if (numrett == INVALID_NUMDOC || !datarett.ok())
        a.zero(ALL_NUMRETT);
    }
  }
  else
  {
    const TString80 contratto = get_base_contract(alleg);
    if (ignora <= em_importo_limite)
    {
      const int anno = alleg.get_int(ALL_ANNO);
      TExclusion_mode new_mode = (alleg.get_real(ALL_IMPORTO) < fe_importo_limite(anno)) ? em_importo_limite : em_incluso;
      if (new_mode == em_importo_limite && contratto.full())
      {
        const TContratto c(alleg);
        real imp, iva;
        if (c.totale_annuale(anno, imp, iva))
          new_mode = (imp < fe_importo_limite(anno)) ? em_importo_limite : em_incluso;
      }
      if (ignora != new_mode)
        ((TRectype&)alleg).put(ALL_IGNORA, ignora = new_mode);
    }
  }

  if (!send_all && ignora != em_incluso)
    return ignora;

  // Creo un nuovo record
  _data.add(alleg);
  return em_incluso;
}

const TString& TDati_rilevanti_array::get_base_contract(const TRectype& alleg)
{
  CHECKD(alleg.num() == LF_ALLEG, "Record non valido ", alleg.num());
  return _base.base_contract(alleg);
}

void TDati_rilevanti_array::add(TArray& fatture, TArray& note, bool send_all, TLog_report& log)
{
  // Cerca di scalare le note dalle relative fatture
  FOR_EACH_ARRAY_ITEM_BACK(note, n, pnota)
  {
    const TRectype& nota = *(const TRectype*)pnota;
    const TString8 numrett = nota.get(ALL_NUMRETT);
    if (numrett.full() && numrett != INVALID_NUMDOC)
    {
      const TDate datarett = nota.get(ALL_DATARETT);
      FOR_EACH_ARRAY_ITEM(fatture, f, pfatt)
      {
        TRectype& fatt= *(TRectype*)pfatt;
        if (fatt.get(ALL_NUMDOC) == numrett && fatt.get_date(ALL_DATAREG) == datarett)
        {
          fatt.add(ALL_IMPORTO, nota.get_real(ALL_IMPORTO));
          fatt.add(ALL_IMPOSTA, nota.get_real(ALL_IMPOSTA));
          note.destroy(n);
          break;
        }
      }
    }
  }
  
  // Cerca di raggruppare le fatture per contratto
  FOR_EACH_ARRAY_ITEM_BACK(fatture, c, pcont)
  {
    const TRectype& cont = *(const TRectype*)pcont;
    const TString80 contratto = get_base_contract(cont);
    if (contratto.full())
    {
      FOR_EACH_ARRAY_ITEM(fatture, f, pfatt) if (f < c)
      {
        TRectype& fatt= *(TRectype*)pfatt;
        if (get_base_contract(fatt) == contratto)
        {
          fatt.add(ALL_IMPORTO, cont.get_real(ALL_IMPORTO));
          fatt.add(ALL_IMPOSTA, cont.get_real(ALL_IMPOSTA));

          const TDate sum_data = fatt.get(ALL_DATAREG);
          const TDate all_data = cont.get(ALL_DATAREG);
          if (all_data > sum_data) 
          {
            fatt.put(ALL_DATAREG, all_data);
            fatt.put(ALL_NUMDOC, cont.get(ALL_NUMDOC));
          }
          fatture.destroy(c);
          break;
        }
      }
    }
  }

  // Aggiunge fatture
  FOR_EACH_ARRAY_ITEM(fatture, nf, pfatt)
  {
    const TRectype& fatt = *(const TRectype*)pfatt;
    add(fatt, send_all, log);
  }
  // Aggiunge note
  FOR_EACH_ARRAY_ITEM(note, nn, pnot)
  {
    const TRectype& nota = *(const TRectype*)pnot;
    add(nota, send_all, log);
  }
}

///////////////////////////////////////////////////////////
// TDati_rilevanti_msk
///////////////////////////////////////////////////////////

class TDati_rilevanti_msk : public TAutomask
{
  TMaskmode _mode;
  bool _sheet_dirty;
  TExclusion_mode _why;
  TLog_report* _log;

protected:
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  void alleg_sort(TSheet_field& s) const;
  void load_sheet();
  bool save_sheet();
  bool save_if_dirty();
  void set_dirty(bool d = true);

  bool send_nota_variazione(const TRectype& alleg, TDati_rilevanti_set& operaz);
  bool send_fatt(const TRectype& alleg, TDati_rilevanti_set& operaz);
  void build_outname(TFilename& n) const;

protected:
  TRecnotype last_user_progr() const;
  TRecnotype nuovo_progr() const;
  bool check_rows(bool show_error);
  void enable_buttons();

  bool send_rec(const TRectype& alleg, TDati_rilevanti_set& operaz);
  TExclusion_mode segnala_movimento(const TRectype& mov, TExclusion_mode motivo);
  
  bool fattura_associata(long numreg_var, TDate& datafatt, TString& numdoc) const;
  bool controlla_mov(TRectype& mrec) const;
  bool azzera_alleg(TAssoc_array& manuali) const;
  void collega_variazioni() const;
  const TString& primo_contratto() const;

public:
  TRecnotype genera_alleg();
  bool elabora_alleg();
  bool send_alleg();
  bool recall_alleg() const;
    
  TExclusion_mode elabora_movimento(const TRectype& mov, TBaseisamfile& falleg);

  bool salva_allegato(const TRectype& mov, TBaseisamfile& falleg, TRecnotype& progr, 
                      const real& corrispettivo, const real& imposta, int tipope);

  TExclusion_mode validate_clifo(const TRectype& mov);
  TExclusion_mode validate_mov(const TRectype& mov);

  TDati_rilevanti_msk() : TAutomask("fe0100a"), _mode(MODE_QUERY), _log(NULL) { load_profile(); set_dirty(false); }
  ~TDati_rilevanti_msk() { save_profile(); }
};

TExclusion_mode TDati_rilevanti_msk::segnala_movimento(const TRectype& mov, TExclusion_mode motivo)
{
  if (_why <= em_importo_limite) // Aggiorna motivo esclusione se attualemte incluso o non rilevante
  {
    _why = motivo;
    if (motivo > em_importo_limite) // Non segnalare pippate
    {
      const long numreg = mov.get_long(MOV_NUMREG);
      const char tipocf = mov.get_char(MOV_TIPO); ;
      const long codcf = mov.get_long(MOV_CODCF);
      const TAnagrafica a(tipocf, codcf, mov.get(MOV_OCFPI));
      TString msg; 
      msg.format(FR("Mov. %7ld %s %s: "), numreg, 
                 tipocf=='F' ? TR("For.") : TR("Cli."), (const char*)a.ragione_sociale());
      msg << mode2string(motivo);
      msg.strip_double_spaces();
      _log->log(1, msg);
    }
  }
  return motivo;
}

TExclusion_mode TDati_rilevanti_msk::validate_clifo(const TRectype& mov)
{
  const char tipocf = mov.get_char(MOV_TIPO);
  const long codcf = mov.get_long(MOV_CODCF);
  const TString16 ocfpi = mov.get(MOV_OCFPI);
  if (tipocf <= ' ' || (codcf <= 0 && ocfpi.blank()))
    return segnala_movimento(mov, em_no_allegato);

  TString8 key; key.format("%c|%ld", tipocf, codcf);
  const TRectype& rec_clifo = cache().get(LF_CLIFO, key);
  const int alleg = rec_clifo.get_int(CLI_ALLEG);

  TString4 stato;
  if (ocfpi.full())
  {
    const TRectype& rec_occas = cache().get(LF_OCCAS, ocfpi);
    stato = rec_occas.get(OCC_STATO);
  }
  else
  {
    if (alleg == 1)
      return segnala_movimento(mov, em_no_allegato);

    if (alleg == 5)
      return segnala_movimento(mov, em_intra);

    stato = rec_clifo.get(CLI_STATOCF);
  }

  if (stato.full())
  {
    if (tipocf == 'C')
    {
      // I clienti privati vanno inclusi anche se residenti in stati esteri in black list
      if (ocfpi.blank() && alleg != 6)
      {
        const TRectype& rec_sta = cache().get("%STA", stato);
        if (rec_sta.get_bool("B0")) 
          return segnala_movimento(mov, em_fiscalita_agevolata);
      }
    }
    else
      return segnala_movimento(mov, em_estero);
  }
  
  return em_incluso;  //se arriva qui il clifo � da considerare
}

TExclusion_mode TDati_rilevanti_msk::validate_mov(const TRectype& mov)
{
  // Ignora eventuale vecchio movimento IVA (ANNOIVA < 2010)
  const int anno = mov.get_int(MOV_ANNOIVA);
  if (anno < 2010)
    segnala_movimento(mov, em_data_limite); 

  // Ignora i movimenti gi� comunicati tramite modello INTRA
  if (!mov.get_real(MOV_CORRLIRE).is_zero() || 
      !mov.get_real(MOV_CORRVALUTA).is_zero())
    segnala_movimento(mov, em_intra);

  return validate_clifo(mov);
}

bool TDati_rilevanti_msk::fattura_associata(long numreg_var, TDate& datafatt, TString& numdoc) const
{
  long numreg_fatt = 0;
  if (main_app().has_module(SCAUT))
  {
    TLocalisamfile partite(LF_PARTITE);
    partite.setkey(2);
    TRectype& part = partite.curr();
    part.put(PART_NREG, numreg_var);
    part.put(PART_NUMRIG, 1);
    if (partite.read() == NOERR) // Ho trovato la partita ora cerco la fattura di riferimento
    {
      int nriga = part.get_int(PART_NRIGA);
      part.zero(PART_NRIGA);     // Uso il record come chiave per leggere l'intera partita
      TRecord_array partita(part, PART_NRIGA);  
      for (nriga = partita.pred_row(nriga); nriga >= 1; nriga = partita.pred_row(nriga))
      {
        const TRectype& riga = partita.row(nriga);
        const int tipomov = riga.get_int(PART_TIPOMOV);
        const long nreg_part = riga.get_long(PART_NREG);
        if (tipomov == 1 && nreg_part > 0) // Fattura
        {
          datafatt = riga.get(PART_DATAREG);
          numdoc = riga.get(PART_NUMDOC);
          break;
        }
      }
    }
  }
  return numdoc.full();
}

TExclusion_mode TDati_rilevanti_msk::elabora_movimento(const TRectype& mov, TBaseisamfile& falleg)
{
  validate_mov(mov);

  const char tipocf = mov.get_char(MOV_TIPO);
  const long codcf = mov.get_long(MOV_CODCF);
  const TString4 tipodoc = mov.get(MOV_TIPODOC);
  const TDate datareg = mov.get_date(MOV_DATAREG);
  const int anno = get_int(F_ANNO);

  const TString& keytok = mov.get(MOV_NUMREG);
  TRecord_array righe_iva(keytok, LF_RMOVIVA);

  real tot_imponibile, tot_imposta;

  //calcolo di imponibile ed imposta di tutte le righe iva del movimento
  for (int r = righe_iva.last_row(); r > 0; r = righe_iva.pred_row(r))
  {
    const TRectype& rmi = righe_iva.row(r);
    const TCodiceIVA ci(rmi.get(RMI_CODIVA));
    int natura_operazione = ci.allegato(tipocf);
    if (natura_operazione <= 0 || natura_operazione > 5)
      continue;

    // Esportazioni
    const bool art_8 = ci.get("S2") == "20" && ci.get("S3") == "1";
    if (art_8)
      segnala_movimento(mov, em_art8);

    const TString4 cod_det = rmi.get(RMI_TIPODET);
    const int tip_det = cod_det == "3" ? 3 : atoi(cache().get("%DET", cod_det, "I0"));
    if (tip_det == 3)
      segnala_movimento(mov, em_passaggi_interni);

    real rmi_imponibile = rmi.get_real(RMI_IMPONIBILE);
    real rmi_imposta = rmi.get_real(RMI_IMPOSTA);

    if (natura_operazione == 4 && rmi_imposta.is_zero()) // se l'imposta non � specificata sullo scontrino ...
      rmi_imposta = ci.scorpora(rmi_imponibile);         // ... scorporo il lordo

    tot_imponibile += rmi_imponibile;
    tot_imposta += rmi_imposta;
  }

  const int modpag = mov.get_int(MOV_MODPAG);
  if (modpag == 1 && _why == em_incluso)
  {
    // Considera solo registrazioni con importo rilevante
    if (abs(tot_imponibile) < fe_importo_limite(anno))
      _why = em_importo_limite; // Non segnalare migliaia di movimenti inutilmente
  }

  const long numreg = mov.get_long(MOV_NUMREG);

  // Registro tutti i dati del cliente e gli importi
  falleg.zero();
  falleg.put(ALL_ANNO, anno);
  falleg.put(ALL_PROGR, numreg);
  falleg.put(ALL_IGNORA, int(_why));
  falleg.put(ALL_TIPOCF, tipocf);
  falleg.put(ALL_CODCF, codcf);
  falleg.put(ALL_OCFPI, mov.get(MOV_OCFPI));
  falleg.put(ALL_DATAREG, mov.get(MOV_DATAREG));
  falleg.put(ALL_NUMDOC, mov.get(MOV_NUMDOC));
  falleg.put(ALL_TIPOPE, tipocf == 'C' ? 1 : 2);
  falleg.put(ALL_IMPORTO, tot_imponibile);
  falleg.put(ALL_IMPOSTA, tot_imposta);
  falleg.put(ALL_MODPAG, modpag);
  if (modpag == 1)
  {
    falleg.put(ALL_DATARETT, mov.get(MOV_DATARETT));
    falleg.put(ALL_NUMRETT, mov.get(MOV_NUMRETT));
  }
  else
    falleg.put(ALL_CONTRATTO, mov.get(MOV_CONTRATTO));

  const int err = falleg.rewrite_write();
  if (err != NOERR)
  {
    TString msg; 
    msg.format(FR("Errore %d di aggiornamento del record %d/%ld sul file %s"), 
               err, anno, numreg, (const char*)falleg.name());
    _log->log(2, msg);
  }

  return _why;
}

// Test di coerenza tra MODPAG, CONTRATTO e NUMRETT
bool TDati_rilevanti_msk::controlla_mov(TRectype& mrec) const
{
  const long numreg = mrec.get_long(MOV_NUMREG);
  TString80 contratto = mrec.get(MOV_CONTRATTO);
 
  int old_modpag = mrec.get_int(MOV_MODPAG);
  int new_modpag = 1;

  bool update = false;

  if (fe_is_nota_variazione(mrec))
  {
    TDate datarett = mrec.get(MOV_DATARETT);
    TString8 numrett = mrec.get(MOV_NUMRETT);
    if (contratto.full())
    {
      mrec.put(MOV_CONTRATTO, contratto.cut(0)); 
      update = true; // Peccato veniale
    }
    if (numrett.blank())  
    {
      if (!fattura_associata(numreg, datarett, numrett))
        numrett = INVALID_NUMDOC;
      mrec.put(MOV_DATARETT, datarett);
      mrec.put(MOV_NUMRETT, numrett);
      update = true;
    }
    const TAnagrafica a(mrec);
    TString msg;
    if (numrett != INVALID_NUMDOC)
    {
      msg.format(FR("Nota n. %ld di %s associata al doc. %s del %s"),
                 numreg, (const char*)a.ragione_sociale(), (const char*)numrett, datarett.string());
      msg.strip_double_spaces();
      _log->log(0, msg);
    }
    else
    {
      msg.format(FR("Nota n. %ld di %s non associata ad una fattura"),
                 numreg, (const char*)a.ragione_sociale());
      msg.strip_double_spaces();
      _log->log(1, msg);
    }
  }
  else
  {
    if (contratto.full())
    {
      const TContratto c(mrec);
      new_modpag = c.modalita_pagamento();
    }
    if (get(MOV_NUMRETT).full() || get(MOV_DATARETT).full())
    {
      mrec.zero(MOV_DATARETT);
      mrec.zero(MOV_NUMRETT);
      update = true; // Peccato veniale
    }

    if (mrec.get(MOV_NUMDOC).blank() || mrec.get(MOV_DATADOC).blank())
    {
      const TAnagrafica a(mrec);
      TString msg;
      msg.format(FR("Movimento n. %ld di %s privo di numero o data documento"),
                  numreg, (const char*)a.ragione_sociale());
      _log->log(1, msg);
    }
  }
  if (old_modpag != new_modpag)
  {
    if (old_modpag > 0) // I vecchi movimenti hanno per forza 0: li perdoniamo!
    {
      const TAnagrafica a(mrec);
      TString msg;
      msg.format(FR("Movimento n. %ld di %s con modalit� di pagamento incongruente: %d->%d"),
                  numreg, (const char*)a.ragione_sociale(), old_modpag, new_modpag);
      msg.strip_double_spaces();
      _log->log(0, msg);
    }
    mrec.put(MOV_MODPAG, new_modpag);
    update = true;
  }
  
  return update;
}

bool TDati_rilevanti_msk::azzera_alleg(TAssoc_array& manuali) const
{
  const int anno = get_int(F_ANNO);
  const TDate data = get(F_DATA);

  TFast_isamfile fast_alleg(LF_ALLEG);
  TFast_isamfile fast_mov(LF_MOV);

  TString query;
  query << "USE " << LF_ALLEG
        << "\nJOIN MOV INTO NUMREG==PROGR"
        << "\nFROM ANNO=" << anno
        << "\nTO ANNO=" << anno << " PROGR=" << MANUAL_ROW;
  TISAM_recordset alleg(query);

  TLocalisamfile& falleg = alleg.cursor()->file();
  TRectype& arec = falleg.curr();

  TLocalisamfile& fmov = alleg.cursor()->file(LF_MOV);
  TRectype& mrec = fmov.curr();

  TString msg; msg << TR("Azzeramento ") << falleg.description() << ' ' << anno;
  TProgind pi(alleg.items(), msg, false);

  manuali.destroy();
  for (bool ok = alleg.move_first(); ok; ok = alleg.move_next())
  {
    pi.addstatus(1);

    const long progr = arec.get_long(ALL_PROGR);
    const long numreg = mrec.get_long(MOV_NUMREG);
    const TDate datareg = mrec.get_long(MOV_DATAREG);
    const int annoiva = mrec.get_long(MOV_ANNOIVA);
    
    bool kill = numreg != progr || annoiva < anno; 
    if (!kill)
    {
      const bool forzata = arec.get_bool(ALL_FORZATURA);
      if (forzata)
        manuali.add(arec.get(ALL_PROGR));
      else
        kill = annoiva == data.year() && datareg > data;
    }
    if (kill)
      falleg.remove(); // Riga generata dalla vecchia versione
  }
  return !manuali.empty();
}

// Cerca l'ultimo numero di riga immesso manualmente
TRecnotype TDati_rilevanti_msk::last_user_progr() const
{
  const int anno = get_int(F_ANNO);

  TRecnotype progr = MANUAL_ROW;
  TString query;
  query << "USE " << LF_ALLEG
        << "\nFROM " << ALL_ANNO << '=' << anno << ' ' << ALL_PROGR << '=' << MANUAL_ROW
        << "\nTO "   << ALL_ANNO << '=' << anno;
  TISAM_recordset alleg(query);
  if (alleg.move_last())
    progr = alleg.get(ALL_PROGR).as_int();
  return progr;
}

TRecnotype TDati_rilevanti_msk::nuovo_progr() const
{
  TRecnotype progr = last_user_progr();

  TSheet_field& righe = sfield(F_RIGHE);
  const int items = righe.items();
  if (items > 0)
  {
    const int col = righe.cid2index(A_RIGA); 
    for (int i = items-1; i >= 0; i--)
    {
      const TRecnotype sheet_progr = atol(righe.cell(i,col));
      if (sheet_progr > progr)
        progr = sheet_progr;
    }
  }

  return progr+1;
}

static int sort_alleg(const TSortable& s1, const TSortable& s2, void* jolly)
{
  const TRectype& a1 = (const TRectype&)s1;
  const TRectype& a2 = (const TRectype&)s2;

  const TString& c1 = a1.get(ALL_CONTRATTO);
  const TString& c2 = a2.get(ALL_CONTRATTO);
  int cmp = c1.compare(c2, -1, true);

  if (cmp == 0 && c1.blank())
  {
    const int n1 = fe_is_nota_variazione(a1);
    const int n2 = fe_is_nota_variazione(a2);
    cmp = n1 - n2;
  }

  if (cmp == 0)
  {
    const TDate d1 = a1.get(ALL_DATAREG);
    const TDate d2 = a2.get(ALL_DATAREG);
    cmp = d1 - d2;
  }

  return cmp;
}

TRecnotype TDati_rilevanti_msk::genera_alleg()
{
  const int anno = get_int(F_ANNO);
  const TDate data_estrazione = get(F_DATA);

  TString str_pi;
  str_pi << TR("Movimenti ") << anno;
  _log = new TLog_report(str_pi);

  TAssoc_array manuali;
  azzera_alleg(manuali);
  
  TRecnotype nprog = 1;

  if (anno >= 2010) // Dummy test for bracing TFast_isamfiles
  {
    TFast_isamfile falleg(LF_ALLEG);
    TFast_isamfile fmov(LF_MOV);

    TString query;
    query << "USE MOV KEY 3 SELECT BETWEEN(DATAREG," << anno << "0101," << data_estrazione.date2ansi() << ")"
          << "\nFROM TIPO=C\nTO TIPO=F";
    TISAM_recordset mov(query);
    TRectype& mov_rec = mov.cursor()->curr();
    const TRecnotype items = mov.items();

    TProgind pi(items, str_pi);
    for (bool ok = mov.move_first(); ok; ok = mov.move_next())
    {
      if (!pi.addstatus(1))
        break;
      _why = em_incluso;

      const int annofe = mov_rec.get_int(MOV_ANNOFE);
      if (annofe >= 2010) // Non elaborae i movimenti gia' inviati in definitivo!
      {
        segnala_movimento(mov_rec, em_inviato);
        continue;
      }

      const TString& key = mov_rec.get(MOV_NUMREG);
      if (manuali.is_key(key))
      {
        manuali.remove(key);
        continue;
      }

      controlla_mov(mov_rec);
      if (fe_is_nota_variazione(mov_rec))
      {
        const TDate datarett = mov_rec.get(MOV_DATARETT);
        if (!datarett.ok() || datarett.year() == anno)
          elabora_movimento(mov_rec, falleg); // Elabora nota di variazione
      }
      else
      {
        const TDate datareg = mov_rec.get(MOV_DATAREG);
        if (datareg.year() == anno)           // Scarta fatture dell'anno dopo   
          elabora_movimento(mov_rec, falleg); 
      }
    }   
  }
  collega_variazioni();

  _log->preview();
  delete _log;
  _log = NULL;

  return nprog;
}

void TDati_rilevanti_msk::collega_variazioni() const
{
  const int anno = get_int(F_ANNO);
  TString query;
  query << "USE ALLEG KEY 2 SELECT (STR(IGNORA<=" << int(em_importo_limite) << "))&&(NUMDOC!=\"\")";
  query << "\nJOIN ALLEG KEY 3 ALIAS 220 INTO ANNO=ANNO TIPOCF=TIPOCF CODCF==CODCF NUMRETT==NUMDOC";
  query << "\nFROM ANNO=" << anno << "\nTO ANNO=" << anno;
  TISAM_recordset fatture(query);

  TProgind pi(fatture.items(), TR("Collegamento note di variazione"), false, true);
  TRelation& rel = *fatture.cursor()->relation();

  for (bool ok = fatture.move_first(); ok; ok = fatture.move_next())
  {
    pi.addstatus(1);
    if (rel.is_first_match(-220))
    {
      real importo = fatture.get(ALL_IMPORTO).as_real();
      for (bool ok = true; ok; ok = rel.next_match(-220))
        importo += rel.curr(-220).get_real(ALL_IMPORTO);

      const TExclusion_mode old_mode = TExclusion_mode(fatture.get(ALL_IGNORA).as_int());
      const TExclusion_mode new_mode = importo < fe_importo_limite(anno) ? em_importo_limite : em_incluso;
      if (old_mode != new_mode)
      {
        TLocalisamfile& f = fatture.cursor()->file();
        f.put(ALL_IGNORA, new_mode);
        f.rewrite();
      }
    }
  }
}

// Analizza tutti i movimenti dell'anno dell'attivit� corrente e genera i record rilevanti
bool TDati_rilevanti_msk::elabora_alleg()
{
  if (!check_fields()) // Controlla che l'anno sia valido
    return false;

  const TRecnotype prog = genera_alleg();
  return prog > 1;
}

bool TDati_rilevanti_msk::send_nota_variazione(const TRectype& alleg, TDati_rilevanti_set& operaz)
{
  real imponibile = alleg.get_real(ALL_IMPORTO);
  real imposta = alleg.get_real(ALL_IMPOSTA);
  if (imponibile.is_zero() && imposta.is_zero())
    return false;

  const TAnagrafica anag(alleg);
  if (!anag.ok())
    return false;

  const char tipocf = alleg.get_char(ALL_TIPOCF);
  char segno_imponibile = tipocf == 'C' ? 'D' : 'C'; // Normalmente sono negative e quindi a debito del dichiarante
  char segno_imposta = segno_imponibile;
  
  if (imponibile >= ZERO)
    segno_imponibile = segno_imponibile == 'D' ? 'C' : 'D';
  else
    imponibile = -imponibile;
  if (imposta >= ZERO)
    segno_imposta = segno_imposta == 'D' ? 'C' : 'D';
  else
    imposta = -imposta;
  
  if (anag.estero())
  {
    operaz.new_rec("5"); // Note di variazione a soggetti non residenti
    if (anag.fisica())
    {
      operaz.set(2, anag.cognome());
      operaz.set(3, anag.nome());
      operaz.set(4, anag.data_nascita());
      operaz.set(5, anag.comune_nascita());
      operaz.set(6, anag.provincia_nascita());
      operaz.set(7, anag.stato_estero_UNICO());
    }
    else
    {
      operaz.set(8, anag.ragione_sociale());
      operaz.set(9, anag.comune_residenza());
      operaz.set(10, anag.stato_estero_UNICO());
      operaz.set(11, anag.indirizzo_residenza());
    }
    operaz.set(12, alleg.get(ALL_DATAREG));
    operaz.set(13, alleg.get(ALL_NUMDOC));
    operaz.set(14, imponibile);
    operaz.set(15, imposta);
    operaz.set(16, alleg.get(ALL_DATARETT));
    operaz.set(17, alleg.get(ALL_NUMRETT));
    operaz.set(18, segno_imponibile);
    operaz.set(19, segno_imposta);
  }
  else
  {
    operaz.new_rec("4"); // Note di variazione a soggetti residenti
    if (anag.partita_IVA().full())
      operaz.set(2, anag.partita_IVA());
    else
      operaz.set(3, anag.codice_fiscale());

    operaz.set(4, alleg.get(ALL_DATAREG));
    operaz.set(5, alleg.get(ALL_NUMDOC));
    operaz.set(6, imponibile);
    operaz.set(7, imposta);
    operaz.set(8, alleg.get(ALL_DATARETT));
    operaz.set(9, alleg.get(ALL_NUMRETT));
    operaz.set(10, segno_imponibile);
    operaz.set(11, segno_imposta);
  }    

  return true;
}

bool TDati_rilevanti_msk::send_fatt(const TRectype& alleg, TDati_rilevanti_set& operaz)
{
  const real importo = alleg.get_real(ALL_IMPORTO);
  const real imposta = alleg.get_real(ALL_IMPOSTA);
  if (importo.is_zero() && imposta.is_zero())
    return false;

  const TAnagrafica anag(alleg);
  if (!anag.ok())
    return false;

  if (anag.estero())
  {
    operaz.new_rec("3"); // Operazioni con soggetti non residenti
    if (anag.fisica())
    {
      operaz.set(2, anag.cognome());
      operaz.set(3, anag.nome());
      operaz.set(4, anag.data_nascita());
      operaz.set(5, anag.comune_nascita());
      operaz.set(6, anag.provincia_nascita());
      operaz.set(7, anag.stato_estero_UNICO());
    }
    else
    {
      operaz.set(8, anag.ragione_sociale());
      if (anag.comune_residenza().blank())
        operaz.set(9, anag.localita_residenza());
      else
        operaz.set(9, anag.comune_residenza());
      operaz.set(10, anag.stato_estero_UNICO());
      operaz.set(11, anag.indirizzo_residenza());
    }
    operaz.set(12, alleg.get(ALL_DATAREG));
    operaz.set(13, alleg.get(ALL_NUMDOC));
    operaz.set(14, alleg.get(ALL_MODPAG));
    operaz.set(15, importo);
    operaz.set(16, imposta);
    operaz.set(17, alleg.get(ALL_TIPOPE));
  }
  else
  {
    const TString& paiv = anag.partita_IVA();
    if (paiv.blank())
    {
      operaz.new_rec("1"); // Operazioni con soggetti residenti non titolari di partita IVA
      operaz.set(2, anag.codice_fiscale());
      operaz.set(3, alleg.get(ALL_DATAREG));
      operaz.set(4, alleg.get(ALL_MODPAG));
      operaz.set(5, real(importo+imposta));
    }
    else
    {
      operaz.new_rec("2"); // Operazioni con soggetti residenti - titolari di partita IVA
      operaz.set(2, paiv);
      operaz.set(3, alleg.get(ALL_DATAREG));
      operaz.set(4, alleg.get(ALL_NUMDOC));
      operaz.set(5, alleg.get(ALL_MODPAG));
      operaz.set(6, alleg.get(ALL_IMPORTO));
      operaz.set(7, alleg.get(ALL_IMPOSTA));
      operaz.set(8, alleg.get(ALL_TIPOPE));
    }
  }    

  return true;
}

bool TDati_rilevanti_msk::send_rec(const TRectype& alleg, TDati_rilevanti_set& operaz)
{
  bool done = false;
  if (fe_is_nota_variazione(alleg))
    done = send_nota_variazione(alleg, operaz);
  else
    done = send_fatt(alleg, operaz);
  return done;
}

bool TDati_rilevanti_msk::recall_alleg() const
{
  const int anno = get_int(F_ANNO);
  if (!yesno_box(FR("Si desidera annullare l'invio definitivo dei movimenti del %d?"), anno))
    return false;

  TFast_isamfile mov(LF_MOV);

  TString query;
  query << "USE MOV KEY 2 SELECT ANNOFE=" << anno;
  query << "\nFROM DATAREG=01-01-" << anno;
  TISAM_recordset recset(query);
  TLocalisamfile& file = recset.cursor()->file();

  TProgind pi(recset.items(), TR("Aggiornamento movimenti di prima nota"));
  for (bool ok = recset.move_first(); ok; ok = recset.move_next())
  {
    if (!pi.addstatus(1))
      break;
    file.zero(MOV_ANNOFE);
    file.rewrite();
  }

  return true;
}

void TDati_rilevanti_msk::build_outname(TFilename& n) const
{
  const int anno = get_int(F_ANNO);
  n = get(F_OUTFOLDER);
  if (n.blank())  
    n.tempdir(); 
  
  TString f; f.format("Spesometro%04d_%05d", anno, prefix().get_codditta());
  n.add(f);
  n.ext("txt");
}

// Genera file per invio telematico
bool TDati_rilevanti_msk::send_alleg()
{
  const int anno = get_int(F_ANNO);
  const bool send_all = get_int(F_SENDALL) != 1;

  TFilename temp; build_outname(temp);

  TDati_rilevanti_array data;

  TString query;
  query << "USE ALLEG KEY 2"
        << "\nFROM " << ALL_ANNO << '=' << anno
        << "\nTO " << ALL_ANNO << '=' << anno;
  
  TISAM_recordset alleg(query);
  const TRecnotype tot_alleg = alleg.items();

  if (tot_alleg > 0)
  {
    _log = new TLog_report(temp);

    const TRectype& rec = alleg.cursor()->curr();

    TString16 last_clifo;
    TArray fatt, note;

    TProgind pi(tot_alleg, TR("Elaborazione file"));
    for (bool ok = alleg.move_first(); ok; ok = alleg.move_next())
    {
      if (!pi.addstatus(1))
        break;
      
      TString16 clifo = rec.get(ALL_OCFPI);
      if (clifo.blank())
        clifo = rec.get(ALL_CODCF);

      if (clifo != last_clifo)
      {
        if (!fatt.empty() || !note.empty())
        {
          data.add(fatt, note, send_all, *_log);
          fatt.destroy();
          note.destroy();
        }
        last_clifo = clifo;
      }
  
      if (fe_is_nota_variazione(rec))
        note.add(rec);
      else
        fatt.add(rec);
    }
    if (!fatt.empty() || !note.empty())
      data.add(fatt, note, send_all, *_log);

    if (_log->recordset()->items())
      _log->preview();
    delete _log;
    _log = NULL;
  }

  TDati_rilevanti_set recset(anno);
  const int tipologia = recset.add_header(*this);

  if (tipologia != 2) // Invio i record tranne che in caso di annullamento
  {
    const int tot = data.items();
    if (tot > 0)
    {
      TProgind pi(tot, TR("Generazione file per Agenzia delle Entrate"));
      for (int a = 0; a < tot; a++)
      {
        if (!pi.addstatus(1))
          break;
        if (send_all || !data[a].get_int(ALL_IGNORA))
          send_rec(data[a], recset);
      }
    }
  }
  recset.add_footer();
  recset.sort();
  bool done = recset.save_as(temp);

  const long maxalleg = get_long(F_MAXREC);
  if (recset.items() > maxalleg)
    done = recset.split(temp, maxalleg);

  if (done && get_bool(F_DEFINITIVO) && yesno_box(TR("Si desidera confermare l'invio definitivo della comunicazione?")))
  {
    TFast_isamfile mov(LF_MOV);
    TProgind pi(data.items(), TR("Aggiornamento movimenti di prima nota"), false);
    for (int i = data.items()-1; i >= 0; i--)
    {
      pi.addstatus(1);
      const TRectype& alleg = data[i];
      const long numreg = alleg.get_long(ALL_PROGR);
      const int ignora = alleg.get_int(ALL_IGNORA);
      if (numreg > 0 && numreg < MANUAL_ROW && !ignora)
      {
        mov.put(MOV_NUMREG, numreg);
        int err = mov.read(_isequal, _lock);
        if (err == NOERR)
        {
          const int modpag = alleg.get_int(ALL_MODPAG);
          mov.put(MOV_MODPAG, modpag);
          if (modpag < 2)
          {
            const TString& nr = alleg.get(ALL_NUMRETT);
            if (nr.full() && nr != INVALID_NUMDOC)
            {
              mov.put(MOV_DATARETT, alleg.get(ALL_DATARETT));
              mov.put(MOV_NUMRETT,  nr);
            }
          }
          else
            mov.put(MOV_CONTRATTO, alleg.get(ALL_CONTRATTO));
          mov.put(MOV_ANNOFE, anno);
          err = mov.rewrite();
        }
        if (err != NOERR)
        {
          error_box(FR("Impossibile aggiornare il movimento %ld: errore %d"), numreg, err);
          break;
        }
      }
    }
  }

  return done;
}

void TDati_rilevanti_msk::set_dirty(bool d)
{
  _sheet_dirty = d;
  enable(DLG_SAVEREC, d);
}

void TDati_rilevanti_msk::alleg_sort(TSheet_field& s) const
{
  const int c_codcf = s.cid2index(A_CODCF);
  const int c_numdoc = s.cid2index(A_NUMDOC);
  const int c_numrett = s.cid2index(A_NUMRETT);
  const int c_forzata = s.cid2index(A_FORZATA);
  const int c_ignora = s.cid2index(A_IGNORA);
  const int c_importo = s.cid2index(A_IMPORTO);
  const int tot = s.items();
  
  for (int k = 0; k < 3; k++)
  {
    bool swapped = false; // Something swapped?
    for (int i = tot-1; i >= 0; i--)
    {
      const TString8 numrett = s.cell(i, c_numrett);
      if (numrett.full() && numrett != INVALID_NUMDOC)
      {
        const long codcf_i = atol(s.cell(i, c_codcf));
        int j = -1;
        if (codcf_i == 37789)
          int cazzone = 1;
        // Cerca la fattura andando in su
        for (j = i-1; j >= 0; j--)
        {
          const long codcf_j = atol(s.cell(j, c_codcf));
          if (codcf_j != codcf_i)
          {
            j = -1;
            break;
          }
          if (numrett == s.cell(j, c_numdoc) || numrett == s.cell(j, c_numrett))
            break;
        }
        if (j < 0) // Non l'ho trovata
        {
          // Cerca la fattura andando in gi�
          for (j = i+1; j < tot; j++)
          {
            const long codcf_j = atol(s.cell(j, c_codcf));
            if (codcf_j != codcf_i)
            {
              j = tot;
              break;
            }
            if (numrett == s.cell(j, c_numdoc))
              break;
          }
        }
        if (j >= 0 && j < tot) // L'ho trovata
        {
          if (i != j+1)
          {
            swapped = true;
            s.move_row(i, j+1);
          }
          if (*s.cell(j, c_numrett) <= ' ')
            s.set_back_and_fore_color(REQUIRED_BACK_COLOR, NORMAL_COLOR, j, c_numdoc);
          s.set_back_and_fore_color(REQUIRED_BACK_COLOR, NORMAL_COLOR, j+1, c_numrett);
        }
      }
    }
    if (!swapped)
      break; // No row swapping -> exit
  }
}

void TDati_rilevanti_msk::load_sheet()
{
  const char tipocf = get(F_TIPOCF)[0];
  const long codcf = get_long(F_CODCF);
  const TString& ocfpi = get(F_OCFPI);
  const int show_all = get_int(F_SHOWALL);

  TSheet_field& s = sfield(F_RIGHE);
  s.hide();   // Nascondo lo sheet per guadagnare un 20% di velocit� di caricamento
  s.destroy(); 

  _mode = MODE_QUERY;

  const int anno = get_int(F_ANNO);

  TString limit; limit << ALL_ANNO << '=' << anno;
  if (codcf > 0)
    limit << ' ' << ALL_TIPOCF << '=' << tipocf << ' ' << ALL_CODCF << '=' << codcf;

  TString sel;
  if (ocfpi.full() || (show_all > 0 && show_all < 7))
  {
    if (ocfpi.full())
      sel << "(" << ALL_OCFPI << "='" << ocfpi << "')";
    if (show_all > 0 && show_all < 7)
    {
      if (sel.full()) sel << "&&";
      sel << "(STR(" << ALL_IGNORA;
      switch (show_all)
      {
      case  1: sel << '<'; break; // Importi superiori al limite (20000 o 3000)
      case  2: sel << '='; break; // Importi inferiori al limite (20000 o 3000)
      default: sel << '>'; break; // Importi scartati (esteri o leggi speciali)
      }
      sel << "1))";
    }
  }

  TString query;
  query << "USE " << LF_ALLEG << " KEY 2";
  if (sel.full())
    query << " SELECT " << sel;
  if (limit.full())
    query << "\nFROM " << limit << "\nTO " << limit;

  TISAM_recordset alleg(query);
  const TRecnotype items = alleg.items();
  if (items > 0)
  {
    TString pi_str; pi_str << TR("Caricamento ") << items << TR(" movimenti del ") << anno;
    TProgind pi(items, pi_str);
    const TRectype& curr = alleg.cursor()->curr();
    int rec = 0;
    for (bool ok = alleg.move_first(); ok; ok = alleg.move_next())
    {
      if (!pi.addstatus(1)) break;
      s.autoload_line(++rec, curr);

      const int modpag = curr.get_int(ALL_MODPAG);
      if (modpag == 1)
        s.enable_cell(rec-1, A_CONTRATTO, false);
      else
      {
        s.enable_cell(rec-1, A_DATARETT, false);
        s.enable_cell(rec-1, A_NUMRETT, false);
      }
    }
  }
  alleg_sort(s);

  s.force_update();
  s.show();
  set_dirty(false);

  if (s.items() > 0)
  {
    _mode = MODE_MOD;
    disable(-1);
  }
  else
  {
    _mode = MODE_QUERY;
    enable(-1);
  }
  enable_buttons();
}

bool TDati_rilevanti_msk::save_sheet()
{
  if (!check_rows(false))
    return false;
  
  bool done = true;
  
  const int anno = get_int(F_ANNO);

  TSheet_field& s = sfield(F_RIGHE);
  const TRecnotype items = s.items();

  if (items > 0)
  {
    TFast_isamfile alleg(LF_ALLEG);
    TRectype& rec = alleg.curr();
    TProgind pi(items, TR("Registrazione righe"), false);

    FOR_EACH_SHEET_ROW(s, r, row)
    {
      if (!pi.addstatus(1)) 
        break;

      alleg.zero();
      rec.put(ALL_ANNO, anno);
      s.autosave_line(r+1, rec);
      // Il tipo operazione non � pi� visibile, per cui lo calcolo ora
      rec.put(ALL_TIPOPE, rec.get_char(ALL_TIPOCF) == 'F' ? 2 : 1); 
      const int err = alleg.rewrite_write();
      if (err != NOERR)
      {
        done = cantwrite_box(alleg.name());
        break;
      }
    }
  }

  if (done)
  {
    set_dirty(false);
  }

  return done;
}

bool TDati_rilevanti_msk::check_rows(bool show_error)
{
  const int anno = get_int(F_ANNO);
  bool ok = anno >= 2010;
  if (!ok)
  {
    if (show_error)
      check_fields(); // Provoco segnalazione automatica
    return false;
  }

  long codcf = 0L;
  TString16 ocfpi;
  TSheet_field& s = sfield(F_RIGHE);
  FOR_EACH_SHEET_ROW(s, i, row)
  {
    row->get(s.cid2index(A_CODCF), codcf);
    row->get(s.cid2index(A_OCFPI), ocfpi);
    if (codcf <= 0L && ocfpi.blank())
    {
      ok = show_error && error_box(FR("Soggetto mancante alla riga %d"), i+1);
      break;
    }
  }

  return ok;
}

const TString& TDati_rilevanti_msk::primo_contratto() const
{
  const TSheet_field& s = sfield(F_RIGHE);
  const int c = s.cid2index(A_CONTRATTO);
  FOR_EACH_SHEET_ROW(s, r, row)
  {
    const char* cc = row->get(c);
    if (cc && *cc > ' ')
      return get_tmp_string() = cc;
  }
  return EMPTY_STRING;
}


bool TDati_rilevanti_msk::save_if_dirty()
{
  bool done = true;
  if (_sheet_dirty && yesno_box(TR("Si desiderano registrare le modifiche?")))
  {
    done = check_rows(true);
    if (done)
      done = save_sheet();
  }
  return done;
}

void TDati_rilevanti_msk::enable_buttons()
{
  TSheet_field& s = sfield(F_RIGHE);
  const int anno = get_int(F_ANNO);
  const bool good_year = anno >= 2010;
  const bool def = get_bool(F_DEFINITIVO);
  const bool full_rows = good_year && !s.empty();

  bool one_sent = false; // Ho spedito almeno un movimento in definitivo
  if (good_year)
  {
    TString query;
    query << "USE MOV KEY 2 SELECT ANNOFE=" << anno;
    query << "\nFROM DATAREG=01-01-" << anno;
    TISAM_recordset recset(query);
    one_sent = recset.move_first();
  }

  if (id2pos(DLG_COPY) > 0) // Solo 10.0
    enable(DLG_COPY, full_rows);
  enable(DLG_CANCEL, full_rows);
  enable(DLG_SAVEREC,full_rows && _sheet_dirty);
  enable(DLG_EXPORT, full_rows);
  enable(DLG_RECALC, !full_rows && good_year && !one_sent);
  enable(DLG_ELABORA, good_year && !(one_sent && def));
  enable(DLG_DELREC, one_sent);
  enable(F_DEFINITIVO, !def);
  if (def) reset(F_DEFINITIVO);

  TFilename temp; build_outname(temp);
  enable(DLG_PREVIEW, temp.exist());
}

bool TDati_rilevanti_msk::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  switch (o.dlg())
  {
  case DLG_OK: // Salva
    if (e == fe_button  && jolly == 0) // Selezione su maschera principale
    {
      if (_mode == MODE_QUERY || save_if_dirty())
        load_sheet();
      return false;
    }
    break;
  case DLG_CANCEL:
    if (e == fe_button && jolly == 0)
    {
      if (_mode != MODE_QUERY && save_if_dirty())
      {
        TSheet_field& s = sfield(F_RIGHE);
        s.destroy();
        s.force_update();
        _mode = MODE_QUERY;
        enable(-1);
        enable_buttons();
      }
      return false;
    }
    break;
  case DLG_SAVEREC:
    if (e == fe_button)
      save_if_dirty();
    break;
  case DLG_EXPORT:
    if (e == fe_button)
      return sfield(F_RIGHE).esporta();
    break;
  case DLG_RECALC:
    if (e == fe_button && check_fields())
    {
      if (elabora_alleg())
        load_sheet();
    }
    break;
  case DLG_ELABORA:
    if (e == fe_button && check_fields())
    {
      send_alleg();
      enable_buttons(); // Disabilita bottone se definitivo
    }
    break;
  case DLG_DELREC:
    if (e == fe_button && o.active())
    {
      if (jolly == 0) // Toolbar principale
      {
        recall_alleg();
        enable_buttons(); // Disabilita bottone
        return false;
      }
      else // Maschera di riga
      {
        const long progr = o.mask().get_long(A_RIGA);
        if (progr >= MANUAL_ROW)
        {
          TLocalisamfile alleg(LF_ALLEG);
          alleg.put(ALL_ANNO, get(F_ANNO));
          alleg.put(ALL_PROGR, progr);
          const int err = alleg.remove();
          if (err != NOERR)
            return error_box(FR("Errore di cancellazione: %d"), err);
        }
        else
          return error_box(TR("Riga non cancellabile"));
      }
    }
    break;
  case DLG_PREVIEW:
    if (e == fe_button)
    {
      TFilename temp; build_outname(temp);
      if (temp.exist())
      {
        TDati_rilevanti_rep rep(temp);
        rep.preview();
      }
    }
    break;
  case F_ANNO:
    if (e == fe_init || e == fe_modify)
    {
      int anno = atoi(o.get());
      if (anno < 2010)
      {
        anno = TDate(TODAY).year()-1;
        o.set(anno);
      }
      on_field_event(efield(F_DATA), fe_modify, jolly);
      enable_buttons();
    }
    break;
  case F_DATA:
    if (e == fe_init || e == fe_modify)
    {
      const int anno = max(2010, get_int(F_ANNO));
      TDate d = o.get();
      if (d < TDate(31,12,anno) || d > TDate(31,12,anno+1))
      {
        if (anno == 2010)
          d = TDate(31,12,2011);
        else
          d = TDate(30,4,anno+1);
        set(F_DATA, d);
      }
    }
    break;
  case DLG_COPY:
    if (e == fe_button)
    {
      TSheet_field& s = sfield(F_RIGHE);
      const int sel = s.selected();
      const char tipocf1 = *s.cell(sel, A_TIPOCF);
      const long codcf1 = atol(s.cell(sel, A_CODCF));
      int modpag1 = atoi(s.cell(sel, A_MODPAG));
      TString80 contratto1 = s.cell(sel, A_CONTRATTO);
      contratto1.trim();
      if (contratto1.empty())
      {
        TMask m("fe0100c");
        m.set(F_ANNO, get(F_ANNO));
        m.set(F_TIPOCF, tipocf1 == 'F' ? "F" : "C");
        m.set(F_CODCF, codcf1);
        m.set(F_RAGSOC, s.cell(sel, A_RAGSOC));
        TString80 k; k << codcf1;
        m.set(A_CONTRATTO, k);
        k.cut(0) << "Contratto " << codcf1 << " anno " << get(F_ANNO);
        m.set(F_RAGOCC, k);
        if (m.run() == K_ENTER)
        {
          contratto1 = m.get(A_CONTRATTO);
          modpag1 = m.get_int(A_MODPAG);
          TString80 codtab;
          codtab.format("%c%6ld%s", tipocf1, codcf1, (const char*)contratto1);
          TModule_table con("CON");
          con.put("CODTAB", codtab);
          con.put("S0", m.get(F_RAGOCC));
          con.put("S6", modpag1);
          const int err = con.write();
          if (err != NOERR)
          {
            if (err == _isreinsert)
            {
              if (!yesno_box(FR("Il contratto '%s' esiste gi�: si desidera continuare ugualmente?"), (const char*)contratto1))
              {
                modpag1 = 1;
                contratto1.cut(0);
              }
            }
            else
            {
              cantwrite_box(con.description());
              modpag1 = 1;
              contratto1.cut(0);
            }
          }
        }
      }
 
      if (modpag1 > 1 && contratto1.not_empty() && yesno_box("Si desidera applicare il contratto '%s' alle righe successive?", (const char*)contratto1))
      {
        const int tcf = s.cid2index(A_TIPOCF);
        const int ccf = s.cid2index(A_CODCF);
        const int frz = s.cid2index(A_FORZATA);
        const int con = s.cid2index(A_CONTRATTO);
        const int ret = s.cid2index(A_NUMRETT);
        const int mod = s.cid2index(A_MODPAG);

        TString80 contratto;
        TString16 numrett;
        for (int r = sel; r < s.items(); r++)
        {
          TToken_string& row = s.row(r);

          const char tipocf = row.get_char(tcf);
          const long codcf = row.get_long(ccf);
          if (tipocf != tipocf1 || codcf != codcf1)
            break;  // Sconfinamento in altro cliente

          row.get(ret, numrett);
          row.get(con, contratto);
          if (numrett.blank() && contratto.blank())
          {
            row.add("X", frz);
            row.add(modpag1, mod);
            row.add(contratto1, con);
            s.enable_cell(r, con);
          }
        }
        s.force_update();
        _sheet_dirty = true;
        enable_buttons();

      }
    }
    break;
  case F_OUTFOLDER:
    if (e == fe_init && o.empty())
    {
      TFilename tmp; tmp.tempdir();
      o.set(tmp);
    }
    break;
  case F_MAXREC:
    if (e == fe_init && o.empty())
      o.set(15000L);
    break;
  case F_RIGHE:
    if (e == fe_init)
      load_sheet(); else
    if (e == se_query_modify)
    {
      TSheet_field& s = (TSheet_field&)o;
      TToken_string& row = s.row(jolly);
      const TRecnotype progr = row.get_long(0);
      s.sheet_mask().enable(DLG_DELREC, progr >= MANUAL_ROW);
      s.sheet_mask().enable(DLG_USER, progr < MANUAL_ROW);
    } else
    if (e == se_notify_modify)
    {
      set_dirty(true); 
      enable_buttons();
    } else
    if (e == se_query_add)
    {
      if (!check_rows(false))
        return false;
    } else
    if (e == se_notify_add)
    {
      TSheet_field& s = (TSheet_field&)o;
      TToken_string& row = s.row(jolly);
      row.add(nuovo_progr(), s.cid2index(A_RIGA));
      row.add(1, s.cid2index(A_MODPAG));
    } else
    if (e == se_query_del)
    {
      TSheet_field& s = (TSheet_field&)o;
      TToken_string& row = s.row(jolly);
      const TRecnotype progr = row.get_long(0);
      return progr >= MANUAL_ROW;
    }
    break;
  case A_CODCF:
  case A_OCFPI:
    if (e == fe_modify || (e == fe_init && !o.empty()))
    {
      TMask& m = o.mask();
      const TAnagrafica anag(m.get(A_TIPOCF)[0], m.get_long(A_CODCF), m.get(A_OCFPI));
      m.set(A_RAGSOC, anag.ragione_sociale());
      m.set(A_PAIV, anag.partita_IVA());
      m.set(A_COFI, anag.codice_fiscale());
    }
    break;
  case DLG_USER:
    if (e == fe_button || e == fe_init)
    {
      const long numreg = o.mask().get_long(A_RIGA);
      const bool enab = (numreg > 0 && numreg < MANUAL_ROW);
      if (e == fe_button && enab)
      {
        TRectype mov(LF_MOV); 
        mov.put(MOV_NUMREG, numreg);
        mov.edit();
      }
      else
        o.enable(enab);
    }
    break;
  default:
    break;
  }

  if (e == fe_modify && jolly == 1 && _mode == MODE_MOD)
  {
    const short id = o.dlg();
    if (id >= A_RIGA && id < A_COFI && id != A_FORZATA)
      o.mask().set(A_FORZATA, true);
  }

  return true;
}

///////////////////////////////////////////////////////////
// TDati_rilevanti_app
///////////////////////////////////////////////////////////

class TDati_rilevanti_app : public TSkeleton_application
{
protected:
  virtual bool create();

public:
  virtual void main_loop();
};

bool TDati_rilevanti_app::create()
{
  // Controllo preventivo dell'avvenuta conversione del tracciato record
  TRectype alleg(LF_ALLEG);
  if (alleg.type(ALL_NUMDOC) == _nullfld)
    return error_box(TR("Il database non � stato ancora convertito per il modulo FE"));
    
  // Teoricamente � possibile visualizzare tutti i movimenti di un anno, per cui allargo il numero riga
  TSheet_field::set_line_number_width(6); 

  return TSkeleton_application::create();
}

void TDati_rilevanti_app::main_loop()
{
  TDati_rilevanti_msk msk;
  msk.run();
}

///////////////////////////////////////////////////////////
// main
///////////////////////////////////////////////////////////

int fe0100(int argc, char* argv[])
{
  TDati_rilevanti_app app;
  app.run(argc, argv, TR("Gestione dati rilevanti"));
  return 0;
}