#include <config.h>
#include <execp.h>
#include <mailbox.h>
#include <modaut.h>
#include <msksheet.h>
#include <urldefid.h>
#include <utility.h>

#include "cgpagame.h"                 

#include "cg2100.h"
#include "cg2102.h"
#include "cg21sld.h"

// Nomi dei campi
#include <clifo.h>
#include <rcausali.h>

/////////////////////////////////////////////////////////// 
// Dati incasso immediato
///////////////////////////////////////////////////////////

class TDati_incasso : public TObject
{              
public:       
  int _step;
  TString _causale;
  TBill _clifo;
  TDate _datadoc;
  TString _numdoc;
  real _totale;
  TString _descrizione;
  TString _causale_fattura;
  TipoIVA _iva_fattura;
};

/////////////////////////////////////////////////////////// 
// Applicazione principale
///////////////////////////////////////////////////////////

TPrimanota_application::TPrimanota_application()
: _rel(NULL), _mode(0), _iva(nessuna_iva), _causale(NULL), _giornale(NULL),
  _lastreg(0), _last_dreg(TODAY), _last_dcom(TODAY), _incasso(NULL)
{
  memset(_msk, 0, sizeof(_msk));
}

TMask* TPrimanota_application::load_mask(int n)
{ 
  if (_msk[n] != NULL) 
    return _msk[n];

  TFilename name("cg2100"); name << char(n == 3 ? 'o' : 'a'+n);
  TMask* m = new TMask(name);
  
  switch (n)
  {
  case 0:        
    if (m)
    {
      m->set_handler(F_NUMREG,   num_handler);
      m->set_handler(F_CODCAUS,  caus_query_handler);
      m->set_handler(F_DATAREG,  datareg_handler); 
      // DATACOMP new way
      // m->set_handler(F_DATACOMP, datacomp_handler);

      TConfig c(CONFIG_STUDIO, "cg");  

      // Scelta primo campo col focus    
      _firstfocus = c.get_bool("PoCuDr") ? F_DATAREG : F_CODCAUS;
      m->first_focus(_firstfocus);
      
      // Registra e inserisci immediatamente un nuovo movimento
      _savenew = !c.get_bool("Cg21SN");
    }  
    break;
  case 2:
    if (m)
    { 
      m->set_handler(F_DATADOC, datadoc_handler); 
      m->set_handler(F_NUMDOC, numdoc_handler); 
      m->set_handler(F_DATA74TER, data74ter_handler); 
      m->set_handler(F_PROTIVA, protiva_handler);
      m->set_handler(F_CLIENTE, clifo_handler);
      m->set_handler(F_FORNITORE, clifo_handler);
      m->set_handler(F_RITFIS, ritfis_handler);
      m->set_handler(F_RITSOC, ritsoc_handler);
      m->set_handler(F_CORRLIRE, corrlire_handler);
      m->set_handler(F_CORRVALUTA, corrvaluta_handler);
      m->set_handler(F_CODIVA, main_codiva_handler);
      m->set_handler(F_OCCASEDIT, occas_handler);
      m->set_handler(F_SOLAIVA, solaiva_handler);
      m->set_handler(F_SHEETIVA, iva_handler);
      m->set_handler(F_CODPAG, codpag_handler);
      
      TSheet_field& is = (TSheet_field&)m->field(F_SHEETIVA);
      is.set_notify(iva_notify);
      TMask& ism = is.sheet_mask();
      ism.set_handler(101, imponibile_handler);
      ism.set_handler(102, codiva_handler);
      ism.set_handler(103, detrazione_handler);
      ism.set_handler(104, imposta_handler);
      ism.set_handler(107, cg_gruppo_handler);
      ism.set_handler(109, iva_sottoconto_handler);
      ism.set_handler(110, iva_sottoconto_handler);
      ism.set_handler(209, sheet_clifo_handler);
      ism.set_handler(309, sheet_clifo_handler); 
      
      // Se esiste lo sheet delle rate
      if (is_fattura())
      {
        TSheet_field& ps = (TSheet_field&)m->field(FS_RATESHEET);
        ps.set_notify(pag_notify);                        
        m->set_handler(FS_RATESHEET, pag_sheet_handler);
        m->set_handler(F_ANNORIF,    annorif_handler);
        m->set_handler(F_NUMRIF,     numrif_handler);
        m->set_handler(FS_RESET,     reset_handler);
        m->set_handler(FS_NRATE,     nrate_handler); 
        m->set_handler(FS_RECALC,    recalc_handler);
        m->set_handler(FS_NSCAB,     codcab_handler);
        m->set_handler(FS_VSCAB,     codcab_handler);
        
        TMask& sm = ps.sheet_mask();
        sm.set_handler(102, ratalit_handler);
        sm.set_handler(103, rataval_handler);
        sm.set_handler(105, tipopag_handler);
        sm.set_handler(106, tipopag_handler);
      }  
    }   
  case 1:
    if (m)
    {  
      m->first_focus(_firstfocus);
      m->set_handler(F_DATAREG, datareg_handler);
      m->set_handler(F_DATACOMP, datacomp_handler);
      m->set_handler(F_DESCR, descr_handler);
      m->set_handler(F_CODCAUS, caus_modify_handler);
      m->set_handler(F_TOTALE, totdoc_handler);
      m->set_handler(F_SHEETCG, cg_handler);
      m->set_handler(SK_VALUTA, valuta_handler);
      m->set_handler(SK_DATACAMBIO, datacambio_handler);
      m->set_handler(SK_CAMBIO, cambio_handler);
      m->set_handler(SK_TOTDOCVAL, totdocval_handler);
      
      if (easydoc_installed())
      {
        m->show(F_EASYDOC);
        m->set_handler(F_EASYDOC, easydoc_handler);
      }

      TSheet_field& cg = (TSheet_field&)m->field(F_SHEETCG);                                                      
      cg.set_notify(cg_notify);
      TMask& cgm = cg.sheet_mask();
      
      cgm.set_handler(CG_DARE, dareavere_handler);
      cgm.set_handler(CG_AVERE, dareavere_handler);
      cgm.set_handler(CG_GRUPPO, cg_gruppo_handler);
      cgm.set_handler(CG_CONTO, cg_conto_handler);
      cgm.set_handler(CG_SOTTOCONTO, suspended_handler);
      cgm.set_handler(CG_SOTTOCONTO + 100, sheet_clifo_handler);
      cgm.set_handler(CG_SOTTOCONTO + 200, sheet_clifo_handler);

      cgm.set_handler(113, suspended_handler);    // Contropartita
      cgm.set_handler(213, sheet_clifo_handler);
      cgm.set_handler(313, sheet_clifo_handler);
      
      if (!fexist("batbcdc.msk"))
      {
        cgm.hide(CG_CDC);
        cg.delete_column(CG_CDC);
      }
    }  
    break;
  case 3:
    m->set_handler(O_CODICE, occas_code_handler);
    break;  
  default:
    CHECKD(0, "Che ca$$o di maschera e' la ", n);
    break;  
  }
  
  _msk[n] = m;
  return m;
}

void TPrimanota_application::open_files(int logicnum, ...)  
{  
  va_list marker;
  va_start(marker, logicnum);
  while (logicnum > 0)
  {
    CHECKD(_file.objptr(logicnum) == NULL, "File gia' aperto: ", logicnum);
    _file.add(new TLocalisamfile(logicnum), logicnum);
    logicnum = va_arg(marker, int);
  }
}

bool TPrimanota_application::user_create()
{
  open_files(LF_TABCOM, LF_TAB, LF_CAUSALI, LF_RCAUSALI, LF_CLIFO, LF_PCON, 0);
  open_files(LF_ATTIV, LF_SALDI, LF_COMUNI, LF_OCCAS, LF_NDITTE, 0);
  open_files(LF_SCADENZE, LF_PAGSCA, 0);
  
  _incasso = new TDati_incasso;
  
  _rel = new TMovimentoPN;
  _rel->add(LF_PARTITE, "NREG=NUMREG", 2);        // Collega la partita aperta dalla fattura

  _causale = new TCausale();
  _giornale = new TLibro_giornale();
  
  set_search_field(F_NUMREG);                     // Set field for default search

  _pag = NULL;  
  _is_saldaconto = FALSE;                            
  
  load_mask(0);                                   
  
  load_colors();
  
  return TRUE;
}


bool TPrimanota_application::save_and_new() const
{ 
  return _savenew || _incasso->_step == 1; 
}


bool TPrimanota_application::user_destroy()
{ 
  for (int m = 3; m >= 0; m--)
    if (_msk[m] != NULL) delete _msk[m];
  
  if (_pag) delete _pag;

  delete _giornale;  
  delete _causale;
  delete _rel;
  
  delete _incasso;
  
  close_files();

  return TRUE;
}

// Legge la causale di codice cod ed il relativo registro dell'anno year
// Certified 99%
bool TPrimanota_application::read_caus(const char* cod, int year)
{
  bool ok = TRUE;

  if (cod != NULL)
    ok = causale().read(cod, year);    

  bool nob = FALSE, dob = FALSE;
  TipoIVA iva = nessuna_iva;
  _is_saldaconto = FALSE;
  
  if (ok)
  {
    _is_saldaconto = gestione_saldaconto() && causale().saldaconto();
    dob = is_fattura() || causale().data_doc();
    nob = is_fattura() || causale().num_doc();
    iva = causale().iva();
  } 
  
  TMask* m = _msk[iva == nessuna_iva ? 1 : 2];
  
  // Se sono chiamato dai campi di query ho belle che finito
  if (_mode == MODE_QUERY || m == NULL)  
    return TRUE;

  const bool ins = m->insert_mode();
  
  m->efield(F_NUMDOC).check_type(nob ? CHECK_REQUIRED : CHECK_NORMAL);  // Num. doc. obbligatorio
  m->efield(F_DATADOC).check_type(dob ? CHECK_REQUIRED : CHECK_NORMAL); // Data doc. obbligatoria
  
  const bool nota_credito = is_nota_credito();
  const bool show_games = is_pagamento() || nota_credito;

  TSheet_field& cgsheet = (TSheet_field&)m->field(F_SHEETCG);
  TMask& cgm = cgsheet.sheet_mask();
  cgm.set_handler(100, show_games ? showpartite_handler : NULL);  // bottoncino riga                                                             
  cgm.enable(100, show_games);
  if (iva == nessuna_iva)
  {
    m->enable(F_PROVVISORIO, !_is_saldaconto);        // Il saldaconto vieta i movimenti provvisori
    m->show(SK_TIPORIGA, is_pagamento());             // Abilita scelta tipo riga
    m->enable(SK_TIPORIGA);
  }  
  else
  {                       
    m->efield(F_CODPAG).enable(!nota_credito);
    
    TEdit_field& numrif = m->efield(F_NUMRIF);
    numrif.set_justify(iva == iva_acquisti ? _num_for : _num_cli);
    numrif.set_trim(!numrif.right_justified());
    activate_numrif(*m, FALSE);
    
    const bool av = causale().reg().agenzia_viaggi();
    m->show(F_DATA74TER, av);
    if (!av) m->reset(F_DATA74TER);
    
    if (ins)
    {
      const long protiva = causale().reg().protocol() + 1;
      m->set(F_PROTIVA, protiva, TRUE); // Aggiorna protocollo IVA
    }
  }                     

  if (ins && !ci_sono_importi())
  {        
    cgsheet.reset();
    for (int i = 1; i < causale().size(); i++)
    {
      const TRectype* rcaus = (TRectype*)causale().objptr(i);
      if (rcaus == NULL) continue;        // Evita eventuali righe nulle

      const int nriga = rcaus->get_int(RCA_NRIGA);
      if (nriga < 1) continue;            // Considera solo righe reali (non riempimenti)
      
      TBill tc; causale().bill(nriga, tc);
      if (tc.gruppo() <= 0) continue;      // Considera solo gruppi validi

      if (tc.tipo() > ' ' && tc.ok())
      {
        TBill clifo(0, 0, tc.sottoconto(), tc.tipo());
        if (clifo.find() && clifo.conto() != 0)          // Legge gruppo e conto
          tc = clifo;
      }  
      
      int err = 0;
      if (tc.descrizione() == "Sconosciuto") err = 1; 
      else if (tc.sospeso()) err = 2;
      
      if (err)
      {          
        error_box("Il conto della riga %d della causale e' %s", 
                  i, err == 1 ? "sconosciuto" : "sospeso");
        continue;          
      }

      const char sezione = rcaus->get_char(RCA_SEZIONE);
      const TImporto zero('D', ZERO);
      const TString80 desc(causale().desc_agg(i));

      if (nriga == 1) m->set(F_DESCR, desc);
      
      if (iva == nessuna_iva)
      {      
        char tipr = ' ';                   
        if (is_pagamento()) 
        {        
          if (nriga <= RIGA_DIFFCAM && nriga != RIGA_SPESE) 
            continue;  // Si considerano solo le spese
          tipr = 'G';
        }
        const int pos = set_cgs_row(-1, zero, tc, desc, tipr);
        if (sezione > ' ')
          cgsheet.disable_cell(pos, sezione == 'A' ? 0 : 1);
      }
      else
      {
        if (nriga >= 2 && nriga <= 9)
          continue;       // Conti per IVA detraibile e non, ritenute sociali e fiscali

        const char tipo = nriga == 1 ? 'T' : ' ';
        if (nriga == 1 && tc.tipo() > ' ' && tc.ok())
          m->set(tc.tipo() == 'C' ? F_CLIENTE : F_FORNITORE, tc.sottoconto());
        set_cgs_row(-1,zero,tc,desc,tipo);
      }
    }
    fill_sheet(*m);
  }
  
  return ok;
} 


// Certified 100%
void TPrimanota_application::print()
{
  TExternal_app stampa("cg3 -0");
  stampa.run();
}

bool TPrimanota_application::menu(MENU_TAG mt)
{             
  bool ok = TRUE;
  if (mt == MENU_ITEM(1))
    set_colors();
  else
    ok = TRelation_application::menu(mt);
  return ok;  
}


bool TPrimanota_application::changing_mask(int mode)
{
  if (mode == MODE_QUERY) 
  {
    const bool flag = _mode != MODE_QUERY; 
    return flag;
  }  
  _iva = iva_errata;
  return TRUE;
}


TMask* TPrimanota_application::get_mask(int mode)
{
  int annoiva = 0;
  TString16 caus;

  _mode = mode;

  switch (mode)
  {
  case MODE_INS:     
    annoiva = _msk[0]->get_int(F_ANNOIVA);
    caus    = _msk[0]->get(F_CODCAUS);
    break;
  case MODE_MOD:
    annoiva = _rel->curr().get_int(MOV_ANNOIVA);
    caus    = _rel->curr().get(MOV_CODCAUS);
    break;
  default:
    return load_mask(0);
  }
  
  if (giornale().year() != annoiva)
    giornale().read(annoiva);
  
  const TCausale& c = causale();
  if (caus != c.codice() || (c.iva() != nessuna_iva && c.reg().year() != annoiva))
    read_caus(caus, annoiva);

  _iva = c.iva();
  return load_mask(_iva == nessuna_iva ? 1 : 2); 
} 

void TPrimanota_application::on_firm_change()
{
  TRelation_application::on_firm_change();        // May be useful, who knows?
  
  _rel->lfile().last();
  _lastreg = _rel->lfile().get_long(MOV_NUMREG);  // Init last registration number
  
  _esercizi.update();                             // Init exercises
}  

void TPrimanota_application::on_config_change()
{
  TConfig cnf(CONFIG_DITTA, "cg");
  _ges_val       = cnf.get_bool("GesVal");
  _ges_sal       = cnf.get_bool("GesSal");
  _npart_is_prot = cnf.get_bool("RifPro"); 

  _num_cli       = cnf.get_bool("NrCliDx");
  _num_for       = cnf.get_bool("NrForDx");
  TPartita::carica_allineamento();
}


void TPrimanota_application::init_mask(TMask& m)
{
  disable_menu_item(M_FILE_PRINT);
  disable_menu_item(M_FONT);
  
  read_caus(NULL, 0);                      // Setta campi obbligatori
  fill_sheet(m);

  if (_iva == nessuna_iva)
  { 
    m.show(-5, _is_saldaconto);    // Abilita campi saldaconto     
  }
  else
  {                        
    const bool corrisp = causale().corrispettivi();

    bool clig, forg;
    if (_iva == iva_acquisti)
    {
      forg = TRUE;
      clig = FALSE;
    } 
    else
    {
      forg = FALSE;
      clig = corrisp ? FALSE : TRUE;
    }                              
    m.show(-1, clig);
    m.show(-2, forg);
    if (corrisp) m.hide(F_STATOPAIV);
    
    // Show/Hide campi valuta: F_VALUTAINTRA, F_CAMBIOINTRA, F_CORRLIRE, F_CORRVAL (GROUP 4)
    m.show(-4, causale().valintra());
    
    m.show(F_CODIVA, m.insert_mode());          // Codice IVA standard
    
    TSheet_field& is = ivas();
    is.enable_column(2, _iva == iva_acquisti);  // Tipo detrazione
    is.enable_column(4, !m.insert_mode());      // Tipo costo ricavo
 
    if (is_fattura())
    {
      const TPartita* game = partite().first();
      _scad_free = game == NULL || !game->esistono_abbuoni_diffcam(m.get_long(F_NUMREG));
      if (m.edit_mode())
        m.enable(DLG_DELREC, _scad_free);  // Disabilita tasto elimina
      m.enable(-1, _scad_free);            // Disabilita cliente
      m.enable(-2, _scad_free);
      m.enable(F_ANNORIF, _scad_free);     // Disabilita anno e numero partita
      m.enable(F_NUMRIF,  _scad_free);
      m.enable(F_SOLAIVA, _scad_free);     // Disabilita movimenti di sola IVA      
      m.enable(F_CODPAG,  _scad_free);     // Disabilita codice pagamento
      m.enable(FS_RESET,  _scad_free);     // Disabilita tasto di reset
      if (!_scad_free) 
        m.set(FS_RECALC, "", TRUE);        // Disabilita ricalcolo automatico 
      m.enable(FS_RECALC, _scad_free);     
    }  
  }
  
  // Show/Hide campi valuta: F_VALUTA, F_CAMBIO, F_DATACAMBIO (GROUP 3)
  const bool valuta = _ges_val && is_saldaconto() && causale().valuta();
  m.show(-3, valuta);
}

void TPrimanota_application::init_query_mode(TMask& m)
{
  enable_menu_item(M_FILE_PRINT);
  enable_menu_item(M_FONT);
    
  if (lnflag())
  {
    m.field(F_NUMREG).on_hit();  // Aggiorna opportunamente le date
  }
  else
  {
    m.set(F_DATAREG,  _last_dreg);    
    // DATACOMP new way                
    // m.set(F_DATACOMP, _last_dcom);  
  }  
  
  _incasso->_step = 0;           // Azzera flag di incasso immediato
}


void TPrimanota_application::fill_sheet(TMask& m) const
{                           
  const int filler = 16;
  
  const int cgpos = m.id2pos(F_SHEETCG);
  const int ivpos = m.id2pos(F_SHEETIVA);

  if (cgpos > 0 && (ivpos > 0 || !is_saldaconto()))
  {
    TSheet_field& cgs = (TSheet_field&)m.fld(cgpos);
    for (int r = cgs.items(); r < filler; r++) 
      cgs.row(r);
  }
  
  if (ivpos > 0)
  {
    TSheet_field& ivas = (TSheet_field&)m.fld(ivpos);
    for (int r = ivas.items(); r < filler; r++) 
      ivas.row(r);
  }
}


// Ritorna il prossimo numero di registrazione libero
// Certified 100%
bool TPrimanota_application::get_next_key(TToken_string& tmp)
{ 
  tmp.add(F_NUMREG); tmp.add(_lastreg+1);
  
  if (_rel->good())                                 // Not reinsert
  {
    TMask& m = curr_mask();
    if (m.insert_mode())
    {                       
      tmp.add(F_CODCAUS);          // Ricopia causale        
      switch (_incasso->_step)
      {       
      case 1:
        tmp.add(_incasso->_causale);
        _msk[0]->set(F_CODCAUS, _incasso->_causale);          // Joke get_mask
        _iva = nessuna_iva;            // Impedisce incremento del numero documento
        break;
      case 2:
        tmp.add(_incasso->_causale_fattura);
        _msk[0]->set(F_CODCAUS, _incasso->_causale_fattura);  // Joke get_mask
        _iva = _incasso->_iva_fattura; // Provoca incremento del numero documento
        break;
      default:
        tmp.add(m.get(F_CODCAUS));    // Ultima causale usata  
        _msk[0]->set(F_CODCAUS, m.get(F_CODCAUS));
        break;
      }                                      
      
      tmp.add(F_DATAREG);          // Ricopia data operazione 
      tmp.add(m.get(F_DATAREG));   
      
      tmp.add(F_DATACOMP);         // Ricopia data competenza
      if (m.field(F_DATACOMP).empty())
        tmp.add(m.get(F_DATAREG));   
      else  
        tmp.add(m.get(F_DATACOMP));  
      
      if (_iva == iva_vendite)
      {
        tmp.add(F_DATADOC); tmp.add(m.get(F_DATADOC));  // Ricopia data documento
        const long n = m.get_long(F_NUMDOC);
        if (n > 0)
        { tmp.add(F_NUMDOC); tmp.add(n+1); }            // incrementa numero documento
      }  
    }  
  }  

  return TRUE;
}


void TPrimanota_application::init_insert_mode(TMask& m)
{
  init_mask(m);             
  
  m.first_focus(_firstfocus);

  if (causale().reg().agenzia_viaggi())
    m.set(F_DATA74TER, m.get(F_DATAREG));              
  
  if (iva() != nessuna_iva)
  {
    occas_mask().reset();
    m.hide(F_OCCASEDIT);
    m.hide(F_ADJUST_PRORATA);  // In inserimento non puo' esistere un pro-rata errato!
  }
  
  partite().destroy();
  if (is_fattura())
  { 
    activate_numrif(m, FALSE);
    const TString16 dt(m.get(F_DATADOC));
    set_pagamento(NULL,dt);  
    set_scadenze(m);
  }  
  else
  {                   
    set_pagamento(NULL,NULL);         // Annulla gestione rate
  }  
  
  _saldi.reset();                     // Inizializza saldi
  
  if (_incasso->_step == 1)
    genera_incasso(NULL);
  else
    m.reset(F_DATACOMP);              // Azzera quasi sempre la data di competenza
                                      // La preservo solo per l'incasso immediato
                                      
  _as400 = FALSE;          // Un movimento nuovo non puo' essere trasferito da AS400
}


void TPrimanota_application::init_modify_mode(TMask& m)
{ 
  init_mask(m);             // Inizializzazione standard                   
  calcola_saldo();          // Verifica eventuali sbilanci contabili

  TSheet_field& cg = cgs();

  // Determina se il movimento e' stato trasferito da AS/400: 
  // praticamente controlla che non ci sia nessun tipo movimento sulle righe contabili.
  _as400 = FALSE;
  if (is_saldaconto() || iva() != nessuna_iva)
  {
    if (cg.items() > 0)
    {
      _as400 = TRUE;
      for (int i = cg.items()-1; i >= 0 && _as400; i--)
      {
        const char rt = row_type(cg.row(i));
        _as400 = rt <= ' ';
      }
    }  
  }
  
  if (_as400 && is_pagamento())
  {
    m.set(SK_TIPORIGA, "A");   // Forza il tipo riga normale
    m.disable(SK_TIPORIGA);    // Disabilita la sua modifica
  }
  
  // Non controllare il prorata durante la cancellazione automatica
  if (autodeleting() != 0x3)
  {
    if (!test_prorata())             // Controlla prorata
    {
      m.show(F_ADJUST_PRORATA);
      m.set_handler(F_ADJUST_PRORATA, prorata_handler);
  
      warning_box("Questo movimento e' stato registrato con una percentuale\n"
                  "prorata non compatibile con quella corrente:\n"
                  "Premere il bottone di correzione automatica delle righe.");
    }
    else
    { 
      // Nascondi il bottone del prorata se esiste
      if (m.id2pos(F_ADJUST_PRORATA) >= 0)
      {
        m.set_handler(F_ADJUST_PRORATA, NULL);
        m.hide(F_ADJUST_PRORATA);
      }  
    }  
  }
}

// Controlla sulla causale se il segno del totale documento (ritsoc=FALSE) 
// o quello delle ritenute sociali (ritsoc=TRUE) e' invertito rispetto al normale
bool TPrimanota_application::test_swap(bool ritsoc)
{
  const char sez = ritsoc ? causale().sezione_ritsoc() : causale().sezione_clifo();
  const bool s = (iva() == iva_vendite) ^ sez == 'D';
  return s;
}


int TPrimanota_application::read(TMask& m)
{
  m.reset();           // Azzera campi e relativi dirty = 3
  m.autoload(*_rel);    // Carica testata
  
  const long numreg = _rel->curr().get_long(MOV_NUMREG);

  partite().destroy(); // Azzera tutte le partite
  
  cgs().reset();       // Azzera tutte le righe contabili

  if (iva() != nessuna_iva)
  { 
    ivas().reset();   // Azzera tutte le righe iva
    
    occas_mask().reset();
    const TString16 occode(_rel->lfile().get("OCFPI"));
    occas_mask().set(O_CODICE, occode, TRUE);
  }

  _saldi.reset();                                // Azzera saldi
  _saldi.set_movprovv(_rel->lfile().get_char(MOV_PROVVIS) > ' ');   
  tiposal tsal = causale().apertura() ? apertura : 
                (causale().chiusura() ? chiusura : normale);
  _saldi.set_tipo_saldo(tsal);
  _saldi.set_anno_es(m.get_int(F_ANNOES));
  _saldi.set_num_ulmov(numreg);
  _saldi.set_data_ulmov((TDate)m.get(F_DATAREG));
  _saldi.set_movimentato(TRUE);

  for (int i = 0; i < _rel->cg_items(); i++)
  {
    const TRectype& r = _rel->cg(i);
    TToken_string& riga = cgs().row(i);          // Vuota la riga

    TImporto import(r.get_char("SEZIONE"), r.get_real("IMPORTO"));
    import.add_to(riga, 0);                      // Dare/Avere 101-102

    TBill conto; conto.get(r);
    riga.add(conto.string(0x3));                 // Conto 103-107
    
    _saldi.aggiorna(conto, import, FALSE);
    
    riga.add("");                                // Codice descrizione 108
    riga.add(r.get("DESCR"));                    // Descrizione riga 109

    conto.get(r, TRUE);
    riga.add(conto.string(0x3));                 // Contropartita 110-114
    riga.add(r.get("NUMGIO"));                   // Centro di costo 115
    const char tipo = r.get_char("ROWTYPE");         
    riga.add(tipo);                              // Tipo di riga 116
    
    disable_cgs_cells(i, tipo);
  }      

  if (_iva == nessuna_iva) 
    return _rel->status();
  
  TMask_field& solaiva = m.field(F_SOLAIVA);
  solaiva.set(i == 0 ? "X" : " ");
  solaiva.on_key(K_TAB);
  
  const int mese_liq = _rel->curr().get_int(MOV_MESELIQ);
  if (mese_liq != 0)
  {
    const TDate data_reg = m.get_date(F_DATAREG);
    const TDate data_liq(1, mese_liq, data_reg.year());
    if (_rel->date2liq(data_reg) != _rel->date2liq(data_liq))
      m.set(F_DIFFERITA, "X");
  }
  
  const bool to_swap = test_swap(FALSE);
  if (to_swap)
  {
    real totdoc(m.get(F_TOTALE));  
    totdoc = -totdoc;
    m.set(F_TOTALE, totdoc.string());
  }
  for (i = 0; i < _rel->iva_items(); i++)
  {
    TRectype& r = _rel->iva(i);
    TToken_string& riga = ivas().row(i);
    
    real imponibile(r.get("IMPONIBILE"));
    if (to_swap) imponibile = -imponibile;
    riga.add(imponibile.string());       // Imponibile 101

    riga.add(r.get("CODIVA"));           // IVA        102
    riga.add(r.get("TIPODET"));          // Detrazione 103

    real imposta(r.get("IMPOSTA"));
    if (to_swap) imposta = -imposta;
    if (imponibile.sign() * imposta.sign() < 0)
    {
      error_box("Registrazione con imponibile e imposta con segni discordi:\n"
                "assegnato il segno dell'imponibile");
      imposta = -imposta;
    }  
    riga.add(imposta.string());          // Imposta    104

    TBill c; c.get(r);
    c.add_to(riga, 4, 0x7);              // Conto 105-110
  }           
  
#ifdef DBG
  if (type2pos('T') >= 0 && type2pos('I') < 0)
    NFCHECK("Sono spariti i tipi riga!");
#endif  
  
  calcola_imp();                         // Calcola totale imponibile ed imposte
  
  if (is_fattura())                      // Ci dovrebbero essere delle scadenze
  {                        
    pags().reset();                      // Azzera sheet rate
    if (!read_scadenze(m))               // Se non esiste fattura
    {
      const TString dd(m.get(F_DATADOC));
      set_pagamento(NULL, dd);           // Ignora codice pagamento in testata
    }  
  }
  
  return _rel->status();
}


// Trasferisce i dati da maschera a movimento di prima nota
void TPrimanota_application::mask2rel(const TMask& m)
{
  m.autosave(*_rel);
  
  TRectype& rec = _rel->curr();             // Record della testata
  
  const long numreg = m.get_long(F_NUMREG);
  const TDate datareg(m.get(F_DATAREG));
  const int annoes = m.get_int(F_ANNOES);

  _saldi.set_movprovv(m.get(F_PROVVISORIO)[0] > ' ');
  tiposal tsal = causale().apertura() ? apertura : 
                (causale().chiusura() ? chiusura : normale);
  _saldi.set_tipo_saldo(tsal);
  _saldi.set_anno_es(annoes);
  _saldi.set_num_ulmov(numreg);
  _saldi.set_data_ulmov((TDate)m.get(F_DATAREG));
  _saldi.set_movimentato(TRUE);
  
  const int tm = causale().tipomov();
  char s[2];
  if (tm > 0) 
  {
    s[0] = tm+'0';
    s[1] = '\0';
  }  
  else
    s[0] = '\0';
  rec.put(MOV_TIPOMOV, s);  // Tolto dalla maschera (su file e' alfanumerico)!

  _rel->destroy_rows(numreg);            // Destroy all records
  cgs_pack();                            // Destroy all null rows

  TArray& rows = cgs().rows_array();
  
  // Controlla se e' un movimento con righe contabili
  if (iva() == nessuna_iva || !m.get_bool(F_SOLAIVA))  
  {                         
    for (int i = 0; i < rows.items(); i++)
    {
      TToken_string& row = (TToken_string&)rows[i];

      TImporto n; n = row;                         
      const TBill conto(row, 2, 0x3);
      _saldi.aggiorna(conto, n, TRUE);
      
      TRectype &r = _rel->cg(i);
      r.zero();

      r.put("NUMREG", numreg);              // Numero registrazione
      r.put("ANNOES", annoes);              // Anno esercizio
      r.put("DATAREG", datareg);            // Data di registrazione
      r.put("NUMRIG", i+1);                 // Numero riga
      
      r.put("SEZIONE", n.sezione());        // Sezione
      r.put("IMPORTO", n.valore());         // Importo
      conto.put(r);                         // Conto
      
      row.get();                            // Codice descrizione (ignorato)
      const char* descr = row.get();        // Descrizione aggiuntiva
      if (i > 0 || m.get(F_DESCR) != descr) // Salva la prima descrizione se diversa da testata
        r.put("DESCR", descr);              // Descrizione riga

      r.put("TIPOCC", row.get());           // Contropartita
      r.put("GRUPPOC", row.get());
      r.put("CONTOC", row.get());
      r.put("SOTTOCONTC", row.get());
      row.get();                            // Descrizione contropartita
      r.put("NUMGIO", row.get());           // Numero riga giornale
      r.put("ROWTYPE", row.get());          // Tipo riga
    }
  }

  if (_iva == nessuna_iva) return;
  
  // Calcola mese di liquidazione precedente
  int mese_liq = 0;              
  if (m.get_bool(F_DIFFERITA))
  { 
    TDate data_liq = m.get(F_DATAREG);
    const int curr_liq = _rel->date2liq(data_liq);
    data_liq.set_day(1);      // Evita problemi coi mesi corti!
    for (mese_liq = curr_liq-1; mese_liq > 0; mese_liq--)
    {
      data_liq.set_month(mese_liq);
      if (_rel->date2liq(data_liq) != curr_liq)
        break;
    }
    if (mese_liq <= 0)
      mese_liq = 12;
  }
  rec.put(MOV_MESELIQ, mese_liq);
  
  if (causale().corrispettivi())
  {
    rec.put("TIPO", ""); 
    rec.put("CODCF", "");  // Azzera il cliente nei movimenti dei corrispettivi
  }
  else
  {
    rec.put("TIPO", clifo()); 

    if (m.field(F_OCCASEDIT).active())        // Se e' un occasionale
    {
      const TMask& om = occas_mask();
      TRelation occas(LF_OCCAS);
      om.autosave(occas);                    // Salva i dati anagrafici
        
      int err = occas.write();
      if (err == _isreinsert)
        err = occas.rewrite();
        
      if (err == NOERR)
        rec.put("OCFPI", om.get(O_CODICE));
      else
        error_box("Errore di scrittura sul file dei clienti/fornitori occasionali: %d", err);  
    }
    else
      rec.zero("OCFPI");
  }
  
  const bool to_swap = test_swap(FALSE);                                   
  if (to_swap)
  {
    real totale = rec.get("TOTDOC");
    totale = -totale;
    rec.put("TOTDOC", totale);
  }
  
  const bool intra = causale().intra();

  TArray& irows = ivas().rows_array();
  ivas_pack();

  for (int i = 0; i < irows.items(); i++)
  {
    TToken_string& row = (TToken_string&)irows[i];

    TRectype &r = _rel->iva(i);
    r.zero();
    r.put("NUMREG", numreg);
    r.put("NUMRIG", i+1);
    
    r.put("ANNOES", annoes);              // Anno d'esercizio della testata (che ca$$ata!)
    r.put("INTRA", intra);                // Causale intra (che ca$$ata!)
    
    real imponibile(row.get(0));
    if (to_swap) imponibile = -imponibile;
    r.put("IMPONIBILE", imponibile);
    
    r.put("CODIVA", row.get());
    r.put("TIPODET", row.get());
    
    real imposta(row.get());
    if (to_swap) imposta = -imposta;
    r.put("IMPOSTA", imposta);
    
    r.put("TIPOCR", row.get());

    const TBill c(row, -1, 0x1);
    const int rimp = bill2pos(c, 'I')+1;
    r.put("RIGAIMP", rimp);
    c.put(r);
  } 
}


void TPrimanota_application::check_saldi()
{
  TString_array& rows = cgs().rows_array();
  const int anno = _rel->curr().get_int("ANNOES");
  
  for (int i = 0; i < rows.items(); i++)
  {
    TToken_string& row = (TToken_string&)rows[i];
    TBill conto(row, 2, 0x3);
    conto.find();
    const char sez = conto.sezione();
    if (sez > ' ')
    {
      const TConto* c = _saldi.find(conto, anno);
      if (c && !c->saldo_finale().is_zero() && c->saldo_finale().sezione() != sez)
        warning_box("Il conto della riga %i ha un saldo finale in %s, "
                    "contrariamente a quanto indicato sul piano dei conti", 
                    i+1, sez == 'A' ? "dare" : "avere");
    }                
  }
}  


int TPrimanota_application::write(const TMask& m)
{     
  static int lasterr = NOERR;
  
  const long numreg = m.get_long(F_NUMREG);
  if (numreg > _lastreg) _lastreg = numreg;             // Aggiorna ultima registrazione libera
  
  if (lasterr == NOERR)                           
    mask2rel(m);                                        // Altrimenti raddoppia i saldi!
  
  _last_dreg = m.get(F_DATAREG);  
  _last_dcom = m.get(F_DATACOMP);  
  
  const int err = _rel->write(TRUE);
  if (err == NOERR)
  {
    _saldi.registra();
    check_saldi();
    
    bool salvaconto = FALSE;
    const long old_nreg = numreg - (lasterr == _isreinsert ? 1 : 0);
    
    if (iva() != nessuna_iva)
    {
      causale().reg().reread();                         // Aggiorna protocollo IVA
      if (is_saldaconto())
      {   
        switch (causale().tipomov()) 
        {
        case tm_fattura:
          partite().update_reg(_rel->curr(), old_nreg);
          write_scadenze(m);                            // Salva fattura
          break;
        case tm_nota_credito:  
          salvaconto = TRUE;                            // Salva nota credito
           break;
        default:
          break;
        }    
      }  
      else 
      {  
        // Genera incasso immediato se e' una fattura e non c'e' gestione saldaconto
        if (!m.get_bool(F_SOLAIVA) && !gestione_saldaconto())                     
        {
          const TString causimm(causale().causale_inc_imm());
          if (causimm.not_empty())
            genera_incasso(causimm);                    
        }    
      }  
    }    
    else 
    {
      salvaconto = is_pagamento();
    }  
    
    if (salvaconto)
    {     
      const long old_nreg = numreg - (lasterr == _isreinsert ? 1 : 0);
      partite().update_reg(_rel->curr(), old_nreg);
      partite().write();
    }  

    link_m770();
    link_cesp(m, "Insert");
  } 
  
  lasterr = err;
  return err;
}


int TPrimanota_application::rewrite(const TMask& m)
{
  mask2rel(m);
  const int err = _rel->rewrite(TRUE);
  if (err == NOERR)
  {
    _saldi.registra();
    check_saldi();
    
    if (is_saldaconto())
    {            
      bool salvaconto = FALSE;
      if (iva() != nessuna_iva) 
      {                       
        if (causale().tipomov() == tm_nota_credito)
          salvaconto = TRUE;
        else  
        {
          partite().update_reg(_rel->curr());
          write_scadenze(m);  
        }  
      }  
      else
      {
        salvaconto = is_pagamento();                   
      }  
      
      if (salvaconto)
      {
        partite().update_reg(_rel->curr());
        partite().rewrite();
      }  
    }    
    
    link_m770();
    link_cesp(m, "Modify");
  }
  
  return err;
}


bool TPrimanota_application::remove()
{
  const bool ok = TRelation_application::remove();
  if (ok)
  {
    _saldi.registra();  
    check_saldi();
    
    TMask& m = curr_mask();
    if (is_saldaconto()) 
    {                   
      if (is_fattura())
      {          
        m.reset(F_ANNORIF); 
        m.reset(F_NUMRIF);
        write_scadenze(m);
      }  
      else
      {                          
        notify_cgline_deletion(-1);               // Notify deletion of all cg lines
        partite().rewrite();
      }
    }  
    link_cesp(m, "Remove");
    
    if (easydoc_connected())
      run_easydoc("Elimina");
  }  
  return ok;
}

///////////////////////////////////////////////////////////
// Gestione incasso immediato
///////////////////////////////////////////////////////////

void TPrimanota_application::genera_incasso(const char* causimm)
{ 
  TMask& m = curr_mask();
  
  if (causimm)
  {
    _incasso->_causale = causimm;
    _incasso->_causale_fattura = m.get(F_CODCAUS);
    _incasso->_iva_fattura = causale().iva();
    _incasso->_datadoc = m.get(F_DATADOC);
    _incasso->_numdoc = m.get(F_NUMDOC);
    _incasso->_clifo.get(_rel->cg(0));
    _incasso->_totale = m.get_real(F_TOTALE);
    _incasso->_descrizione= m.get(F_DESCR);
    _incasso->_step = 1; 
  }  
  else
  {
    TCausale& caus = causale();
    m.set(F_DATADOC, _incasso->_datadoc);
    m.set(F_NUMDOC, _incasso->_numdoc);
    TBill contro; caus.bill(2, contro);

    cgs().reset();  
    TToken_string& row1 = cgs().row(0);
    TImporto imp(caus.sezione_clifo(), _incasso->_totale);
    imp.add_to(row1, 0); 
    row1.add(_incasso->_clifo.string(0x3));
    row1.add(" ");
    row1.add(_incasso->_descrizione);
    row1.add(contro.string(0x3));
   
    TToken_string& row2 = cgs().row(1);
    imp.swap_section();
    imp.add_to(row2, 0); 
    row2.add(contro.string(0x3));
    row2.add(" ");
    row2.add(caus.desc_agg(2));
    row2.add(_incasso->_clifo.string(0x3));
    _incasso->_step = 2;
    fill_sheet(m);
  }          
}

///////////////////////////////////////////////////////////
// Gestione collegamento 770
///////////////////////////////////////////////////////////

bool TPrimanota_application::is_percipient(long forn, char& tipper, long& codper) const
{
  TLocalisamfile fornitori(LF_CLIFO);
  fornitori.put("TIPOCF", "F");  
  fornitori.put("CODCF", forn);  
  fornitori.read();
  tipper = fornitori.get_char(CLI_TIPOAPER);
  codper = fornitori.get_long(CLI_CODANAGPER);
  return codper > 0;
}

long TPrimanota_application::calcola_m770(int tipo_coll, real& spese, real& compenso, 
                                          real& iva, real& ritfis)
{                   
  const int m770 = causale().link_m770();
  long forn = 0;

  if (tipo_coll == 1)
  {            
    TString_array& riva = ivas().rows_array();
    TCodiceIVA codiva; 
    for (int i = 0; i < riva.items(); i++)
    {                           
      TToken_string& row = riva.row(i);
      codiva.read(row.get(1));
      const TString& tipoiva = codiva.tipo();

      const real imponibile(row.get(0));
      if ((tipoiva == "ES" || tipoiva == "NI" ||tipoiva == "NS") && riva.items() > 1)
      { 
        if (m770 != 5)
          spese += imponibile;
      }
      else
        compenso += imponibile;

      const real imposta(row.get(3));
      iva += imposta;
    }
    
    i = type2pos('F');
    if (i >= 0)
    {
      TImporto imp; imp = cgs().row(i);
      ritfis = imp.valore();
    }
    
    i = type2pos('S');
    if (i >= 0)
    {
      TImporto imp; imp = cgs().row(i);
      ritfis += imp.valore();
    }

  }  
  
  if (tipo_coll == 3 || tipo_coll == 7)
  { 
    TImporto imp;
    TString_array& rcg = cgs().rows_array();
    for (int i = 0; i < rcg.items(); i++)
    {
      TToken_string& row = rcg.row(i);
      imp = row;

      const char cf = row.get_char(2);
      if (cf == 'F')                    // Evviva, e' un fornitore!
      {            
        const long cod = row.get_long(5);
        char tipper;
        long codper;
        if (!is_percipient(cod, tipper, codper))  
          continue;                     // Se non e' percipente ignoralo
          
        if (forn == 0 && tipo_coll == 3)
        {
          forn = cod;                   // E' il primo che trovo!
        }  
        else                            // Fine ricerca.
        {
          forn = 0;
          compenso = ZERO;
          break;
        }  
        compenso = imp.valore();
      }  
    }  
  }    
  
  if (tipo_coll == 4)
  {
    TBill zio; causale().bill(2, zio);
    
    TString_array& rcg = cgs().rows_array();
    for (int i = 0; i < rcg.items(); i++)
    {                    
      TToken_string& r = rcg.row(i);
      const TBill bill(r, 3, 0x0);
      if (zio == bill)
      {
        TImporto imp; imp = r;
        ritfis = imp.valore(); 
        break;
      }  
    }
  }
  
  if (tipo_coll == 6)
  {
    TString_array& rcg = cgs().rows_array();
    TToken_string& row = rcg.row(0);
    TImporto imp;
    imp = row;
    if (imp.sezione() == 'D')
      compenso = imp.valore();
    else
      compenso = ZERO;  
  }
  
  return forn;
}

bool TPrimanota_application::link_m770()
{                                           
  const int m770 = causale().link_m770();
  if (!has_module(M77AUT) || iva() == iva_vendite || m770 == 0)
    return FALSE;
  
  int tipo_coll = 0;
  char tipper = ' ';
  long codper = 0;
  real spese, compenso, imposte, ritenute;
  
  if (iva() == iva_acquisti)
  {       
    if (m770 == 1 || m770 == 5)
    {
      const long forn = curr_mask().get_long(F_FORNITORE);  
      if (is_percipient(forn, tipper, codper))
        calcola_m770(tipo_coll = 1, spese, compenso, imposte, ritenute);
    }    
  }
  else            // Movimento puramente contabile
    switch (m770)
    {
    case 1:
      {                                  
        tipo_coll = is_saldaconto() ? 7 : 3;
        const long forn = calcola_m770(tipo_coll, spese, compenso, imposte, ritenute);
        if (forn != 0)                           
          is_percipient(forn, tipper, codper);
        else 
          tipo_coll = 7;
      }  
      break;
    case 2:
      calcola_m770(tipo_coll = 4, spese, compenso, imposte, ritenute);
      break;
    case 6:
      calcola_m770(tipo_coll = 6, spese, compenso, imposte, ritenute);
      break;
    default:
      break;  
  }
  
  if (tipo_coll)
  {
    TToken_string s(80);
    s.add(tipo_coll);
    s.add(curr_mask().insert_mode() ? "I" : "M");
    s.add(tipper);
    s.add(codper);
    s.add(curr_mask().get(F_NUMREG));
    s.add(curr_mask().get(F_NUMDOC));
    s.add(curr_mask().get(F_DATADOC));
    if (iva() == iva_acquisti)
      s.add(curr_mask().get(F_TOTALE));
    else
      s.add("");
    s.add(spese.string());    
    s.add(compenso.string());    
    s.add(imposte.string());    
    s.add(ritenute.string());   
    
    const char* name = (tipo_coll == 4) ? "770 -1" : "770 -0";
    TMessage m(name, "LINK770", s);                                                      
    m.send();
    
    TExternal_app a(name);
    a.run();
  }
  
  return tipo_coll > 0; 
}

HIDDEN void ini2bill(TConfig& ini, TBill& bil, bool contro)
{
  char tipo = ini.get_char(contro ? "TIPOC" : "TIPO");
  int gruppo = ini.get_int(contro ? "GRUPPOC" : "GRUPPO");
  int conto =  ini.get_int(contro ? "CONTOC" : "CONTO");
  long sottoconto = ini.get_long(contro ? "SOTTOCONTC" : "SOTTOCONTO");
  bil.set(gruppo, conto, sottoconto, tipo);
}

HIDDEN bool set_not_empty(TMask& msk, int id, TConfig& ini, const char* var)
{
  const TString& val = ini.get(var);
  bool ok = val.not_empty();
  if (ok)
    msk.set(id, val);       
  return ok;  
}

HIDDEN bool add_not_empty(TToken_string& str, int pos, TConfig& ini, const char* var)
{
  const TString& val = ini.get(var);
  bool ok = val.not_empty();
  if (ok)
    str.add(val, pos);
  return ok;
}

void TPrimanota_application::ini2mask(TConfig& ini, TMask& msk, bool query)
{
  TRelation_application::ini2mask(ini, msk, query);
  if (query)
  { 
    set_not_empty(msk, F_CODCAUS, ini, MOV_CODCAUS);
    TString16 val = ini.get(MOV_DATAREG);
    if (val.empty())
      val = TDate(TODAY).string();
    msk.set(F_DATAREG, val);
  }
  else
  {                                    
    int i;
    
    if (iva() != nessuna_iva)
    {     
      add_cgs_tot(msk);
    
      TSheet_field& is = ivas();
      for (i = 0; ini.set_paragraph(format("%d,%d", LF_RMOVIVA, i+1)); i++)
      {
        TToken_string& riga = is.row(i);
  
        iva_notify(is, i, K_SPACE);
        add_not_empty(riga, 0, ini, RMI_IMPONIBILE); // Imponibile 101
        add_not_empty(riga, 1, ini, RMI_CODIVA);     // IVA        102
        add_not_empty(riga, 2, ini, RMI_TIPODET);    // Detrazione 103
        add_not_empty(riga, 3, ini, RMI_IMPOSTA);    // Imposta    104
    
        TBill c; ini2bill(ini, c, FALSE);
        if (c.gruppo() > 0)
          c.add_to(riga, 4, 0x7);                    // Conto 105-110
        
        iva_notify(is, i, K_ENTER);
      }           
      calcola_imp();  // Calcola totale imponibile ed imposte
    }
    
    TSheet_field& cg = cgs();
    for (i = 0; ini.set_paragraph(format("%d,%d", LF_RMOV, i+1)); i++)
    {                        
      char tipo = ini.get_char(RMV_ROWTYPE);         
      if (tipo == '\0') tipo = ' ';
      
      TBill conto; ini2bill(ini, conto, FALSE);

      int numrig = -1;   // Normalmente aggiungi la riga in fondo
      
      // Cerca una riga che abbia gia' il tipo ed il conto richiesto
      if (strchr(" DINT", tipo) != NULL)
      {
        if (tipo == 'I' || tipo == ' ')
          numrig = bill2pos(conto, tipo);
        else  
          numrig = type2pos(tipo);
        if (numrig < 0)  
          tipo = ' ';
      }  
        
      TToken_string& riga = cg.row(numrig);          
      
      if (numrig < 0 || !(cg.cell_disabled(numrig, 0) && cg.cell_disabled(numrig, 1)))
      {
        TImporto import(ini.get_char(RMV_SEZIONE), real(ini.get(RMV_IMPORTO)));
        if (!import.is_zero())
          import.add_to(riga, 0);                  // Dare/Avere 101-102
      }
      
      if (numrig < 0 || !cg.cell_disabled(numrig, 3))
      {
        if (conto.gruppo() > 0)
          conto.add_to(riga, 2, 0x3);              // Conto 103-107
      }    
             
      riga.add("", 7);                             // Codice descrizione 108
      add_not_empty(riga, 8, ini, RMV_DESCR);      // Descrizione riga 109
  
      ini2bill(ini, conto, TRUE);                  
      if (conto.gruppo() > 0)
        conto.add_to(riga, 9, 0x3);                // Contropartita 110-114
      
      riga.add(ini.get(RMV_NUMGIO), 14);           // Centro di costo 115
      riga.add(tipo, 15);                          // Tipo di riga 116
    }    
    
    if (is_fattura())
    {
      TSheet_field& pag = pags(); 
      const int start_items = pag.items();
  
      msk.reset(FS_RECALC);     // Disabilita ricalcolo automatico
      for (i = 0; ini.set_paragraph(format("%d,%d", LF_SCADENZE, i+1)); i++)
      {
        TToken_string& row = pag.row(i);
        if (i >= start_items) 
          pag_notify(pag, i, K_CTRL+K_INS);
  
        pag_notify(pag, i, K_SPACE);
        add_not_empty(row, 0, ini, SCAD_DATASCAD);
        add_not_empty(row, 1, ini, SCAD_IMPORTO);
        add_not_empty(row, 2, ini, SCAD_IMPORTOVAL);
        pag_notify(pag, i, K_ENTER);
      }
      pagamento().set_sheet(pag);
    }
  }
}

void TPrimanota_application::mask2ini(const TMask& msk, TConfig& ini)
{
  TRelation_application::mask2ini(msk, ini);
  
  const char mov_cesp = causale().link_cespiti();
  if (mov_cesp > ' ')
  {              
    const char str_cesp[2] = { mov_cesp, '\0' };
    ini.set("MOVCESP", str_cesp);
    ini.set("DESCRCAUS", msk.get(F_DESCRCAUS));
    const TEsercizio& annoes = _esercizi[msk.get_int(F_ANNOES)];
    ini.set("INIZIOES", annoes.inizio());
    ini.set("FINEES", annoes.fine());
  }
  
  int i, f;                                    
  for (i = 0; i < _rel->cg_items(); i++)
  {                         
    ini.set_paragraph(format("%d,%d", LF_RMOV, i+1));  
    
    const TRectype& rec = _rel->cg(i); 
    for (f = rec.items()-1; f >= 0; f--)
    {
      const char* name = rec.fieldname(f);
      if (strcmp(name,RMV_NUMGIO) == 0)
        continue; // Non scrive il codice del centro di costo
      ini.set(name, rec.get(name));
    }
    
    const TBill zio(rec);
    ini.set("DESCRCONTO", zio.descrizione());
    ini.set("TIPOCR", zio.tipo_cr());

    const TBill french(rec, TRUE);
    ini.set("DESCRCONTOC", french.descrizione());
  }

  for (i = 0; i < _rel->iva_items(); i++)
  {                         
    ini.set_paragraph(format("%d,%d", LF_RMOVIVA, i+1));  
    const TRectype& rec = _rel->iva(i); 
    for (f = rec.items()-1; f >= 0; f--)
    {
      const char* name = rec.fieldname(f);
      CHECKD(name, "Dalle righe iva e' sparito il campo ", f);
      ini.set(name, rec.get(name));
    }
  }
  
  if (is_fattura())
  {
    const long numreg = _rel->lfile().get_long(MOV_NUMREG);
    TPartita* game = partite().first();
    if (game)
    {
      const int rigafatt = game->prima_fattura(numreg);
      if (rigafatt > 0)
      {
        const TRiga_partite& riga = game->riga(rigafatt);
        for (int r = 1; r <= riga.rate(); r++)
        {
          ini.set_paragraph(format("%d,%d", LF_SCADENZE, r));  
          const TRectype& rec = riga.rata(r); 
          for (f = rec.items()-1; f >= 0; f--)
          {
            const char* name = rec.fieldname(i);
            ini.set(name, rec.get(name));
          }
        }
      }
    }
  }
}

bool TPrimanota_application::link_cesp(const TMask& msk, const char* action)
{                        
  // Controlla autorizzazione
  if (!has_module(CEAUT))
    return FALSE;
  
  // Controlla flag sulla causale
  if (causale().link_cespiti() <= ' ')
    return FALSE;
  
  // Controlla l'esistenza del programma cespiti
  if (!fexist("cespcg.exe"))    
    return FALSE;

  // Cerca una riga con tipo costo/ricavo
  for (int i = _rel->cg_items()-1; i >= 0; i--)
  {                         
    const TRectype& rec = _rel->cg(i); 
    const TBill zio(rec);
    char tipo_cr = char('0' + zio.tipo_cr());
    if (strchr("2348", tipo_cr)) 
      break;
  }
  if (i < 0)
    return FALSE;
    
  TFilename cespini;
  cespini.tempdir();
  cespini.add("ActCsp.ini");
  
  TConfig cespo(cespini, "Transaction");
  cespo.set("Action", action);
  mask2ini(msk, cespo);
  
  TString appname;
  appname << "cespcg -t /i" << cespini;
  TExternal_app app(appname);
  bool ok = app.run(FALSE, 0x3) == 0;   // Force user name!
  return ok;
}

bool TPrimanota_application::protected_record(TRectype& mov)
{
  bool ok = !TRelation_application::protected_record(mov);
  if (ok && autodeleting() == 0x3)
  {
    if (mov.get_bool(MOV_STAMPATO))
    {  
      const long numreg = mov.get_long(MOV_NUMREG);
      ok = yesno_box("Il movimento %ld e' gia' stato stampato sul libro giornale:\n"
                     "si desidera eliminarlo ugualmente?", numreg);
      if (ok && mov.get_bool(MOV_REGST))
      {
        ok = yesno_box("Il movimento %ld e' gia' stato stampato sul bollato:\n"
                       "si desidera eliminarlo ugualmente?", numreg);
      }  
      if (ok && mov.get_bool(MOV_INVIATO))
      {                  
        ok = yesno_box("Il movimento %ld e' stato inviato ad un'altra contabilita':\n"
                       "si desidera eliminarlo ugualmente?", numreg);
      }
    }  
  }
  return !ok;
}

int cg2100 (int argc, char** argv)
{
  TPrimanota_application* a = new TPrimanota_application;
  a->run(argc, argv, "Prima nota");
  delete a;
  return 0;
}