// ve6100.cpp: programma di contabilizzazione documenti
// Orgiastica visione...
//      L'anatra del dubbio dalle labbra di Vermouth.
//******************************************************************************
//          \\\\\\\\                 e                                         *
//     b     \\\\\\\\                          b                               *
//            \\\\\\\\   e                            r          d        l    *
//             \\\\\\\\                                                        *
//              \\\\\\\\    i       V              o             e      h      *
//      L        \\\\\\\\                                                      *
//                \\\\\\\\     b        d                                      *
//              o  \\\\\\\\                      m         a                   *
// +++++++++++++++++++++++++            b                              t       *
// ++++++++++++++++++++++++++--------------------------------------------------|
// ++++++++++++++++++++++++++--------------------------------------------------|
// ++++++++++++++++++++++++            |---------------------------------------|
//    u           ////////          u  |---------------------------------------|
//       '       ////////    a         |             /\            /\          |
//              ////////           i   |  r         /  \  r       /  \         |
//         a   ////////               \|/          /    \        /    \  l     |
//            ////////-----------------*---\  a   /  t   \      /   a  \      /|
//           ////////                 /|\   \    /        \    /        \    / |
//   d      ////////          n        |     \  /          \  /          \  /  |
//         ////////    l          d    |      \/     a      \/     l      \/   |
//-----------------------------------------------------------------------------|
//
// Scared uh?

#include <applicat.h>
#include <relation.h>
#include <tabutil.h>
#include <mask.h>
#include <sheet.h>
#include <urldefid.h>
#include <progind.h>
#include <modaut.h>
#include "../cg/cg2101.h"
#include "../cg/cglib.h"
#include "../cg/saldacon.h"
#include "velib02.h"
#include "ve6100a.h"

#include <mov.h>
#include <rmov.h>
#include <rmoviva.h>
#include <clifo.h>
#include <cfven.h>
#include <occas.h>
#include <scadenze.h>

enum error_type {
  no_error,
  elaboration_error,
  nr_es_error,
  nr_reg_error,
  nr_doc_error,
  chg_stat_error,
  clifo_error,
  ultprot_error,
  datadoc_error,
  caus_error,
  register_error,
  change_error,
  val_error,
  codpag_error,
  row_type_error,
  no_rows_error,
  conto_error,
  movement_error,
  mov_write_error,
  generic_error
};

// TMovimentoPN_VE
// Classe derivata da TMovimentoPN per calcolare automaticamente le righe contabili
// una volta settate le righe iva e la riga di totale documento
// Sostanzialmente di tratta di aggiungere un metodo in piu' :
// recalc_cg_rows(), liberamente ispirato alla notify_iva() in cg2102.cpp

class TMovimentoPN_VE : public TMovimentoPN
{
  TCausale * _caus;
  
protected:
  // simula il K_SPACE di iva_notify
  void create_row(int i);
  // simula il K_ENTER di iva_notify
  void enter_row(int i);
  // verifica se si tratta di iva indetraibile
  bool detraibile(TRectype& rec) const ;
  // cerca la prima tra quelle di contabilita' che corrisponde al tipo indicato 
  int type2pos(char tipo);
  // Trova nelle righe contabili un conto nelle righe di tipo prescelto
  int bill2pos(const TBill& conto, char tipo);
  // trasforma un real in TImporto, in base al tipo riga
  TImporto real2imp(const real& r, char row_type);
  // setta il record delle righe di contabilita'
  int set_cg_rec(int n, const TImporto& imp, TBill& conto, const char* desc, char tipo);
  // aggiunge l'importo indicato alla n-esima riga di contabilita'
  bool add_cg_rec(int n, const TImporto& imp);
  // Legge l'importo della riga n e lo ritorna col segno dovuto
  TImporto get_cg_imp(int n);
  // Setta l'importo della riga n 
  void set_cg_imp(int n, const TImporto& imp);
public:
  // verifica se il movimento e' quadrato oppure ha qualche maledetto sbilancio
  // ritorna TRUE, ovviamente, se everything's alright.
  bool movement_ok() ;
  // ricalcola le righe di contabilita' dalle righe iva presenti
  // e verifica la quadratura del movimento. Ritorna TRUE se il movimento e' scrivibile
  bool recalc_cg_rows(TCausale* caus = NULL);
  TMovimentoPN_VE() {};
  virtual ~TMovimentoPN_VE() {}
};

TImporto TMovimentoPN_VE::real2imp(const real& r, char row_type)
{   
  CHECK(_caus,"Orgggssbb..._caus pointer is NULL!");
  bool dare;
  if (row_type == 'S')
  {
    dare = _caus->sezione_ritsoc() == 'D';
  }
  else
  {
    dare = _caus->sezione_clifo() == 'D';
    if (row_type != 'T' && row_type != 'F') 
      dare = !dare;
  }  
  
  TImporto importo(dare ? 'D' : 'A', r);
  return importo;
}

bool TMovimentoPN_VE::detraibile(TRectype& rec) const
{
  CHECK(_caus,"Orgggssbb..._caus pointer is NULL!");
  if (_caus->iva() == iva_vendite)
    return TRUE;
  
  if (rec.get_int(RMI_TIPODET) != 0)
    return FALSE;
    
  const real & p = _caus->reg().prorata();
  return p < 100.0;
}

int TMovimentoPN_VE::bill2pos(const TBill& conto, char tipo)
{
  const int items = cg_items();
  for (int i = 0; i < items; i++)
  {
    TRectype& s = cg(i);
    const char t = s.get_char(RMV_ROWTYPE);
    if (t == tipo)
    {
      TBill c;
      const int  gr = s.get_int(RMV_GRUPPO);
      const int  co = s.get_int(RMV_CONTO);
      const long so = s.get_long(RMV_SOTTOCONTO);
      c.set(gr,co,so);
      if (c == conto)
        return i;
    }
  }
  return -1;
}

int TMovimentoPN_VE::type2pos(char tipo)
{
  const int items = cg_items();
  for (int i = 0; i < items; i++)
  {
    TRectype& s = cg(i);
    const char t = s.get_char(RMV_ROWTYPE);
    if (t == tipo)
      return i;
  }
  return -1;
}

void TMovimentoPN_VE::set_cg_imp(int n, const TImporto& imp)
{ 
  TRectype& rec = cg(n);
  rec.put(RMV_SEZIONE,imp.sezione());
  rec.put(RMV_IMPORTO,imp.valore());
}

TImporto TMovimentoPN_VE::get_cg_imp(int n)
{     
  TRectype& rec = cg(n);
  TImporto importo;
  const char sez = rec.get_char(RMV_SEZIONE);
  const real valore(rec.get_real(RMV_IMPORTO));
  importo.set(sez,valore);
  return importo;
}

bool TMovimentoPN_VE::add_cg_rec(int n, const TImporto& imp)
{
  TImporto tot(get_cg_imp(n));
  tot += imp;       
  //tot.normalize();
  set_cg_imp(n, tot);
  return tot.is_zero();
}

int TMovimentoPN_VE::set_cg_rec(int n, const TImporto& imp, TBill& conto,
                                const char* desc, char tipo)
{
  const bool insert = n < 0;
  if (insert) n = cg_items(); // Questa e' la prima riga di contabilita' vuota e disponibile
  TRectype& rec = cg(n);
  
  if (insert)
  {
    TRectype& head = lfile().curr();
    const int annoes = head.get_int(MOV_ANNOES);
    const long numreg = head.get_long(MOV_NUMREG);
    TDate datareg(head.get_date(MOV_DATAREG));
    rec.put(RMV_ANNOES,annoes);
    rec.put(RMV_NUMREG,numreg);
    rec.put(RMV_DATAREG,datareg);
  }
  rec.put(RMV_SEZIONE,imp.sezione());
  rec.put(RMV_IMPORTO,imp.valore());
  rec.put(RMV_TIPOC,conto.tipo());
  rec.put(RMV_GRUPPO,conto.gruppo());
  rec.put(RMV_CONTO,conto.conto());
  rec.put(RMV_SOTTOCONTO,conto.sottoconto());
  //rec.put(RMV_DESCR,conto.descrizione());

  if (tipo == 'T')                      // Calcolo contropartita
  {
    TRectype& irec = iva(0);
    const char t  = irec.get_char(RMI_TIPOC);
    const int  gr = irec.get_int(RMI_GRUPPO);
    const int  co = irec.get_int(RMI_CONTO);
    const long so = irec.get_long(RMI_SOTTOCONTO);
    rec.put(RMV_TIPOCC,t);
    rec.put(RMV_GRUPPOC,gr);
    rec.put(RMV_CONTOC,co);
    rec.put(RMV_SOTTOCONTOC,so);
  }
  else
  {
    const int pos = type2pos('T');
    if (pos >= 0)
    {
      TRectype& crec = cg(pos);
      const char t  = crec.get_char(RMV_TIPOC);
      const int  gr = crec.get_int(RMV_GRUPPO);
      const int  co = crec.get_int(RMV_CONTO);
      const long so = crec.get_long(RMV_SOTTOCONTO);
      rec.put(RMV_TIPOCC,t);
      rec.put(RMV_GRUPPOC,gr);
      rec.put(RMV_CONTOC,co);
      rec.put(RMV_SOTTOCONTOC,so);
    }
  }
  
  rec.put(RMV_ROWTYPE,tipo);
  return n;
}

void TMovimentoPN_VE::create_row(int i) 
{
  CHECK(_caus,"Orgggssbb..._caus pointer is NULL!");
  TRectype& cur = iva(i);
  real oldimp = cur.get_real(RMI_IMPONIBILE);
  real oldiva = cur.get_real(RMI_IMPOSTA);
  
  if (oldiva.is_zero() && _caus->corrispettivi()) // In caso di corrispettivi ...
  {               
    const TString zanicchi(cur.get(RMI_CODIVA));      // Codice IVA
    const TCodiceIVA i(zanicchi);          
    oldiva = i.scorpora(oldimp);             // ... scorpora imposta dall'imponibile
  }  
    
  const char tipod = detraibile(cur) ? 'D' : 'N';        

  if (type2pos(tipod) < 0 && !oldiva.is_zero())
  {
    const int ri = tipod == 'D' ? 3 : 4;     // Calcola riga causale per l'IVA
    TBill c; _caus->bill(ri, c); 
    if (c.ok())
    {
      const TString80 d(_caus->desc_agg(ri));
      set_cg_rec(-1, real2imp(ZERO, 'I'), c, d, tipod);
    }  
    else 
      if (ri == 4)                           // Se non esiste il conto IVA indetraibile ...
      {                                      // ... somma imponibile e imposta
        oldimp += oldiva;                    
        oldiva = 0.0;                             
      }
  }  
    
  TBill oldconto;
  const int  gr = cur.get_int(RMI_GRUPPO);
  const int  co = cur.get_int(RMI_CONTO);
  const long so = cur.get_long(RMI_SOTTOCONTO);
  oldconto.set(gr,co,so);
  if (oldconto.ok())
  {
    if (bill2pos(oldconto, 'I') < 0)
    {                      
      const TString d(_caus->desc_agg(2));
      set_cg_rec(-1, real2imp(ZERO, 'I'), oldconto, d, 'I');
    }  
  } 
}

void TMovimentoPN_VE::enter_row(int i)
{
  CHECK(_caus,"Orgggssbb..._caus pointer is NULL!");
  TRectype& cur = iva(i);
  real imponibile = cur.get_real(RMI_IMPONIBILE);
  real imposta = cur.get_real(RMI_IMPOSTA);
    
  if (imposta.is_zero() && _caus->corrispettivi())   // In caso di corrispettivi ...
  {               
    const TString zanicchi(cur.get(RMI_CODIVA));                         
    const TCodiceIVA i(zanicchi);          
    imposta = i.scorpora(imponibile);        // ... scorpora imposta dall'imponibile
  }  
    
  TBill conto;
  const char t  = cur.get_char(RMI_TIPOC);
  const int  gr = cur.get_int(RMI_GRUPPO);
  const int  co = cur.get_int(RMI_CONTO);
  const long so = cur.get_long(RMI_SOTTOCONTO);
  conto.set(gr,co,so,t);
  
  int newpos = bill2pos(conto, 'I');         // Riga in cui andra' l'imponibile
    
  const bool detrarre = detraibile(cur);     // Determina se IVA detraibile
    
  // Calcola riga causale col conto opportuno
  const int ri = detrarre ? RIGA_IVA_DETRAIBILE : RIGA_IVA_NON_DETRAIBILE;           
  TBill contoiva;  _caus->bill(ri, contoiva);
    
  if (ri == 4 && !contoiva.ok())             // Se non c'e' il conto IVA indetraibile ...
  {                                          // ... somma imponibile e imposta
    imponibile += imposta;                   
    imposta = 0.0;
  }
    
  // Aggiorna conto sulla riga contabile
  if (newpos < 0) // conto non esistente: da inserire
  {
    const TImporto val(real2imp(imponibile, 'I'));         
    if (conto.ok() && !val.is_zero())        // Se c'e' imponibile ...
    {                                        // crea una nuova riga contabile
      const TString d(_caus->desc_agg(2));
      set_cg_rec(-1, val, conto, d, 'I');
    }  
  }  
  else
  {
    TImporto val(real2imp(imponibile, 'I'));         
    add_cg_rec(newpos, val);
  }  
    
  // Aggiorna conto IVA sulla riga contabile

  const char tipod = detrarre ? 'D' : 'N';
  int newposiva = type2pos(tipod);

  if (newposiva < 0)
  {                                          
    if (!imposta.is_zero())                               // Se  c'e' imposta ...
    {                                                     // ... crea nuova riga per l'IVA
      const TImporto val(real2imp(imposta, 'I'));         
      const TString d(_caus->desc_agg(ri));
      set_cg_rec(-1, val, contoiva, d, tipod);
    }  
  }
  else
  {
    const TImporto val(real2imp(imposta, 'I'));         
    add_cg_rec(newposiva, val);
  }
}

bool TMovimentoPN_VE::movement_ok()
{
  TImporto tot_imp;
  TImporto imp;
  
  const int max = cg_items();
  for (int i = 0; i < max; i++)
  {
    TRectype& r = cg(i);
    const char sez =  r.get_char(RMV_SEZIONE);
    const real val(r.get_real(RMV_IMPORTO));
    imp.set(sez,val);
    tot_imp+=imp;
  }
  
  if (!tot_imp.is_zero())
    return FALSE;
  return TRUE;
}

bool TMovimentoPN_VE::recalc_cg_rows(TCausale* caus)
{
  const int righe = iva_items();
  bool external_caus = TRUE;
  if (caus == NULL)
  {
    external_caus = FALSE;
    TRectype& head = lfile().curr();
    TString16 codcau(head.get(MOV_CODCAUS));
    int year = head.get_int(MOV_ANNOIVA); 
    _caus = new TCausale (codcau,year);
  }
  else
    _caus = caus;
  for (int i=0; i<righe; i++)
  {
    create_row(i);
    enter_row(i);
  }
  if (!external_caus)
  {
    delete _caus;
    _caus = NULL;
  }
  return movement_ok();
}

// TIVA_element
// classe di elementi da memorizzare nel TAssoc_array (vedi sotto) _righe_iva
class TIVA_element : public TObject
{
  real _imp;
  real _iva;
  real _ali;
public:
  real& imp()    { return _imp;} // Imponibile
  real& iva()    { return _iva;} // Iva
  real& ali()    { return _ali;} // Aliquota %
  void     zero(){ _imp = 0.0; _iva = 0.0; _ali = 0.0; }
  virtual TObject* dup() const { return new TIVA_element(*this); }
  TIVA_element& operator = (TIVA_element& a);
  TIVA_element& operator += (const TRiga_documento& a);
  TIVA_element() {_imp = 0.0; _iva = 0.0; _ali = 0.0;}  
  ~TIVA_element() {};
};

TIVA_element& TIVA_element::operator=(TIVA_element& a)
{ 
  _imp = a.imp(); _iva = a.iva(); _ali = a.ali();
  return *this;
}

TIVA_element& TIVA_element::operator+=(const TRiga_documento& a)
// It's horrible, I know.
{ 
  _imp += a.imponibile(); _iva += a.imposta(); _ali = a.iva().aliquota();
  return *this;
}

// TContabilizzazione_app
// Applicazione di contabilizzazione documenti
class TContabilizzazione_app : public TApplication
{
  TString16     _cod_el;       // codice elaborazione immesso
  TString16     _spin_cod,     // codice iva spese d'incasso
                _spbo_cod;     // codice iva spese bolli
  TDate         _data_reg;     // data di registrazione immessa
  TDate         _data_ini;     // data di inizio intervallo
  TDate         _data_fine;    // data di fine intervallo
  bool          _auto_data;    // se TRUE prende in automatico la data di registrazione dalla data documento
  bool          _sc_enabled;   // se TRUE il saldaconto di ditta e' abilitato
  bool          _can_write;    // se TRUE e' abilitata la scrittura. Non appena rileva un errore rimane a FALSE for this instance
  bool          _nump_cfg;     // se TRUE prende il numero rif. partita dal numero protocollo
  byte          _nump_iva;     // se 1 prende il numero protocollo da registro iva, se 2 prende il numero protocollo dal numero doc.
  TMask         *_msk;         // maschera di selezione dati
  TRelation     *_clifo;       // relazione dei clienti e fornitori + cfven
  TLocalisamfile *_attiv,      // file delle attivita' (per far funzionare TRegistro)
                 *_fcaus,      // file delle causale (per far funzionare TCausale)
                 *_docfile,    // file dei documenti (per far funzionare TDocumento)
                 *_rdocfile,   // file delle righe documento (per far funzionare TDocumento)
                 *_frcaus,     // file delle righe causali (per far funzionare TCausale)
                 *_part,       // file delle partite (per far funzionare TPartita)
                 *_scad,       // file delle scadenze (per far funzionare TPartita)
                 *_pags,       // file dei pagamenti (per far funzionare TPartita)
                 *_anamag;     // file delle anagrafiche di magazzino
  TTable         *_cpg,        // tabella condizioni di pagamento
                 *_gmc,        // tabella gruppi/sottogruppi merceologici
                 *_rfa,        // tabella raggruppamenti fiscali
                 *_cve,        // tabella categorie di vendita
                 *_val,        // tabella valute estere
                 *_tri,        // tabella tipi di riga
                 *_prs,        // tabella prestazioni
                 *_spp,        // tabella spese
                 *_caa,        // tabella categorie acquisto articoli
                 *_cra,        // tabella categorie ricavo articoli
                 *_cco;        // tabella categorie contabili
  TDocumento   *_doc;       // documento corrente
  TMovimentoPN_VE    *_movimento; // movimento di prima nota costruito sul documento corrente
  TPagamento         *_pagamento; // pagamento corrente, ricalcolato prima di scrivere le scadenze
  TAssoc_array       _righe_iva;  // array per la memorizzazione delle righe iva raggruppate in codesto modo:
                                  // CODICE_IVA+TIPOCF+GRUPPO+CONTO+SOTTOCONTO
                                  // una volta completo, tale array viene scorso per comporre le righe IVA
                                  // di _movimento.
  TCausale           *_caus;      // causale del documento corrente
  TArray_sheet       *_num_sheet; // Array sheet selezionabile dei codici numerazione
  TString_array      _tipi_doc;   // Array di stringhe contenente i tipi documenti da elaborare
  error_type         _error;      // Errore rilevato durante l'elaborazione
  long               _total_docs; // Totale documenti contabilizzati.
  char               _final_doc_status; // Valore per lo stato finale del documento
  TToken_string      _search_seq;       // Sequenza di ricerca del conto costo/ricavo la correttezza dell'ordinamento
                                        // va controllata nel programma di modifica parametri:
                                        // "" = fine ordinamento
                                        // CF = cliente fornitore
                                        // CA = causale
                                        // AR = articolo (costo/ricavo)
                                        // GM = gruppo merceologico
                                        // SM = sottogruppo merceologico
                                        // RF = raggruppamento fiscale
                                        // CV = categoria di vendita
                                        // CC = categoria contabile
                                        // Gli utlimi 6 fanno parte della ricerca per costi ricavi, in particolare AR,GM,SM e RF
                                        // non possono essere interrotti da CV o CC. Ad es. CA|CF|AR|CV|GM|CC|RF non e' valida come stringa
                                        // di ricerca.
  TBill _sco_perc_bill, _sco_imp_bill,  // Conti per gli sconti a percentuale ed importi (dalla configurazione)
        _spin_billa, _spin_billv,
        _spbo_billa, _spbo_billv;
protected: // TApplication
  bool   sc_enabled() const ; 
  // Funzione per ricercare il conto di costo/ricavo
  error_type   search_costo_ricavo(TBill& conto, const TRiga_documento& r);
  // Funzione atomica per aggiungere le righe di spese d'incasso e bolli al TAssoc_array _righe_iva
  void calculate_spese(real& spese, real& sp_iva, int ndec, bool is_incasso);
  // Funzione per aggiungere le righe di spese d'incasso e bolli al TAssoc_array _righe_iva (chiama calculate_spese())
  error_type   add_spese_inbo();
  // Funzione per aggiungere la riga iva al TAssoc_array _righe_iva
  error_type   add_iva_row(const TBill& conto, const TRiga_documento& r);
  // Crea le righe iva su _movimento
  error_type   create_iva_rows();
  // Crea la riga di totale documento
  error_type   create_total_doc_row();
  // Cambia lo stato del documento
  error_type   change_doc_status();
  // Compila la testata del movimento
  error_type   compile_head_mov();
  // Compila le righe del movimento
  error_type   compile_rows_mov();
  // Compila le scadenze
  error_type   compile_scad();
  // Coontrolla se lo stato ed il tipo del documento sono validi e rispettano la selezione
  bool doc_tipo_stato_ok();
  // calcola il pagamento
  void calc_pagament();
  // scrive le scadenze
  error_type write_scadenze();
  // scrive il movimento e le scadenze
  error_type write_all();
  // Visualizza l'ultimo errore rilevato
  void display_error();
  // Contabilizza il documento
  void contabilize_document();
  // Contabilizza i documenti
  void contabilize();
  // Le 4 seguenti non hanno bisogno di commenti
  virtual bool menu(MENU_TAG mt);
  virtual bool create();
  virtual bool destroy();
  virtual void on_config_change();
  // Handler del codice elaborazione differita
  static bool handle_cod_eld(TMask_field& f, KEY k);
  // Handler dell'intervallo di date
  static bool handle_data_range(TMask_field& f, KEY k);
  // Handler della data di registrazione
  static bool handle_data_reg(TMask_field& f, KEY k);
  // Handler del pulsante di selezione codici numerazione
  static bool handle_select(TMask_field& f, KEY k);
  // Ritorna il TArray_sheet contenente le selezioni sui codici numerazione
  // Costruisce lo sheet dei codici numerazione
  void   build_num_sheet();
  TArray_sheet* get_num_sheet() const { return _num_sheet; }
public:
  // Verifica se non ci sono stati errori
  bool good() const { return _error == no_error;}
  error_type status() { return _error; }
  void set_status(error_type e) { _error = e; }
  TContabilizzazione_app() {_msk = NULL; _num_sheet = NULL;} 
  virtual ~TContabilizzazione_app() { }
};

inline TContabilizzazione_app& app() { return (TContabilizzazione_app&) main_app(); }

bool TContabilizzazione_app::handle_data_range(TMask_field& f, KEY k)
{
  if (k==K_ENTER && f.dirty())
  {
    TMask& m = f.mask();
    TDate da(m.get_date(F_DATA_INI));
    TDate a(m.get_date(F_DATA_FIN));
    m.field(F_DATA_REG).set_dirty();
    //if (a == botime || da == botime) return TRUE;
    if (a < da) 
    {
      f.error_box("La data di inizio deve essere minore della data di fine.");
      return FALSE;
    }
    if ((a - da) > 15)
    {
      f.error_box("L'intervallo tra le date non puo' eccedere i 15 giorni.");
      return FALSE;
    }
  }
  return TRUE;
}

bool TContabilizzazione_app::handle_data_reg(TMask_field& f, KEY k)
{
  if (k==K_ENTER && f.dirty())
  {
    TMask& m = f.mask();
    
    TDate data_reg(f.get());
    if (data_reg == botime) return TRUE;
    TDate da(m.get_date(F_DATA_INI));
    //if (!da.ok()) return TRUE;
    if ((data_reg - da) > 15)
    {
      f.error_box("L'intervallo tra la data di registrazione e la data di inizio non puo' eccedere i 15 giorni.");
      return FALSE;
    }
    if (data_reg < da)
    {
      f.error_box("La data di registrazione non puo' essere minore della data di inizio.");
      return FALSE;
    }
  }
  return TRUE;
}

bool TContabilizzazione_app::handle_cod_eld(TMask_field& f, KEY k)
{
  if (f.to_check(k) && k == K_TAB) // se e' cambiato ricostruisce anche lo sheet dei codici numerazione
  {
    app()._cod_el = f.get(); // aggiorna il codice elaborazione per la build_num_sheet()
    f.mask().disable(DLG_OK);
    app().build_num_sheet();
  }
  return TRUE;
}

bool TContabilizzazione_app::handle_select(TMask_field& f, KEY k)
{
  if (k == K_SPACE)
  {
    TMask& m = f.mask();
    TArray_sheet* s = app().get_num_sheet();
    if (s->run())
    {
      if (s->checked() != 0) // Hai selezionato qualcosa ?
        m.enable(DLG_OK);    // allora abilita il pulsante di conferma 
      else
        m.disable(DLG_OK);
    }
  }
  return TRUE;
}

void TContabilizzazione_app::build_num_sheet()
{
  _num_sheet->destroy();
  
  TTable eld("%ELD");
  TTable num("%NUM");
  TString s1,s2,s3;
  TString16 tipon1,tipon2,tipon3,tipon4,tipon5; // tipi documento validi per la numerazione
  long pos;
  
  eld.put("CODTAB",_cod_el);
  if (eld.read() == NOERR)
  {
    TToken_string t;
    s1.format("%-20s",(const char*)eld.get("S2"));
    s3 = eld.get("S7");
    _final_doc_status = eld.get("S4")[0];
    for (int i=0;i<5;i++)
    {
      t = s1.mid(i*4,4);     // Tipo documento
      if (t.trim().empty()) break;
      t.add(s3.mid(i,1));  // Stato iniziale
      _tipi_doc.add(t);    // Aggiunge questo tipo documento alla lista
    }
    for (pos=0,num.first();num.good();num.next(),pos++) // scorre tutte le numerazioni possibili
    {
      TToken_string t;
      t.add(" ");
      t.add(num.get("CODTAB"));
      t.add(num.get("S0"));
      _num_sheet->add(t);
      s2 = num.get("S2"); // reperisce i tipi documento validi per questa numerazione
      tipon1 = s2.mid(0,4);
      tipon2 = s2.mid(4,4);
      tipon3 = s2.mid(8,4);
      tipon4 = s2.mid(12,4);
      tipon5 = s2.mid(16,4);
      const int n1 = s1.find(tipon1);
      const int n2 = s1.find(tipon2);
      const int n3 = s1.find(tipon3);
      const int n4 = s1.find(tipon4);
      const int n5 = s1.find(tipon5);
      if ((tipon1.empty() || n1<0) && (tipon2.empty() || n2<0) && 
           (tipon3.empty() || n3<0) && (tipon4.empty() || n4<0) &&
           (tipon5.empty() ||  n5<0))
        _num_sheet->disable_row(pos);
      else
        _num_sheet->enable_row(pos);
    }
  }
}

void TContabilizzazione_app::on_config_change()
{
  TPartita::carica_allineamento();
}

bool TContabilizzazione_app::create()
{
  TApplication::create();
  if (!has_module(CGAUT))
  {
    error_box("Impossibile eseguire il programma se il modulo Contabilita' Generale non e' abilitato");
    return FALSE;
  }
  _error = no_error;
  _can_write = TRUE;
  _caus = NULL;
  _pagamento = NULL;
  _movimento = NULL;
  _msk = new TMask("ve6100a");
  _msk->set_handler(F_CODICE_ELAB,handle_cod_eld);
  _msk->set_handler(F_DATA_INI,handle_data_range);
  _msk->set_handler(F_DATA_FIN,handle_data_range);
  _msk->set_handler(F_DATA_REG,handle_data_reg);
  _msk->set_handler(DLG_USER,handle_select);
  _num_sheet = new TArray_sheet(-1,-1,-4,-4,"Codici numerazione",
               "@1|Cod. numerazione|Descrizione@50");
  _docfile = new TLocalisamfile(LF_DOC);
  _rdocfile = new TLocalisamfile(LF_RIGHEDOC);
  _doc = new TDocumento;
  _cpg = new TTable("%CPG");
  _tri = new TTable("%TRI");
  _val = new TTable("%VAL");
  _prs = new TTable("PRS");
  _spp = new TTable("SPP");
  _caa = new TTable("CAA");
  _cra = new TTable("CRA");
  _gmc = new TTable("GMC");
  _rfa = new TTable("RFA");
  _cve = new TTable("CVE");
  _cco = new TTable("CCO");
  _clifo = new TRelation(LF_CLIFO);
  _clifo->add(LF_CFVEN,"TIPOCF=TIPOCF|CODCF=CODCF");
  _fcaus = new TLocalisamfile(LF_CAUSALI);
  _frcaus = new TLocalisamfile(LF_RCAUSALI);
  _part = new TLocalisamfile(LF_PARTITE);
  _scad = new TLocalisamfile(LF_SCADENZE);
  _pags = new TLocalisamfile(LF_PAGSCA);
  _attiv = new TLocalisamfile(LF_ATTIV); // Altrimenti TRegistro non va!
  _anamag = new TLocalisamfile(LF_ANAMAG);
  TConfig  conf(CONFIG_DITTA);
  
  _search_seq = conf.get("RICERCACR","ve");
  
  // costruisce la stringa che controlla la ricerca del conto costo/ricavo
  // Attenzione! non esegue alcun controllo di consistenza sulla corretta sequenza
  // presuppone che il programma di configurazione abbia generato correttamente
  // il tutto.
  
  if (_search_seq.items() == 0)
  {
    error_box("Non e' abilitata alcuna ricerca per il conto di costo/ricavo in configurazione.");
    return FALSE;
  }
  _sc_enabled = conf.get_bool("GesSal","cg");
  _nump_cfg = conf.get_bool("RifPro","cg");
  
  int gr,co;
  long so;
  
  gr = conf.get_int("SCOPRCODCON","ve",1);
  co = conf.get_int("SCOPRCODCON","ve",2);
  so = conf.get_long("SCOPRCODCON","ve",3);
  _sco_perc_bill.set(gr,co,so);

  gr = conf.get_int("SCOIMCODCON","ve",1);
  co = conf.get_int("SCOIMCODCON","ve",2);
  so = conf.get_long("SCOIMCODCON","ve",3);
  _sco_imp_bill.set(gr,co,so);
  
  gr = conf.get_int("SPINCODCONA","ve",1);
  co = conf.get_int("SPINCODCONA","ve",2);
  so = conf.get_long("SPINCODCONA","ve",3);
  _spin_billa.set(gr,co,so);

  gr = conf.get_int("SPINCODCONV","ve",1);
  co = conf.get_int("SPINCODCONV","ve",2);
  so = conf.get_long("SPINCODCONV","ve",3);
  _spin_billv.set(gr,co,so);

  gr = conf.get_int("SPBOCODCONA","ve",1);
  co = conf.get_int("SPBOCODCONA","ve",2);
  so = conf.get_long("SPBOCODCONA","ve",3);
  _spbo_billa.set(gr,co,so);

  gr = conf.get_int("SPBOCODCONV","ve",1);
  co = conf.get_int("SPBOCODCONV","ve",2);
  so = conf.get_long("SPBOCODCONV","ve",3);
  _spbo_billv.set(gr,co,so);
  
  _spin_cod = conf.get("SPINCODIVA","ve");
  _spbo_cod = conf.get("SPBOCODIVA","ve");

  dispatch_e_menu(BAR_ITEM(1));
  return TRUE;
}

bool TContabilizzazione_app::destroy()
{
  if (_msk) delete _msk;
  if (_num_sheet) delete _num_sheet;
  if (_docfile) delete _docfile;
  if (_rdocfile) delete _rdocfile;
  if (_doc) delete _doc;
  if (_cpg) delete _cpg;
  if (_tri) delete _tri;
  if (_val) delete _val;
  if (_gmc) delete _gmc;
  if (_rfa) delete _rfa;
  if (_cve) delete _cve;
  if (_cco) delete _cco;
  if (_prs) delete _prs;
  if (_spp) delete _spp;
  if (_caa) delete _caa;
  if (_cra) delete _cra;
  if (_fcaus) delete _fcaus;
  if (_frcaus) delete _frcaus;
  if (_part)  delete _part;
  if (_scad) delete _scad;
  if (_pags) delete _pags;
  if (_clifo) delete _clifo;
  if (_attiv) delete _attiv;
  if (_anamag) delete _anamag;
  return TApplication::destroy();
}

bool TContabilizzazione_app::menu(MENU_TAG mt)
{
  while (_msk->run() == K_ENTER)
  {
    _cod_el = _msk->get(F_CODICE_ELAB);
    _data_ini = _msk->get_date(F_DATA_INI);
    _data_fine = _msk->get_date(F_DATA_FIN);
    _data_reg = _msk->get_date(F_DATA_REG);
    _auto_data = _msk->get_bool(F_DATA_AUTO);
    _nump_iva = _msk->get_int(F_SELPROT);
    //_data_fine++;  
    contabilize();
  }
  return FALSE;
}

error_type TContabilizzazione_app::search_costo_ricavo(TBill& conto, const TRiga_documento& r)
{
  const int items = _search_seq.items();
  TLocalisamfile & cli_file = _clifo->lfile(); // YES, arriva qui dentro quando la relazione e' gia' posizionata
  const bool is_cli = cli_file.get(CLI_TIPOCF) == "C";
  bool skip_art_related = FALSE;
  bool skip_clifo = _clifo->bad();
  TCodiceIVA codiva(r.get("CODIVA"));
  const char t = r.tipo().tipo();
  int gr,co;
  long so;
  
  switch (t)
  {
    case 'O': // righe omaggio come articoli spiaccicato identico (avranno imponibile 0)
    case 'M': // righe di merce
    {
      // posiziona l'anagrafica sull'articolo specificato sulla ..iga
      _anamag->put("CODART",r.get("CODART"));
      if (_anamag->read() != NOERR) // se non trova l'articolo saltera' anche gmc,smc,rfa.
        skip_art_related = TRUE;
    
      // Scorre la stringa di ricerca
      for (int i=0;good() && i<items;i++)
      { 
        TString16 tok(_search_seq.get(i));
        if (tok == "CF")
        {
          if (skip_clifo) continue;
          gr = cli_file.get_int(CLI_GRUPPORIC);
          co = cli_file.get_int(CLI_CONTORIC);
          so = cli_file.get_long(CLI_SOTTOCRIC);
          conto.set(gr,co,so);
          if (conto.ok()) break; // se lo trova esce (tutti != 0)
        }
        else
        if (tok == "CA")
        {
          CHECK(_caus,"Orgggssbb..._caus pointer is NULL!");
          if (_caus->IVA2bill(codiva,conto)) break; // se lo trova esce
        }
        else
        if (tok == "AR")
        {
          if (skip_art_related) continue;
          gr = _anamag->get_int(is_cli ? "GRUPPOV" : "GRUPPOA");
          co = _anamag->get_int(is_cli ? "CONTOV" : "CONTOA");
          so = _anamag->get_long(is_cli ? "SOTTOCV" : "SOTTOCA");
          conto.set(gr,co,so);
          if (!conto.ok()) // se il conto non c'e' guarda la categoria acquisti/vendite
          {
            TTable *t = is_cli ? _cra : _caa;
            
            t->put("CODTAB",_anamag->get(is_cli ? "CATCONV" : "CATCONA"));
            if (t->read() == NOERR)
            {
              gr = atoi(t->get("S1"));
              co = atoi(t->get("S2"));
              so = atol(t->get("S3"));
              conto.set(gr,co,so);
            }
          }
          if (conto.ok()) break;
        }
        else
        if (tok == "GM" || tok == "SM" || tok == "RF")
        {
          if (skip_art_related) continue;
          const bool is_fis = tok == "RF";
          TTable * tab = is_fis ? _rfa : _gmc;
          TString codtab(_anamag->get(is_fis ? "GRMERC" : "RAGGFIS"));
          if (tok == "GM")
            codtab.cut(2); // gli ultimi 2 si riferiscono al sottogruppo.
          tab->put("CODTAB",codtab);
          if (tab->read() == NOERR)
          {
            gr = tab->get_int(is_cli ? "I3" : "I0");
            co = tab->get_int(is_cli ? "I4" : "I1");
            so = tab->get_long(is_cli ? "I5" : "I2");
            conto.set(gr,co,so);
          }
          if (conto.ok()) break;
        }
        else
        if (tok == "CV" || tok == "CC")
        {
          const bool is_cve = tok == "CV";
          if (is_cve && !is_cli) continue; // se e' un fornitore salta questa condizione
          TTable* t = is_cve ? _cve : _cco;
          TString cod(is_cve ? _doc->get("CATVEN") : "");
          if (cod.empty())
          {
            if (skip_clifo) continue; // se non aveva trovato il cliente salta al prossimo
            cod = _clifo->lfile(LF_CFVEN).get(is_cve ? CFV_CATVEN : CFV_CODCATC);
          }
          t->put("CODTAB",cod);
          if (t->read() == NOERR)
          {
            const bool x =(is_cve || is_cli);
            gr = t->get_int(x ? "I3" : "I0");
            co = t->get_int(x ? "I4" : "I1");
            so = t->get_long(x ? "I5": "I2");
            conto.set(gr,co,so);
          }
          if (conto.ok()) break;
        }
      }
      break; // case 'M'
    }
    case 'P': // righe prestazione 
    case 'S': // righe spese
    {
      TTable*  tab = t == 'P' ? _prs : _spp;
      tab->put("CODTAB",r.get("CODART"));
      if (tab->read()==NOERR)
      {
        gr = tab->get_int(is_cli ? "I0" : "I3");
        co = tab->get_int(is_cli ? "I1" : "I4");
        so = tab->get_long(is_cli ? "I2" : "I5");
        conto.set(gr,co,so);
      }
      break;  // case 'P','S'
    }
    case 'C': // righe sconti (TBI) (reperire l'imposta e l'imponibile normalizzare. Il conto e' nei parametri)
      // quale sconto deve reperire dalla configurazione? Sconto ad importo o sconto a percentuale?
      if (!r.get("SCONTO").empty()) // se c'e' la percentuale reperisce il conto dello sconto a percentuale
        conto = _sco_perc_bill;
      else // altrimenti quello dello sconto ad importo
        conto = _sco_imp_bill;
      break;  // case 'C'
    case 'D': // righe descrizioni (saltare)
    default :
      break;
  } // end of switch
  if (good() && !conto.find())
    _error = conto_error;
  return _error;
}

bool TContabilizzazione_app::doc_tipo_stato_ok()
// Verifica che il tipo documento corrente esista tra i tipi previsti dalla elaborazione
// differita selezionata
{
  const int items = _tipi_doc.items();
  bool found = FALSE;
  const TString16 tipo(_doc->tipo().codice());
  const char stato = _doc->stato();
  for (int i=0;i<items && !found;i++)
  {
    TToken_string& t = _tipi_doc.row(i);
    const TString16 tipox(t.get(0));
    const char statox = t.get(1)[0];
    if (tipo == tipox && stato == statox)
      found = TRUE;
  }
  return found;
}

bool TContabilizzazione_app::sc_enabled() const
{
  bool rt = _sc_enabled;
  if (_caus != NULL) rt &= _caus->saldaconto();
  return rt;
}

error_type TContabilizzazione_app::compile_head_mov()
// Compila la testata
{ 
  TEsercizi_contabili esc;
  TLocalisamfile& mov = _movimento->lfile();
  TRectype& mov_rec = mov.curr();
  
  // Reperisce la data documento
  TDate datadoc(_doc->data());
  if (!datadoc.ok())
  {
    _error = datadoc_error;
    return _error;
  }
  
  // reperisce la data di registrazione, che e' anche la data di competenza ed
  // eventualmente la data74ter se nel registro della causale vi e' scritta l'informazione
  // sulle agenzie di viaggio.
  // se si e' specificata la data automatica prende invece la data del documento
  TDate data_reg(_auto_data ? datadoc : _data_reg);
  
  esc.update();
  // reperisce il codice anno esercizio,
  int cod_es = esc.date2esc(data_reg); 
  if (cod_es <= 0)
  {
    _error = nr_es_error;
    return _error;
  }
  
  // reperisce l'ultimo numero di registrazione disponibile
  mov.last();
  const long numreg = mov.get_int(MOV_NUMREG) + 1;
  if (mov.status() != NOERR || numreg < 1)
  {
    _error = nr_reg_error;
    return _error;
  }
  
  TCodice_numerazione cod_num(_doc->numerazione());
  // calcola il numero documento aggiungendo l'eventuale prefisso/postfisso.
  TString numdoc(cod_num.complete_num(_doc->numero()));
  if (numdoc.empty() || cod_num.status() != NOERR)
  {
    _error = nr_doc_error;
    return _error;
  }
  numdoc.upper(); // Il numero documento e' uppercase!
  
  // Istanzia la causale del documento corrente...
  const TTipo_documento& tipo = _doc->tipo();
  const TString codcaus(tipo.causale());
  _caus = new TCausale(codcaus,data_reg.year());
  if (!_caus->ok())
  {
    _error = caus_error;
    return _error;
  }
  // per reperire il tipo documento ed il tipo movimento
  
  // reperisce la descrizione dal tipo documento e la completa con la data documento ed il
  // numero documento
  TString descr(tipo.riferimento());
  if (descr.empty()) descr = tipo.descrizione();
  descr << " n. " << _doc->numero();
  descr << " del " << datadoc.string();

  // Codice registro IVA
  TRegistro& registro = _caus->reg();
  if (!registro.ok())
  {
    _error = register_error;
    return _error;
  }
  
  long ult_prot;
  if (_nump_iva == 1)  // Reperisce l'ultimo numero di protocollo dal registro IVA
  {
    ult_prot = registro.protocol() +1;
    if (ult_prot < 1)
    {
      _error = ultprot_error;
      return _error;
    }
  }
  else // oppure dal numero di documento
    ult_prot = _doc->numero();
  // Reperisce la valuta
  TDate datacam(_doc->get_date("DATACAMBIO"));
  TString codval(_doc->valuta());
  real cambio(_doc->cambio());
  codval.trim();
  if (codval == "LIT")
  {
    codval = "";
    cambio = ZERO;
  } 
  if (codval.not_empty())
  {
    _val->put("CODTAB",codval);
    if (_val->read() != NOERR)
    {
      _error = val_error;
      return _error;
    }
  }
  // Reperisce il cambio
  if ((cambio != ZERO && codval.empty()) ||
       cambio == ZERO && codval.not_empty())
  {
    _error = change_error;
    return _error;
  }
  
  // Dati del cliente...
  TString tipocf(_doc->get("TIPOCF"));
  long codcf = _doc->get_long("CODCF");
  TString occas;
  {
    TLocalisamfile& cli_file = _clifo->lfile();
    cli_file.put(CLI_TIPOCF,tipocf);  cli_file.put(CLI_CODCF,codcf);
    if (_clifo->read(_isequal) == NOERR) // posiziona il cliente una volta per tutte
    {
      if (cli_file.get_bool(CLI_OCCAS))
      {
        occas = _doc->get("OCFPI");
        TLocalisamfile ocf(LF_OCCAS);
        ocf.put(OCC_CFPI,occas);
        if (ocf.read() != NOERR)
        {
          _error = clifo_error; 
          return _error;
        }
      }
    }
    else
    {
      _error = clifo_error;
      return _error;
    }
  }
  
  // Codice pagamento
  TString codpag(_doc->get("CODPAG"));
  if (sc_enabled() || codpag.not_empty()) // La condizione di pagamento va controllata 
  {                                       // se e' abilitato il saldaconto o se e' stata inserita
    _cpg->put("CODTAB",codpag);
    if (_cpg->read() != NOERR)
    {
      _error = codpag_error;
      return _error;
    }
  }
  
  // Mo' riempie il record della incornata (testata)
  mov_rec.zero();
  mov_rec.put(MOV_ANNOES,cod_es); mov_rec.put(MOV_NUMREG,numreg);
  mov_rec.put(MOV_DATAREG,data_reg); mov_rec.put(MOV_DATACOMP,data_reg);
  mov_rec.put(MOV_DATADOC,datadoc);
  if (registro.agenzia_viaggi()) mov_rec.put(MOV_DATA74TER,data_reg);
  mov_rec.put(MOV_NUMDOC,numdoc); mov_rec.put(MOV_TIPODOC,_caus->tipo_doc());
  mov_rec.put(MOV_CODCAUS,_caus->codice()); mov_rec.put(MOV_DESCR,descr);
  mov_rec.put(MOV_TIPOMOV,char(_caus->tipomov()+'0')); mov_rec.put(MOV_ANNOIVA,data_reg.year());
  mov_rec.put(MOV_REG,registro.name());
  mov_rec.put(MOV_PROTIVA,ult_prot); mov_rec.put(MOV_CODVAL,codval);
  mov_rec.put(MOV_CAMBIO,cambio); mov_rec.put(MOV_TIPO,tipocf);
  mov_rec.put(MOV_DATACAM,datacam);
  mov_rec.put(MOV_CODCF,codcf); mov_rec.put(MOV_OCFPI,occas);
  mov_rec.put(MOV_CODPAG,codpag);
  if (_caus->intra())
  {
    mov_rec.put(MOV_CODVALI,codval);mov_rec.put(MOV_CAMBIOI,cambio);
    real corrval(_doc->imponibile());
    real corrlire = corrval*cambio;
    if (codval.not_empty() && codval != "LIT")
    {
      mov_rec.put(MOV_CORRLIRE,corrlire);
      mov_rec.put(MOV_CORRVALUTA,corrval);
    } else
        mov_rec.put(MOV_CORRLIRE,corrval);
  }
  real totdocval(_doc->totale_doc());
  real totdoclit = totdocval * cambio;
  if (codval.not_empty() && codval != "LIT")
  {
    mov_rec.put(MOV_TOTDOC,totdoclit);
    mov_rec.put(MOV_TOTDOCVAL,totdocval);
  } else
      mov_rec.put(MOV_TOTDOC,totdocval);
  return _error;
}

void TContabilizzazione_app::calculate_spese(real& spese, real& sp_iva, int ndec, bool is_incasso)
{
  char tipo;
  int  gr,co;
  long so;
  const bool is_cli = _doc->get("TIPOCF") == "C";
  const TBill& zio = is_incasso ? (is_cli ? _spin_billv : _spin_billa) : (is_cli ? _spbo_billv : _spbo_billa);
  TIVA sp_cod(is_incasso ? _spin_cod : _spbo_cod);
  TIVA_element el_tmp;
  TString key;

  sp_iva = iva(spese,sp_cod,ndec);
  tipo = zio.tipo();
  gr = zio.gruppo();
  co = zio.conto();
  so = zio.sottoconto();
  key.format("5|%-4s|%c|%3d|%3d|%6ld",(const char*)sp_cod.codice(),tipo,gr,co,so);
  const bool exists = _righe_iva.is_key(key);
  TIVA_element& el  = (exists ? (TIVA_element&)_righe_iva[key] : el_tmp);
  el.imp() = spese; 
  el.iva() = sp_iva;
  el.ali() = sp_cod.aliquota();
  _righe_iva.add(key,el,exists);
}

error_type TContabilizzazione_app::add_spese_inbo()
// Aggiunge le righe di spese incasso/bolli
{
  real tot_netto, sp_incasso, sp_bolli;
  real iva_sp_incasso, iva_sp_bolli;
  const int ndec = _doc->in_valuta() ? 3 : 0;
  
  // Aggiunge le spese d'incasso
  tot_netto = _doc->totale_netto();
  sp_incasso = _doc->spese_incasso(tot_netto,ndec,_netto/*TRUE*/);
  if (sp_incasso != 0.0)
    calculate_spese(sp_incasso,iva_sp_incasso,ndec,_netto/*TRUE*/);
    
  // Aggiunge le spese bolli
  tot_netto += sp_incasso + iva_sp_incasso;
  sp_bolli = _doc->bolli(tot_netto,ndec, _netto/*TRUE*/);
  if (sp_bolli != 0)
    calculate_spese(sp_bolli,iva_sp_bolli,ndec,FALSE);
  
  return _error;
}

error_type TContabilizzazione_app::add_iva_row(const TBill& conto, const TRiga_documento& r)
// Aggiunge le righe iva all'assoc_array di raggruppamento
{
  TIVA_element el_tmp;
  TString cod(r.iva().codice());
  const char tipo = conto.tipo();
  const int  gr = conto.gruppo();
  const int  co = conto.conto();
  const long so = conto.sottoconto();
  TString key;
  const char tipo_r = r.tipo().tipo();
  int ord=0;
  // Ordine con cui vengono immesse le righe IVA:
  // merce, omaggi, prestazioni, spese, bolli/spese d'incasso, sconti.
  
  switch (tipo_r)
  {
    case 'M':
      ord = 1;
      break;
    case 'O':
      ord = 2;
      break;
    case 'P':
      ord = 3;
      break;
    case 'S':
      ord = 4;
      break;
    case 'C':
      ord = 6;
      break;
    default: break;
  }
  key.format("%d|%-4s|%c|%3d|%3d|%6ld",ord,(const char*)cod,tipo,gr,co,so);
  const bool exists = _righe_iva.is_key(key);
  TIVA_element& el  = (exists ? (TIVA_element&)_righe_iva[key] : el_tmp);
  el += r;
  _righe_iva.add(key,el,exists);
  return no_error;
}

error_type TContabilizzazione_app::create_iva_rows()
{
  const int items = _righe_iva.items();
  const bool in_valuta = _doc->in_valuta();
  TRectype& head = _movimento->lfile().curr();
  int gr,co;
  long so;
  char tipo;
  TToken_string key;
  TString_array key_arr;
  TString16 codiva;
  TBill conto;
  
  const int annoes = head.get_int(MOV_ANNOES);
  const long numreg = head.get_long(MOV_NUMREG);
  real cambio = head.get_real(MOV_CAMBIO);
  real imponibile,imposta;

  _righe_iva.get_keys(key_arr);  
  key_arr.sort();
  //TIVA_element* cur = (TIVA_element*)_righe_iva.first_item();
  TIVA_element cur;
  for (int i = 0; i<items/* && cur != NULL*/;i++)
  {
    //key =_righe_iva.get_hashobj()->key();
    key = key_arr.row(i);
    cur = (TIVA_element&) _righe_iva[key];
    codiva = key.get(1);
    tipo = key.get_char(2);
    gr = key.get_int(3);
    co = key.get_int(4);
    so = atol(key.get(5));
    conto.set(gr,co,so,tipo);
    conto.find();
    imponibile = cur.imp();
    if (in_valuta)
    {
      imponibile = cur.imp() * cambio; //  imponibile in lire
      imposta = (imponibile * cur.ali()) / 100.0; // questa e' l'imposta ricalcolata
    }
    else
      imposta = cur.iva();
    // In contabilita' abbiamo 2 decimali...
    imponibile.ceil(2);
    imposta.ceil(2);
    TRectype& rec_iva = _movimento->iva(i);
    rec_iva.put(RMI_ANNOES,annoes);
    rec_iva.put(RMI_NUMREG,numreg);
    rec_iva.put(RMI_NUMRIG,i+1); // La numerazione comincia da 1
    rec_iva.put(RMI_CODIVA,codiva);
    rec_iva.put(RMI_IMPONIBILE,imponibile);
    rec_iva.put(RMI_IMPOSTA,imposta);
    rec_iva.put(RMI_TIPOCR,conto.tipo_cr());
    rec_iva.put(RMI_INTRA,_caus->intra());
    rec_iva.put(RMI_TIPOC,conto.tipo());
    rec_iva.put(RMI_GRUPPO,conto.gruppo());
    rec_iva.put(RMI_CONTO,conto.conto());
    rec_iva.put(RMI_SOTTOCONTO,conto.sottoconto());
    //cur = (TIVA_element*)_righe_iva.succ_item();
  }
  return _error;
}

error_type TContabilizzazione_app::create_total_doc_row()
// Crea la riga contabile di totale documento
{
  TRectype& rec_cg = _movimento->cg(0);
  TRectype& head = _movimento->lfile().curr();
  const int annoes = head.get_int(MOV_ANNOES);
  const long numreg = head.get_long(MOV_NUMREG);
  TDate datareg(head.get_date(MOV_DATAREG));
  real totdoc(head.get_real(MOV_TOTDOC));
  char rowtype = 'T';
  TLocalisamfile& cli_file = _clifo->lfile();
  TString16 tipocf(cli_file.get(CLI_TIPOCF));
  const long  codcf = cli_file.get_long(CLI_CODCF);
  int gruppo = 0, conto = 0;
  
  // Trova il mastro del cliente/fornitore
  // cerca prima sul record del cliente/fornitore
  if (_clifo->good())
  {
    gruppo = cli_file.get_int(CLI_GRUPPO);
    conto =  cli_file.get_int(CLI_CONTO);
  }
  
  // poi sulla categoria di vendita (solo per i clienti)
  if ((gruppo == 0 || conto == 0) && tipocf == "C")
  {
    TString16 cod_cve(_clifo->lfile(LF_CFVEN).get(CFV_CATVEN));
    _cve->put("CODTAB",cod_cve);
    if (_cve->read() == NOERR)
    {
      gruppo = _cve->get_int("I3");
      conto  = _cve->get_int("I4");
    }
  }
  
  TString descr;
  descr = head.get(MOV_DESCR);// La descrizione della riga di totale documento la prende dalla testata
  // se ancora non e' stato trovato piglia quello della causale
  if (gruppo == 0 || conto == 0)
  {
    TBill zio;
    _caus->bill(1,zio);
    gruppo = zio.gruppo();
    conto = zio.conto();
  }
  
  if (gruppo == 0 || conto == 0)
    _error = conto_error;
  
  TRectype& first_iva_row = _movimento->iva(0);
  const char tc = first_iva_row.get_char(RMI_TIPOC);
  const int grc = first_iva_row.get_int(RMI_GRUPPO);
  const int coc = first_iva_row.get_int(RMI_CONTO);
  const long soc =first_iva_row.get_long(RMI_SOTTOCONTO);
  rec_cg.put(RMV_ANNOES,annoes);rec_cg.put(RMV_NUMREG,numreg);
  rec_cg.put(RMV_NUMRIG,1); rec_cg.put(RMV_SEZIONE,tipocf == "C" ? "D" : "A");
  rec_cg.put(RMV_DATAREG,datareg); rec_cg.put(RMV_TIPOC,tipocf);
  rec_cg.put(RMV_GRUPPO,gruppo);rec_cg.put(RMV_CONTO,conto);
  rec_cg.put(RMV_SOTTOCONTO,codcf); rec_cg.put(RMV_DESCR,descr);
  rec_cg.put(RMV_TIPOCC,tc); rec_cg.put(RMV_GRUPPOC,grc);
  rec_cg.put(RMV_CONTOC,coc);rec_cg.put(RMV_SOTTOCONTOC,soc);
  rec_cg.put(RMV_IMPORTO,totdoc); rec_cg.put(RMV_ROWTYPE,rowtype);
  
  return _error;
}

error_type TContabilizzazione_app::compile_rows_mov()
// Compila le righe
{
  const int rows = _doc->rows();
  
  _righe_iva.destroy(); // resetta l'assoc_array delle righe di iva  
  for (int i=1; good() && i<=rows; i++) // browse all this fucked document rows
  {
    const TRiga_documento& r= (*_doc)[i];
    TString16 tiporiga(r.get("TIPORIGA"));
    _tri->put("CODTAB",tiporiga);
    if (_tri->read() == NOERR) // controlla l'esistenza della riga
    {
      TBill conto;
      const char tipo = r.tipo().tipo();
      if (tipo != 'D')
      {
        search_costo_ricavo(conto,r); // l'errore eventuale viene settato qui dentro
        if (good())
          add_iva_row(conto,r);
      }
    }
    else
      _error = row_type_error;
  }
  
  if (good() && _righe_iva.items() == 0)
    _error = no_rows_error;
  
  // Crea le righe per le spese d'incasso e bolli
  if (good())
    add_spese_inbo();
  
  // Crea le righe di IVA
  if (good())
    create_iva_rows();
  // Crea la riga di totale documento
  if (good())
    create_total_doc_row();
  // crea le righe di contabilita'
  if (good())
    if (!_movimento->recalc_cg_rows(_caus)) _error = movement_error;
  return _error;
}

void TContabilizzazione_app::calc_pagament()
{
  TRectype& head = _movimento->lfile().curr();
  TString16 codpag(head.get(MOV_CODPAG));
  TString16 data(_doc->get("DATAINSC"));
  _pagamento = new TPagamento(codpag, data);
  real totspese = _doc->spese();
  real totimposte = _doc->imposta();
  real totimponibili = _doc->totale_doc() - totimposte - totspese;
  const bool valuta = head.get(MOV_CODVAL).not_empty();
  if (valuta)
  {
    const real change(head.get_real(MOV_CAMBIO));
    real val1 = totimponibili * change;
    real val2 = totimposte * change;
    real val3 = totspese * change;
    _pagamento->set_total_valuta( totimponibili, totimposte, totspese, change, val1, val2 ,val3);
  }
  else
    _pagamento->set_total( totimponibili, totimposte, totspese );
  _pagamento->set_rate_auto( );      
}

error_type TContabilizzazione_app::write_scadenze()
// Scrive le scadenze. Liberamente tratto da cg2104.cpp.
{
  calc_pagament();
  
  const TRectype& head = _movimento->lfile().curr();
  const long nreg = head.get_long(MOV_NUMREG);
  const int anno  = head.get_int(MOV_ANNOIVA);
  const TString numpart(head.get(_nump_cfg ? MOV_PROTIVA : MOV_NUMDOC));
  
  //TPartita* oldgame = partite().first();
  TPartita* newgame = NULL;
  
  int nuova_riga = 0;

  if (anno > 0 && !numpart.blank())
  {
    const int tmov = _caus->tipomov();
    const TString desc(head.get(MOV_DESCR));
    const TString codpag(head.get(MOV_CODPAG));
    const TString codcaus(_caus->codice());
    const TString v(head.get(MOV_CODVAL));
    const TDate   d(head.get_date(MOV_DATACAM));
    const real    c(head.get_real(MOV_CAMBIO)); 
    const TValuta cambio(v, d, c); 
    const TString agente(_doc->get("CODAG")); 
    const char sezione = _movimento->cg(0).get_char(RMV_SEZIONE);        // Dare/Avere
 
    TBill clifo; 
    const char tipocf = head.get_char(MOV_TIPO);
    const long codcf  = head.get_long(MOV_CODCF);
    clifo.set(0,0,codcf,tipocf);
    newgame = new TPartita(clifo, anno, numpart);
  
    const int row = newgame->prima_fattura(nreg);         // Riga fattura di questo movimento
    TRiga_partite& partita = row <= 0 ? newgame->new_row() : newgame->riga(row);
    nuova_riga = partita.get_int(PART_NRIGA);
    
    CHECK(_pagamento,"E che diavolo! Ti avevo detto di calcolare prima _pagamento");
    TPagamento& pag = *_pagamento;                      
                          
    // put data on partita
    partita.put(PART_TIPOMOV,   tmov);
    partita.put(PART_NREG,      nreg);
    partita.put(PART_NUMRIG,    1);
    partita.put(PART_DATAREG,   head.get_date(MOV_DATAREG)); 
    partita.put(PART_DATADOC,   head.get_date(MOV_DATADOC));
    partita.put(PART_NUMDOC,    head.get(MOV_NUMDOC));
    partita.put(PART_DESCR,     desc);
    partita.put(PART_CODCAUS,   codcaus);
    partita.put(PART_REG,       _caus->reg().name());
    partita.put(PART_PROTIVA,   head.get_long(MOV_PROTIVA));
    partita.put(PART_SEZ,       sezione);
    
    const real totdoc(head.get_real(MOV_TOTDOC));
    partita.put(PART_IMPTOTDOC, totdoc);
    
    cambio.put(partita);
    const bool in_valuta = cambio.in_valuta();

    real imponibile, imponibile_val;   
    for (int i = pag.n_rate()-1; i >= 0; i--)
    {            
      if (in_valuta)
        imponibile_val += pag.tval_rata(i);
      imponibile += pag.tlit_rata(i);
    }

    partita.put(PART_IMPORTO, imponibile);
    partita.put(PART_IMPORTOVAL, imponibile_val);
    partita.put(PART_IMPOSTA, pag.imposta());
    partita.put(PART_SPESE, pag.spese());
    
    const TString abipr(_doc->get("CODABIP")),
                  cabpr(_doc->get("CODCABP")),
                  abi(_doc->get("CODABIA")),
                  cab(_doc->get("CODCABP"));
    
  
    partita.elimina_rata(-1);                    // Elimina tutte le rate eventuali
    const int nr = pag.n_rate();
    for (i = 0; i < nr; i++)
    {    
      TRiga_scadenze& scadenza = partita.new_row(); 

      scadenza.put(SCAD_CODPAG,     codpag);     // Codice pagamento
      scadenza.put(SCAD_CODAG,      agente);     // Codice agente

      scadenza.put(SCAD_DATASCAD,   pag.data_rata(i));  // Data scadenza
      scadenza.put(SCAD_IMPORTO,    pag.tlit_rata(i));  // Importo
      if (in_valuta)
        scadenza.put(SCAD_IMPORTOVAL, pag.tval_rata(i)); // Importo in valuta
      scadenza.put(SCAD_TIPOPAG,    pag.tipo_rata(i));    // Tipo pagamento
      scadenza.put(SCAD_ULTCLASS,   pag.ulc_rata(i));     // Ulteriore classificazione
      scadenza.put(SCAD_CODABIPR,   abipr);              // Ns ABI  
      scadenza.put(SCAD_CODCABPR,   cabpr);              // Ns CAB  
      scadenza.put(SCAD_CODABI,     abi);                // Vs ABI  
      scadenza.put(SCAD_CODCAB,     cab);                // Vs CAB 
//      scadenza.put(SCAD_DESCR,     ????);          // Note  
    }
  }

  if (newgame != NULL)           // Se non ho cancellato il numero partita ...
  {                    
    if (!newgame->write())            // Salva nuova partita
      error_box("Si e' verificato un errore scrivendo le scadenze del documento.");
    delete newgame;
  }  
  
  if (_pagamento != NULL)
  {
    delete _pagamento;
    _pagamento = NULL;
  }
  return _error;
}

error_type TContabilizzazione_app::write_all()
// Scrive il movimento e le scadenze, gestendo la rinumerazione se il movimento e' gia presente
{
  // N.B: _error non viene settato, per non stampare il messaggio di errore 2 volte.
  // basta solo ritornare qualcosa di != da no_error, per evitare le operazioni successive
  // a write_all
  TRectype& head = _movimento->lfile().curr();
  long numreg = head.get_long(MOV_NUMREG);
  while (_movimento->write() == _isreinsert)
    head.put(MOV_NUMREG,++numreg);
  if (_movimento->status() != NOERR)
  {
    error_box("Errore %d scrivendo il movimento %ld.",_movimento->status(),numreg);
    return generic_error;
  }
  if (sc_enabled()) write_scadenze();
  return no_error;
}

error_type TContabilizzazione_app::change_doc_status()
// Cambia lo stato del documento
{
  TLocalisamfile d(LF_DOC);
  _doc->stato(_final_doc_status);
  if (_doc->head().rewrite(d) != NOERR)
    _error = chg_stat_error; 
  return _error;
}

void TContabilizzazione_app::display_error()
{
  TString msg;
  TString numerazione = _doc->numerazione();
  const long numero = _doc->numero();

  switch (_error)
  {
    case elaboration_error:
      msg.format("Il documento %s/%ld non rientra tra i tipi documento validi per l'elaborazione."
                "Verificare i tipi documento ed il loro stato iniziale sul codice elaborazione inserito.",(const char*)numerazione,numero);
      break;
    case nr_es_error:
      msg.format("Rilevato un codice esercizio errato contabilizzando il documento %s/%ld."
                "Verificare l'esistenza e la correttezza della tabella esercizi e della data del documento.",(const char*)numerazione,numero);
      break;
    case nr_reg_error:
      msg.format("Rilevato un numero di registrazione errato contabilizzando il documento %s/%ld."
                "Verificare l'integrita' del file movimenti.",(const char*)numerazione,numero);
      break;
    case nr_doc_error:
      msg.format("Rilevato un numero di documento errato contabilizzando il documento %s/%ld."
                "Verificare il numero documento e il codice numerazione inseriti in tabella.",(const char*)numerazione,numero);
      break;
    case chg_stat_error:
      msg.format("Rilevato un errore cambiando lo stato al documento %s/%ld."
                "Verificare l'integrita' del file documenti.",(const char*)numerazione,numero);
      break;
    case clifo_error:
      msg.format("Rilevato un errore caricando le informazioni del Cli/Fo sul documento %s/%ld."
                "Verificare l'esistenza delle informazioni inserite sul file documenti e Cli/Fo.",(const char*)numerazione,numero);
      break;
    case ultprot_error:
      msg.format("Rilevato un numero di protocollo IVA errato relativamente al documento %s/%ld."
                "Verificare le informazioni inserite sul registro %s/%d.",(const char*)numerazione,numero,
                (const char*) _caus->reg().name(),_caus->reg().year());
      break;
    case datadoc_error:
      msg.format("Rilevato una data documento vuota relativamente al documento %s/%ld."
                "Verificare l'informazione inserita.",(const char*)numerazione,numero);
      break;
    case caus_error:
      msg.format("Rilevato un errore caricando la causale relativamente al documento %s/%ld."
                "Verificare l'esistenza del codice causale inserito.",(const char*)numerazione,numero);
      break;
    case register_error:
      msg.format("Rilevato un errore caricando il registro relativamente al documento %s/%ld."
                "Verificare l'esistenza del codice causale/registro inseriti.",(const char*)numerazione,numero);
      break;
    case change_error:
      msg.format("Rilevato un cambio senza valuta relativamente al documento %s/%ld."
                "Verificare la correttezza delle informazioni inserite.",(const char*)numerazione,numero);
      break;
    case val_error:
      msg.format("Rilevato un codice valuta inesistente relativamente al documento %s/%ld."
                "Verificare la correttezza della informazione inserita.",(const char*)numerazione,numero);
      break;
    case codpag_error:
      msg.format("Rilevato un codice pagamento non esistente relativamente al documento %s/%ld."
                "Verificare l'esistenza del codice pagamento inserito.",(const char*)numerazione,numero);
      break;
    case row_type_error:
      msg.format("Rilevato un codice tipo riga non esistente relativamente al documento %s/%ld."
                "Verificare l'esistenza dei vari codici riga inseriti.",(const char*)numerazione,numero);
      break;
    case no_rows_error:
      msg.format("Nessuna riga iva contabile e' stata trovata relativamente al documento %s/%ld."
                "Verificare l'esistenza dei vari codici riga inseriti.",(const char*)numerazione,numero);
      break;
    case conto_error:
      msg.format("Rilevato un conto di costo/ricavo inesistente relativamente al documento %s/%ld."
                "Verificare l'esistenza dei conti associati alle righe.",(const char*)numerazione,numero);
      break;
    case movement_error:
      msg.format("Rilevato uno sbilancio nel movimento relativamente al documento %s/%ld."
                "Verificare la correttezza degli importi delle righe.",(const char*)numerazione,numero);
      break;
    case mov_write_error:
      msg.format("Rilevato un errore in scrittura movimento relativamente al documento %s/%ld."
                "Verificare la consistenza dei files.",(const char*)numerazione,numero);
      break;
    default:
      msg.format("E' stato rilevato un errore generico contabilizzando il documento %s/%ld.",
                  (const char*)numerazione,numero);
      break;
  }
  warning_box(msg);
  _error = no_error; // reset error, as any other one would do, so you can show me the other ones.
  _can_write = FALSE; // But from now on u cannot write anymore. U must exit this program and repair errors occurred.
}

void TContabilizzazione_app::contabilize_document()
{
  _movimento = new TMovimentoPN_VE;
  
  //_doc->compile_summary(); // ricalcola il documento

  compile_head_mov();
  if (good()) compile_rows_mov();

  if (good() && _can_write)
    if (write_all() == no_error)  // Se la scrittura e' andata ok...
    {
      _total_docs++;
      change_doc_status();
    }
  if (_caus != NULL)
  {
    delete _caus;
    _caus = NULL;
  }
  delete _movimento;
}

void TContabilizzazione_app::contabilize()
{
  TRelation doc_rel(LF_DOC);
  TLocalisamfile righe_doc(LF_RIGHEDOC);
  TRectype da(LF_DOC),a(LF_DOC);
  const long items = _num_sheet->items();
  int year_from = _data_ini.year();
  int year_to = _data_fine.year();
  TString16 codnum;
  TString msg,filt_expr;

  
  _total_docs = 0;
  da.put("DATADOC",_data_ini);
  da.put("PROVV","D");
  da.put("ANNO",year_from);
  a.put("DATADOC",_data_fine);
  a.put("PROVV","D");
  a.put("ANNO",year_to);
  for (long i=0L; i<items;i++)  // Scorre per tutte le numerazioni dello sheet
    if (_num_sheet->checked(i)) // Se la numerazione corrente e' stata selezionata
    {                           // istanzia un cursore per la numerazione corrente, con i limiti di data
      codnum = _num_sheet->row(i).get(1);
      filt_expr = "CODNUM=\"";
      filt_expr << codnum << "\"";
      TCursor doc_cur(&doc_rel,filt_expr,3,&da,&a);
      const long cur_items = doc_cur.items(); // Scorre tutti i documenti che rientrano nell'intervallo selezionato
      if (cur_items == 0) 
      {
        warning_box("Non vi sono documenti da contabilizzare per il codice numerazione %s",(const char*)codnum);
        continue;
      }
      msg = "Contabilizzazione documenti ";
      msg << codnum << " dal ";
      msg << _data_ini.string() << " al ";
      msg << _data_fine.string();
#ifdef DBG      
      TProgind p(cur_items,msg,TRUE,TRUE,1);
#else
      TProgind p(cur_items,msg,FALSE,TRUE,1);
#endif
      long j = 0;
#ifdef DBG      
      for (;j<cur_items && !p.iscancelled();j++)
#else      
      for (;j<cur_items;j++)
#endif      
      { 
        p.setstatus(j+1);
        doc_cur = j;
        if (_doc->read(doc_cur.curr()) != NOERR) continue; // legge il documento
        if (codnum != _doc->get("CODNUM")) continue; // patch del cazzo
        if (doc_tipo_stato_ok())    // controlla che il tipo documento e lo stato siano coerenti con la ELD selezionata
          contabilize_document();
        else
        {
          // ATTENZIONE!: se non ha ancora cominciato a contabilizzare, eventuali
          // documenti con tipo o stato differenti vengono semplicemente scartati
          // senza segnalare errori
          if (_total_docs > 0)
            _error = elaboration_error;
        }
        if (!good())    
          display_error();
      }
#ifdef DBG      
      if (p.iscancelled()) break;
#endif
    }
  if (_total_docs > 0)
    message_box("Totale documenti contabilizzati: %ld",_total_docs);
    
}

int ve6100 (int argc, char **argv)
{
  TContabilizzazione_app a;
  a.run(argc,argv,"Contabilizzazione documenti");
  return TRUE;
}
